# 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= # 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= SSO_CLIENT_SECRET= # Audience claim the server requires in every access token SSO_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= SSO_STORAGE_ENCRYPTION_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/` path — not just the Keycloak base URL (e.g. `http://localhost:8180/realms/onetick`, not `http://localhost:8180`).