Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Authentication And User Config Lookup

🔐 Boundary: authentication proves who is calling. User config lookup decides which virtual hosts and backends that caller can reach.

Authentication and user config lookup

This page follows the identity boundary in the request path. The gateway does not let an MCP method choose arbitrary backend URLs. It validates the bearer token, loads the caller’s UserConfig, and only then lets MCP validators select a virtual host from that config.

Request Order

Authentication and config lookup happen before McpService handles the MCP method:

StepCodeOutput
Path contextvirtual_host_id_layerVirtualHostId extension.
JWT validationclaims_layerContextForgeClaims extension.
Session headersession_id_layerOptional SessionId extension.
Config lookupuser_config_store_layerUserConfig extension.
Virtual host checkvirtual_host_config_layer404 when the path’s virtual host id is not in the loaded config.
MCP validationInitializeCallValidator or AuthorizedCallValidatorSelected VirtualHost, session id, and claims.

The order matters: user_config_store_layer needs ContextForgeClaims, and MCP validators need both UserConfig and VirtualHostId.

Token Validation

claims_layer reads Authorization: Bearer ... and decodes the JWT with the algorithm declared in the JWT header.

Token propertyCurrent behavior
AlgorithmAccepts RS256/RS384/RS512 when an RSA public key is configured, or HS256/HS384/HS512 when a shared secret is configured.
IssuerMust match mcpgateway.
AudienceMust match mcpgateway-api.
Expirationexp is validated.
Unsupported algorithmRejected before claims are inserted.

The decoded value is stored as ContextForgeClaims. The fields currently important to routing are:

ClaimRouting role
subBecomes the user config key and the principal for backend session lookup.
iss, aud, expAuthentication checks only.
jti, token_use, iat, teams, user, scopesCarried in claims for future policy use; not currently used by MCP routing. token_use, iat, teams, and scopes are optional, as is user.full_name, so tokens without those fields still validate.

A concrete decoded payload for the local admin@example.com subject looks like this (timestamps shown as example Unix seconds). Of everything here, MCP routing depends only on sub today. The optional fields are included for illustration:

{
  "iss": "mcpgateway",
  "aud": "mcpgateway-api",
  "sub": "admin@example.com",
  "exp": 1717180800,
  "iat": 1717177200,
  "jti": "example-token",
  "token_use": "api",
  "teams": ["team_awesome"],
  "user": {
    "email": "admin@example.com",
    "full_name": "API Token User",
    "is_admin": true,
    "auth_provider": "api_token"
  },
  "scopes": {
    "server_id": "my_id",
    "permissions": ["tools.read", "servers.use"],
    "ip_restrictions": ["192.169.1.0/24"],
    "time_restrictions": null
  }
}

User Config Key

user_config_store_layer turns the subject into a typed key:

ContextForgeClaims.sub
  -> User::new(subject)
  -> UserConfigStore::get_config(&user)

The Redis adapter serializes that User key with MessagePack. The key includes both the key type and the subject, so user config data is not just stored under the raw subject string.

Cache And Redis Lookup

RedisUserConfigStore checks an in-process LRU cache before going to Redis:

StageBehavior
LRU hitClone the decoded UserConfig from the cache.
LRU missMessagePack-encode User::new(subject), GET that Redis key, decode the MessagePack UserConfig, then cache it.
Cache size50,000 entries.
Cache expiry--user-config-cache-expiry-seconds, default 60 seconds. 0 disables the cache and reads Redis on every request.
Redis retry settingConnection manager is configured with 1,000 retries.

The cache is an implementation detail of RedisUserConfigStore. Routing code depends on UserConfigStore, not Redis commands.

Failure Behavior

Failures before RMCP method handling are HTTP responses:

FailureResponse
Missing Authorization header401 Unauthorized.
Header does not start with Bearer 401 Unauthorized.
JWT header or body cannot be decoded401 Unauthorized.
JWT uses an unsupported algorithm401 Unauthorized.
Required decoder key or secret is not configured401 Unauthorized.
No user config exists for claims.sub400 Bad Request.
Redis/config store error other than missing data500 Internal Server Error.
user_config_store_layer runs without claims400 Bad Request.
Virtual host id not present in the caller’s config404 Not Found with body {"detail":"Server not found"}.

A valid user config can still fail a request: virtual_host_config_layer returns 404 before MCP method handling when the config does not contain the path’s VirtualHostId.

What This Boundary Does Not Do

Authentication and user config lookup do not route to a backend by themselves. They only establish:

caller identity
  + caller UserConfig
  + requested VirtualHostId

McpService still has to validate the MCP call, resolve the virtual host, and choose either the initialize path, routed backend path, or local method path.