Skip to content

Support Vault JWT auth method to enable per-user, short-lived token auth in gateway deployments #107

Description

@gastoncan

Background

vault-mcp-server currently only authenticates to Vault using a static token — either VAULT_TOKEN env var or X-Vault-Token per-request. In gateway-fronted deployments (e.g., Toolhive, or any reverse proxy that terminates user OIDC and forwards Authorization: Bearer <JWT> to the MCP server), this means:

  • Every user reaches Vault as the same service-account identity, so Vault audit logs cannot attribute requests to real users.
  • The static token must be granted the union of all users' privileges, breaking least-privilege.
  • Per-user RBAC at the Vault layer (via group-driven policies) is impossible — authz can only be enforced at the gateway.

Proposal

Support Vault's JWT auth method as an alternative authentication mode. When configured, the server would:

  1. Read the JWT from the incoming request (e.g., Authorization: Bearer <JWT> header forwarded by the gateway).
  2. Exchange it via POST /v1/auth/<jwt-mount>/login with role=<configured-role> and jwt=<incoming-jwt> to obtain a short-lived, user-scoped Vault token.
  3. Use that token (X-Vault-Token) for the actual Vault API call(s) on behalf of the request.
  4. Optionally cache the resulting Vault token in-memory keyed by the JWT's sub+iat (or jti) for the token's TTL, to avoid a /login round-trip per request.

This is the pattern Vault Agent, vault-secrets-operator, and most modern Vault clients already implement.

Configuration sketch

New env vars / flags (all optional; static-token mode remains the default):

Variable Purpose
VAULT_AUTH_METHOD token (default) or jwt
VAULT_AUTH_JWT_PATH Mount path of the JWT auth method (default jwt)
VAULT_AUTH_JWT_ROLE Vault role to authenticate against
VAULT_AUTH_JWT_HEADER Header to read the JWT from (default Authorization)
VAULT_AUTH_JWT_CACHE_TTL Optional cap on per-user token cache (default = role's token_ttl)

When VAULT_AUTH_METHOD=jwt, the existing VAULT_TOKEN / X-Vault-Token paths are bypassed for that request.

Why this matters

In my organization's deployment (Vault behind Toolhive + Keycloak):

  • Vault already has the JWT auth method wired to Keycloak with external-group → policy mappings (/mcp-admin, /vault-kv-admins, /mcp-readonly), so per-user policy enforcement is fully working at the Vault layer for any client that can present the JWT.
  • The Toolhive gateway already exchanges the user's Keycloak token to aud=mcp-vault and forwards it as Authorization: Bearer to the backend.
  • The only missing link is vault-mcp-server doing the JWT-for-Vault-token exchange — without it, the per-user trust chain ends at the gateway and collapses into a shared service account at Vault.

Acceptance criteria

  • JWT auth mode usable as a drop-in alternative to static-token mode (configured per deployment)
  • Per-request /login exchange with optional in-memory caching keyed by JWT identity
  • Surface JWT-login errors clearly to the caller (401/403 with reason; not silent fallback to static token)
  • Static-token mode remains the default and unchanged (backward compatible)
  • Docs section describing gateway-based deployment with JWT auth

References

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions