keycloak setting up mcp cimd profile
In this example, we will be setting up our mcp authentication using Keycloak CIMD using vscode desktop.
Let's get our keycloak instance up and running.
docker run -p 8080:8080 -e KC_FEATURES=cimd -e KC_BOOTSTRAP_ADMIN_USERNAME=admin -e KC_BOOTSTRAP_ADMIN_PASSWORD=admin quay.io/keycloak/keycloak:latest start-dev
Then setup follow these steps here to configure vscode desktop
Setting up the client profile for VS Code desktop
Navigate to Realm Settings → Client Policies → Profiles tab.
Click Create client profile.
Give the profile a name such as
vscode-cimd-profileand click Save.Click Add executor and select
client-id-metadata-documentfrom the list.Configure the executor with the following options:
Allow http scheme:
OFFTrusted domains:
vscode.dev,127.0.0.1,code.visualstudio.com(This option is applied not only to theclient_idURL but also to the URL-valued properties of the Client ID Metadata Document, such asclient_uri,logo_uri,tos_uri,policy_uri, andjwks_uri. VS Code desktop’s Client ID Metadata Document includes alogo_uriproperty whose value is a URL oncode.visualstudio.com. Therefore, this domain must be included in the trusted domains list.)Restrict same domain:
OFF(VS Code desktop uses a localhost redirect URI such ashttp://127.0.0.1:<port>/callback, which is not on the same domain asvscode.dev)Only Allow Confidential Client:
OFF(VS Code desktop is a public client)
Click Save.
Setting up the client policy for VS Code desktop
Navigate to Realm Settings → Client Policies → Policies tab.
Click Create client policy.
Give the policy a name such as
vscode-cimd-policyand click Save.Under Conditions, click Add condition and select
client-id-urifrom the list.Configure the condition with the following options:
URI scheme:
httpsTrusted domains:
vscode.dev
Click Save.
Under Associated client profiles, add the
vscode-cimd-profileprofile created in the previous step.Click Save.
This is my vscode-cimd-policy looks like
And my client-id-uri looks like:
We can see that vscode's has the followings: client-metadata.json
Please note your configuration must match
If you don't have the trusted domain configure, then you will run into an issue:
Fire up your brownser and then paste the following in your brower url.
http://localhost:8080/realms/master/protocol/openid-connect/auth?client_id=https%3A%2F%2Fvscode.dev%2Foauth%2Fclient-metadata.json&response_type=code&redirect_uri=http%3A%2F%2F127.0.0.1%3A33418%2F&resource=http%3A%2F%2Flocalhost%3A8080%2Fmcp
Here is the URL decoded into a clean, human-readable format:
client_id = https://vscode.dev/oauth/client-metadata.json
response_type = code
redirect_uri = http://127.0.0.1:33418/
resource = http://localhost:8080/mcp
And then keycloak will ask you for your username and password. Please enter your username and password and then you will see the following screen.
You will noticed that we didn't pass any of our configration details here except the domain and fortunately that's all it needs.
The Flow: How Your Configuration Works
When you open that URL, here's what happens behind the scenes:
1. Browser sends request to Keycloak:
GET /auth?client_id=https://vscode.dev/oauth/client-metadata.json&...
2. Keycloak receives it and asks: "Is this a valid client?"
❌ It's NOT a pre-registered client in the database
✅ But it IS a URL (the client_id parameter)
3. Keycloak checks its CLIENT POLICIES:
├─ "Is this a URI scheme?" → YES (https://)
├─ "Is the domain trusted?" → Check against vscode.dev
└─ If YES to both → Trigger the associated PROFILE
4. The PROFILE (with client-id-metadata-document executor) runs:
├─ Recognizes client_id is a URL
├─ Fetches https://vscode.dev/oauth/client-metadata.json
├─ Validates the metadata (checks redirect_uri, grant types, etc.)
├─ Extracts the allowed redirect URIs from the metadata
└─ Uses those to validate your redirect_uri parameter
5. Keycloak validates:
✅ redirect_uri (http://127.0.0.1:33418/) matches metadata's allowed list
✅ resource parameter is present
✅ All OAuth 2.0 requirements met
6. Shows login page ✅Why It Works
| Step | Component | Action |
|---|---|---|
| 1 | Client Policy | Detects client_id=https://vscode.dev/... matches the client-id-uri condition |
| 2 | Client Policy | Checks if vscode.dev is in trusted domains ✅ |
| 3 | Associated Profile | Triggers the client-id-metadata-document executor |
| 4 | Executor | Fetches the metadata JSON from that URL |
| 5 | Executor | Validates redirect_uri against the fetched metadata's redirect_uris list |
| 6 | Executor | Validates domain trust and other security checks |
| 7 | Keycloak | Issues token with resource as the audience |
If we didn't configure anything and when are request comes into keycloak, the following workflow takes place.
Keycloak receives: client_id=https://vscode.dev/oauth/client-metadata.json
Looks for "vscode.dev" in registered clients
Doesn't find it
❌ Rejects the request
OAuth CIMD Flow (From the Spec)
The core idea is that instead of receiving a client_id from the authorization server, the client uses an HTTPS URL as its client_id. That URL points to a JSON document containing the client's metadata — name, redirect URIs, supported grant types, and more. The authorization server fetches this document when it encounters the URL-based client_id.
The Step-by-Step Flow (How It Should Work)
Here's what happens:
- The client initiates an Authorization request with its URL as the client_id (e.g., https://client.example.com/oauth-client).
- The authorization server recognizes the client_id as a URL and fetches it via HTTPS.
- The response is a JSON document containing standard OAuth client metadata.
- The authorization server validates the metadata, displays consent information to the user, and proceeds with the OAuth flow.
- Subsequent requests can use cached metadata according to HTTP caching headers
Comments