Authentication Architecture Guide
Overview
AuthSystem (src/auth/system.rs) composes three subsystems behind one authenticate() entry point:
pub struct AuthSystem {
config: Arc<AuthConfig>,
storage: Arc<StorageLayer>,
jwt: Arc<JwtHandler>, // src/auth/jwt/types.rs
api_key: Arc<ApiKeyHandler>, // src/auth/api_key/creation.rs
rbac: Arc<RbacSystem>, // src/auth/rbac/system.rs
}
AuthSystem::authenticate(auth_method, context) -> Result<AuthResult> dispatches on
AuthMethod::{Jwt, ApiKey, Session, None} (src/auth/types.rs). Session auth is a
stub that always rejects ("Session authentication is not yet implemented").
Semantic rejections return AuthResult { success: false, error: Some(..) };
infrastructure failures return Err(GatewayError).
Middleware pipeline
Wired in src/server/http.rs (actix runs wraps in reverse registration order, so
the request path is outermost-first):
RequestIdMiddleware -> AuditMiddleware -> IpAccessMiddleware -> CORS/Metrics
-> AuthMiddleware (src/server/middleware/auth.rs)
-> RateLimitMiddleware (src/server/middleware/rate_limit.rs)
-> SecurityHeadersMiddleware -> handler
AuthMiddleware per request: public-route bypass → fail-closed check when both
auth methods are disabled (allow_anonymous gate) → brute-force lockout via
AuthRateLimiter → credential extraction → authentication → endpoint/operation
authorization → insert User / ApiKey into request extensions.
Credential Extraction
extract_auth_method_with_api_key_header (src/server/middleware/helpers.rs)
resolves credentials in this priority order:
Authorization: Bearer <jwt>→AuthMethod::JwtAuthorization: ApiKey <key>→AuthMethod::ApiKeyAuthorization: gw-...(raw key, no scheme) →AuthMethod::ApiKey- Configured API key header (
auth.api_key_header, defaultAuthorization) X-API-Keyfallback (when the configured header differs)session=<id>cookie →AuthMethod::Session
There is no Bearer sk- form: gateway keys are gw- prefixed, and a raw
sk-... value matches nothing.
API Key Authentication
Key generation and hashing
Keys are generated by generate_api_key() in src/utils/auth/crypto/keys.rs:
a fixed gw prefix plus 32 alphanumeric characters (gw-<32 chars>, 35 total).
They are hashed — never stored in plaintext:
// src/utils/auth/crypto/keys.rs
pub fn generate_api_key() -> String; // "gw-" + 32 alphanumerics
pub fn hash_api_key(api_key: &str, hmac_secret: Option<&str>) -> String;
pub fn extract_api_key_prefix(api_key: &str) -> String; // "gw-a...mnop" display only
hash_api_key computes HMAC-SHA256 when api_key_hmac_secret is configured,
otherwise plain SHA-256. Argon2 is used only for user passwords
(src/utils/auth/crypto/password.rs), never for API keys.
Handler
ApiKeyHandler::new(storage: Arc<StorageLayer>, hmac_secret: Option<String>)
stores keys through the database layer and looks them up by full hash
(find_api_key_by_hash) — there is no prefix lookup.
impl ApiKeyHandler {
pub async fn create_key(&self, user_id: Option<Uuid>, team_id: Option<Uuid>,
name: String, permissions: Vec<String>) -> Result<(ApiKey, String)>; // (stored, raw)
pub async fn verify_key(&self, raw_key: &str) -> Result<Option<(ApiKey, Option<User>)>>;
pub async fn verify_key_detailed(&self, raw_key: &str) -> Result<ApiKeyVerification>;
}
Verification rejects inactive or expired keys and keys whose owner user is
missing/inactive; it refreshes last_used_at throttled to once per 5 minutes
(LAST_USED_THROTTLE) via an in-memory DashMap<Uuid, Instant> cache.
Names must be 1–255 chars without control characters; permissions must be from
VALID_PERMISSIONS (creation.rs): *, system.admin, analytics.read,
api.chat, api.embeddings, api.images, and dotted read/write/delete grants
on users, teams, api_keys (e.g. users.read, api_keys.delete).
Management lives on the same handler: revoke_key, list_user_keys,
update_permissions, update_expiration, regenerate_key (returns a new raw
key), cleanup_expired_keys (src/auth/api_key/management.rs).
JWT Authentication
Claims
Single role: String (not a role list), plus token identity fields
(src/auth/jwt/types.rs):
pub struct Claims {
pub sub: Uuid, // user ID
pub iat: u64, pub exp: u64,
pub iss: String, // fixed "litellm-rs"
pub aud: String, // "api" for access, "refresh" for refresh tokens
pub jti: String, // UUID token ID
pub role: String,
pub permissions: Vec<String>,
pub team_id: Option<Uuid>,
pub session_id: Option<String>,
pub token_type: TokenType, // Access | Refresh | PasswordReset |
} // EmailVerification | Invitation
Handler
JwtHandler::new(config: &AuthConfig) builds HS256 signing keys from
jwt_secret; lifetime comes from jwt_expiration. There is no configurable
audience field — audiences are hard-coded per token kind.
impl JwtHandler {
pub async fn create_access_token(&self, user_id: Uuid, role: String,
permissions: Vec<String>, team_id: Option<Uuid>, session_id: Option<Uuid>)
-> Result<String>;
pub async fn create_refresh_token(&self, user_id: Uuid,
session_id: Option<String>) -> Result<String>; // exp = expiration * 24
pub async fn create_token_pair(...) -> Result<TokenPair>;
pub async fn verify_access_token(&self, token: &str) -> Result<Claims>;
pub async fn verify_refresh_token(&self, token: &str) -> Result<Uuid>;
}
For both public creation methods, team_id must currently be None; passing Some(...)
returns BadRequest("Active team selection requires verified membership").
Team-scoped access tokens and token pairs are created only through the crate-private
create_access_token_for_verified_team / create_token_pair_for_verified_team paths
after active membership has been verified.
verify_access_token enforces aud == "api" and rejects unknown team-scope
versions; verify_refresh_token enforces aud == "refresh" plus
token_type == Refresh. JWT decode failures map to
GatewayError::Auth("JWT error: ...").
References
- reference/middleware-pipeline.md — AuthMiddleware wiring, brute-force lockout, route-level permission checks
- reference/rbac.md — Permission/Role structs, RbacSystem checks, role inheritance, default roles
- reference/rate-limiting.md — DashMap RateLimiter strategies, Redis backend, rate-limit key policy
- reference/configuration.md — flat
auth:YAML surface, validation rules, top-levelrate_limit:section - reference/security-best-practices.md — secret policy, HMAC key hashing, redaction, audit events, key lifecycle
- reference/error-types.md — GatewayError variants used across the auth stack and their HTTP mappings