GitHub MCP Server with Vault Token Exchange¶
This guide demonstrates connecting to the GitHub MCP server through MCP Gateway, using HashiCorp Vault to manage per-user GitHub Personal Access Tokens (PATs).
Overview¶
When a user makes an MCP request:
- AuthPolicy validates the user's OIDC token
- Authorino authenticates to Vault using the user's access token
- Authorino fetches the user's GitHub PAT from Vault, keyed by their OIDC subject
- The PAT is injected into the
Authorizationandx-mcp-api-keyheaders for the upstream MCP server
Prerequisites¶
- MCP Gateway installed and configured
- External GitHub MCP server set up (Steps 1-5)
- HashiCorp Vault deployed and accessible from the cluster -- see Vault installation docs
- Vault KV v2 secrets engine enabled at
secret/ - OIDC provider configured -- see Authentication
- Kuadrant with Authorino installed
Step 1: Configure Vault for Authorino access¶
Configure Vault's JWT auth method so Authorino can verify user tokens against your OIDC provider. For instructions on enabling JWT authentication, see Vault JWT auth documentation. For a simpler development setup using Vault's root token, see the Vault integration guide.
Create a Vault policy granting read access to MCP Gateway secrets. In production, use a templated path so each user's Vault token can only read their own secret:
vault policy write authorino - <<EOF
path "secret/data/mcp-gateway/{{identity.entity.aliases.AUTH_JWT_ACCESSOR.name}}" {
capabilities = ["read"]
}
EOF
Replace AUTH_JWT_ACCESSOR with the accessor value from vault auth list for the JWT auth method. For development, a wildcard path is simpler:
vault policy write authorino - <<EOF
path "secret/data/mcp-gateway/*" {
capabilities = ["read"]
}
EOF
Create a Vault role that accepts user JWTs:
vault write auth/jwt/role/authorino - <<EOF
{
"role_type": "jwt",
"bound_audiences": ["<your-client-id>"],
"user_claim": "sub",
"policies": ["authorino"],
"ttl": "1h"
}
EOF
Note:
bound_audiencesmust match an audience (aud) claim present in the user's access token. Vault requires at least one entry if the token contains anaudclaim.
Verify the policy and role were created:
Step 2: Store a GitHub PAT in Vault¶
Store each user's GitHub PAT in Vault, keyed by their OIDC subject claim (sub):
Replace <user-sub> with the user's OIDC subject identifier (e.g., a username or UUID, depending on your identity provider).
Note: The
subclaim must be present in the access token for the Vault path lookup to work. Some identity providers (e.g., Keycloak 26+) use lightweight access tokens that omitsubby default. Configure your IdP to include it, or use a different claim in the Vault URL expression.
The PAT needs at minimum read:user scope. Adjust scopes based on which GitHub MCP tools your users need access to.
Verify the secret was stored:
Step 3: Create the AuthPolicy¶
This AuthPolicy uses the user's own access token to authenticate to Vault and fetch their GitHub PAT. All requests require a valid JWT.
kubectl apply -f - <<EOF
apiVersion: kuadrant.io/v1
kind: AuthPolicy
metadata:
name: github-vault-policy
namespace: mcp-test
spec:
targetRef:
group: gateway.networking.k8s.io
kind: HTTPRoute
name: github-mcp-external
rules:
authentication:
"mcp-clients":
jwt:
# issuerUrl must be reachable by Authorino from within the cluster.
# use jwksUrl with an in-cluster URL to bypass OIDC discovery if needed.
issuerUrl: <your-oidc-issuer-url>
metadata:
"vault-login":
priority: 0
http:
url: http://vault.vault.svc.cluster.local:8200/v1/auth/jwt/login
method: POST
body:
expression: |
"{\"role\": \"authorino\", \"jwt\": \"" + request.headers["authorization"].split("Bearer ")[1] + "\"}"
cache:
key:
expression: auth.identity.sub
ttl: 300
"vault":
priority: 1
when:
- predicate: auth.metadata.exists(p, p == "vault-login") && has(auth.metadata["vault-login"].auth) && has(auth.metadata["vault-login"].auth.client_token)
http:
urlExpression: |
"http://vault.vault.svc.cluster.local:8200/v1/secret/data/mcp-gateway/" + auth.identity.sub
method: GET
headers:
"X-Vault-Token":
expression: auth.metadata["vault-login"].auth.client_token
authorization:
"found-vault-secret":
patternMatching:
patterns:
- predicate: |
has(auth.metadata.vault.data) && has(auth.metadata.vault.data.data) && has(auth.metadata.vault.data.data.github_pat) && type(auth.metadata.vault.data.data.github_pat) == string
response:
success:
headers:
"authorization":
plain:
expression: |
"Bearer " + auth.metadata.vault.data.data.github_pat
"x-mcp-api-key":
plain:
expression: |
auth.metadata.vault.data.data.github_pat
EOF
The vault-login step authenticates to Vault using the user's JWT (cached per user for 5 minutes). The vault step fetches the secret at secret/data/mcp-gateway/<sub>. The authorization rule verifies the secret contains a github_pat field, and the response injects it into the request headers.
Note: The MCPServerRegistration
credentialRefprovides a static PAT for broker tool discovery. The AuthPolicy above injects the per-user PAT at request time. Both are needed -- see the external MCP server guide.
If requests return 403 after applying this policy, check Authorino logs for vault-login errors (audience mismatch, expired tokens, unreachable Vault).
Step 4: Verify¶
Check that the AuthPolicy is accepted:
Connect to the gateway using the MCP Inspector or your MCP client. Log in with your OIDC credentials. Under Tools > List Tools, you should see GitHub tools with prefix github_. Calling github_get_me should return the GitHub user profile associated with the PAT stored in Vault for the authenticated user.
Note
This example uses Keycloak as the OIDC provider. If you're testing locally with self-signed certificates, you may need to accept the Keycloak certificate in your browser first. Navigate to your Keycloak URL directly and accept the certificate warning before connecting.
Cleanup¶
To remove the GitHub MCP server resources, see the cleanup section in the external MCP server guide. You may also want to remove the Vault policy, role, and stored secrets created in Steps 1 and 2.
Next Steps¶
- Vault Integration -- general Vault integration patterns including root token setup for development
- Authorization -- add tool-level access control on top of Vault credentials
- External MCP Servers -- connect to other external MCP servers