MCP server authentication#

The MCP server supports OAuth2/OIDC authentication for incoming client connections, with Keycloak supported as the identity provider.

By default (SSO_ENABLED=false) the MCP server accepts all incoming connections without authentication. This is appropriate for local development or isolated internal networks.

For production deployments or any server accessible over a network, enable SSO to require users to authenticate before connecting. MCP clients handle the login flow automatically through a browser window.

Configure the MCP server#

Add the following to your .env file:

SSO_ENABLED=true

# Keycloak realm issuer URL
SSO_ISSUER_URL=<your-keycloak-issuer-url>

# Public URL of the MCP server (used to build the OAuth2 redirect URI)
SSO_BASE_URL=http://localhost:8080

# Client ID and secret registered with your Keycloak client
SSO_CLIENT_ID=<your-client-id>
SSO_CLIENT_SECRET=<your-client-secret>

# Audience claim the server requires in every access token
SSO_AUDIENCE=<your-audience>

# Scopes that must be present in the token
SSO_REQUIRED_SCOPES=openid

# Required when SSO is enabled — the server fails to start without them.
# Generate once and keep stable; do not regenerate on each restart.
SSO_JWT_SIGNING_KEY=<paste a stable random secret>
SSO_STORAGE_ENCRYPTION_KEY=<paste a stable Fernet key>

# Directory where OAuth state is stored (default: ./auth-data)
# SSO_STORAGE_PATH=./auth-data

Note

SSO_JWT_SIGNING_KEY and SSO_STORAGE_ENCRYPTION_KEY are required when SSO is enabled — startup fails if either is missing or invalid. These keep the OAuth state persistent across restarts. See Configuration.

Generate the two keys with (run these in the server’s Python environment, where cryptography is already installed as a dependency), then create the storage directory:

# SSO_JWT_SIGNING_KEY
python -c "import secrets; print(secrets.token_urlsafe(32))"

# SSO_STORAGE_ENCRYPTION_KEY
python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"

# SSO_STORAGE_PATH (default: ./auth-data)
mkdir -p auth-data

Restart the server. The SSO_ISSUER_URL is used to auto-discover the JWKS endpoint; all tokens are validated against the provider’s public keys.

Verify#

Connect with an MCP client (e.g. Cursor — see Client setup). The client should report that authentication is required and open a browser window to complete the login. After signing in, the server accepts the connection and the tools become available.

See Configuration for the full list of SSO_* variables.

Common pitfalls#

SSO_BASE_URL must match the redirect URI registered with your IDP

SSO_BASE_URL is used to construct the OAuth2 callback URL sent to the provider. If it does not match the Valid redirect URIs pattern registered on the client, the provider will reject the login with an invalid_redirect_uri error. If the MCP server sits behind a reverse proxy, use the public-facing URL here, not localhost.

Client secret rotation

If you regenerate the client secret in your IDP, update SSO_CLIENT_SECRET in your .env file and restart the server. The server does not detect secret rotation at runtime.

Audience mismatch after login

If the server logs audience mismatch (got 'xxx', expected 'yyy') after a successful login, the SSO_AUDIENCE value does not match what the IDP is embedding in the token. Check that your IDP is configured to include the correct audience claim in access tokens, and that SSO_AUDIENCE in your .env matches it exactly.

Startup validation

The server validates all SSO_* variables at startup when SSO_ENABLED=true. A missing or malformed variable will prevent the server from starting — check the startup logs if the server exits immediately after enabling SSO.

IDP setup - Keycloak#

The steps below walk through registering the MCP server as a client in Keycloak.

Prerequisites:

  • A running Keycloak instance (version 21+). The examples below assume it is reachable at http://localhost:8180. Substitute your actual Keycloak URL throughout.

  • Admin access to create a realm and client.

Refer to the Keycloak Server Administration Guide for general Keycloak setup and concepts.

Step 1: Create a realm#

  1. Log in to the Keycloak Admin Console (e.g. http://localhost:8180/admin).

  2. In the sidebar click Manage realms → Create realm.

  3. Enter a Realm name (e.g. onetick) and click Create.

Step 2: Create a client#

  1. In your new realm, go to Clients → Create client.

  2. Set Client type to OpenID Connect and enter a Client ID (e.g. onetick-mcp-client). Click Next.

  3. Enable Client authentication (makes this a confidential client). Leave Standard flow enabled (it is on by default) — this is the Authorization Code flow used for browser login. Click Next.

  4. Under Valid redirect URIs, add the MCP server’s callback URL:

    http://localhost:8080/auth/callback
    

    Replace http://localhost:8080 with the public URL of your MCP server if it is not running locally.

  5. Under Web origins, add the MCP server base URL (without a trailing path):

    http://localhost:8080
    
  6. Click Save.

  7. Open the Credentials tab and copy the Client secret — you will need this in the next step.

Step 3: Add an audience mapper#

The MCP server validates the aud claim in every access token. Add a mapper so Keycloak embeds the expected audience value in tokens:

  1. Open the client you just created and go to the Client scopes tab.

  2. Click onetick-mcp-client-dedicated → Configure a new mapper → select Audience from the list.

  3. Fill in the fields:

    • Name: onetick-mcp-aud

    • Included Custom Audience: onetick-mcp-aud — this field must be set; leave Included Client Audience empty.

    • Leave Add to ID token off and Add to access token on.

  4. Click Save.

Step 4: Create a test user#

If no users exist in the realm yet, create one to verify the login flow:

  1. In the sidebar go to Users → Add user.

  2. Fill in the fields:

    • Username: e.g. testuser

    • Email: e.g. testuser@example.com

    • Enable Email verified.

  3. Click Create.

  4. Open the Credentials tab → Set password.

  5. Enter a password, disable Temporary, and click Save.

Step 5: Configure and verify#

Using the values from the steps above, fill in the .env variables from the Configure the MCP server section, then restart the server.

Connect with an MCP client (e.g. Cursor — see Client setup). The client should report that authentication is required and open a browser window to complete the login. After signing in, the server accepts the connection and the tools become available.

Note

Set SSO_ISSUER_URL to the full realm URL including the /realms/<realm-name> path — not just the Keycloak base URL (e.g. http://localhost:8180/realms/onetick, not http://localhost:8180).