Skip to main content

Custom auth permissions

Use this reference after configuring a custom authentication handler. The handler identifies the caller with a UserAPIKeyAuth object; the fields and flags below determine which permissions and limits LiteLLM enforces.

What gets enforced​

GoalSet it onFlags required
Key / user / team / end-user rate limitsreturned object (rpm_limit, team_tpm_limit, …)none
Per-model rate limits, key / team scopedmetadata / team_metadata on the returned objectnone
Per-model rate limits, project scopedthe project record (model_tpm_limit / model_rpm_limit)custom_auth_run_common_checks
Team / user / project budgetthe team / user / project recordcustom_auth_run_common_checks
Team / user / project model allowlistthe team / user / project recordcustom_auth_run_common_checks
End-user budgetthe end-user recordcustom_auth_run_common_checks or enable_post_custom_auth_checks
Key model allowlist (models)returned objectboth flags
Key per-model budget (model_max_budget)returned objectenable_post_custom_auth_checks
Key expiry (expires)returned objectenable_post_custom_auth_checks
Key scalar budget (max_budget / soft_budget)not supported; use a per-scope budgetn/a

Note: Project per-model limits go on the object's project_metadata with the flag off, but the DB project record overrides it once the flag is on, so set them there. (Team per-model via team_metadata always stays on the object.)

See Enforce budgets and model access and Key-level enforcement for examples.

Returned-object controls​

Rate limits​

All scopes below are enforced directly off the returned object, with no flag.

def UserAPIKeyAuth(
# Key
tpm_limit: Optional[int] = None,
rpm_limit: Optional[int] = None,
# User
user_tpm_limit: Optional[int] = None,
user_rpm_limit: Optional[int] = None,
# Team
team_tpm_limit: Optional[int] = None,
team_rpm_limit: Optional[int] = None,
# Per team-member
team_member_tpm_limit: Optional[int] = None,
team_member_rpm_limit: Optional[int] = None,
# Per end-user
end_user_tpm_limit: Optional[int] = None,
end_user_rpm_limit: Optional[int] = None,
# Per-model (key / team scoped)
metadata: Dict = {}, # e.g. {"model_tpm_limit": {...}, "model_rpm_limit": {...}}
team_metadata: Optional[Dict] = None, # same keys, team scoped
): ...
note

Per-model rate limits are read from metadata (key) and team_metadata (team), keyed by model name. The model key must equal the request's model string exactly, or the limit is skipped silently.

rpm_limit_per_model / tpm_limit_per_model exist on the object but are inert; use metadata / team_metadata instead, or the project record (see below).

Additional controls​

def UserAPIKeyAuth(
max_parallel_requests: Optional[int] = None, # Concurrent request limit
allowed_model_region: Optional[AllowedModelRegion] = None, # Geographic restrictions
blocked: Optional[bool] = None, # Whether the key is blocked
config: Dict = {}, # Configuration settings
): ...

Object permissions​

from litellm.proxy._types import LiteLLM_ObjectPermissionTable, UserAPIKeyAuth
from litellm.proxy.mcp_server.mcp_server_manager import (
global_mcp_server_manager,
)

def _server_id(name: str) -> str:
server = global_mcp_server_manager.get_mcp_server_by_name(name)
if not server:
raise ValueError(f"Unknown MCP server '{name}'")
return server.server_id

object_permission = LiteLLM_ObjectPermissionTable(
mcp_servers=[_server_id("deepwiki"), _server_id("everything")], # MCP servers this key is allowed to use
mcp_tool_permissions={"deepwiki": ["search", "read_doc"]}, # optional per-server tool allow-list
)

UserAPIKeyAuth(
object_permission=object_permission,
)

Enforce budgets and model access​

Set custom_auth_run_common_checks: true to enforce budgets and model-access alongside custom auth:

general_settings:
custom_auth: custom_auth.user_api_key_auth
custom_auth_run_common_checks: true

Your handler returns the IDs; the budgets and allowlists live on the matching DB records (/team/new, /user/new, /project/new, /customer/new, or the UI), which LiteLLM loads and enforces.

For example, a team with a budget and model allowlist:

curl -X POST 'http://localhost:4000/team/new' \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-H 'Content-Type: application/json' \
-d '{
"team_id": "eng-team",
"max_budget": 100,
"models": ["gpt-6-luna", "claude-sonnet-5"]
}'
# ...then return that team_id from custom auth:
return UserAPIKeyAuth(api_key=api_key, team_id="eng-team")

For project per-model rate limits, set model_tpm_limit / model_rpm_limit on the project record (keyed by model name) and return that project_id:

# On the project record (via /project/new or the UI):
# model_tpm_limit = {"gpt-6.1-sol": 100000, "claude-sonnet-5": 50000}
# model_rpm_limit = {"gpt-6.1-sol": 100, "claude-sonnet-5": 200}
note
  • The project record's metadata replaces any project_metadata you set on the returned object, so configure project per-model limits on the project record, not on the object.
  • For per-model rate limits, the model key must equal the request's model string exactly, or the limit is skipped silently.

Key models vs project models​

These are separate controls:

FieldWhere it is enforcedSource of truth
models on UserAPIKeyAuthKey-level allowlistValue you return from custom auth
project_id on UserAPIKeyAuthProject-level allowlistmodels on the project record in LiteLLM's DB

An empty models list ([]) means no restriction. Names must match the model group in your config (wildcards supported). See Project Management and Config Settings.

Key-level enforcement​

The following are enforced from the returned object, but only when litellm.enable_post_custom_auth_checks: true is also set:

general_settings:
custom_auth: custom_auth.user_api_key_auth
custom_auth_run_common_checks: true # required for the key models allowlist

litellm_settings:
enable_post_custom_auth_checks: true
return UserAPIKeyAuth(
api_key=api_key,
models=["gpt-6-luna"], # key model allowlist (needs both flags)
model_max_budget={"gpt-6.1-sol": {"budget_limit": 100, "time_period": "30d"}}, # key per-model budget
)

This path also enforces end-user budgets and per-model end-user budgets when end_user_id is set. To reject expired credentials, return their original expiration time as described in token expiration.