Skip to content

Commit e69da62

Browse files
authored
Merge pull request #1 from sharpninja/add-mcp-web-keycloak-client
Add Keycloak OIDC client configuration automation with comprehensive documentation
2 parents 87cfb93 + 8bad9b4 commit e69da62

9 files changed

Lines changed: 1859 additions & 0 deletions

.gitignore

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,16 @@ testResults.xml
3636
.vscode/mcp.json
3737
/logs/web-ui-startup
3838
/TestResults/run-all-tests
39+
40+
# Node.js
41+
package-lock.json
42+
yarn.lock
43+
.npm
44+
.yarn
45+
dist/
46+
47+
# .NET
48+
TestResults/
49+
*.trx
50+
.dotnet/
51+
.tools/
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
# Client Architecture
2+
3+
This document describes the three OIDC clients configured in Keycloak for McpServer authentication and their respective OAuth 2.0 flows.
4+
5+
## Overview
6+
7+
McpServer uses Keycloak as its identity provider with three distinct client configurations:
8+
9+
1. **mcp-server-api** — Confidential client for JWT validation on the API server
10+
2. **mcp-director** — Public client for CLI authentication via Device Authorization Flow
11+
3. **mcp-web** — Confidential client for browser-based authentication via Authorization Code Flow
12+
13+
All three clients are configured to work with the `mcpserver` realm and include the `mcp-server-api` audience in their tokens.
14+
15+
## 1. mcp-server-api (API Client)
16+
17+
### Purpose
18+
This client validates JWT tokens issued by Keycloak on the McpServer API endpoints. It does not directly authenticate users but provides the API server with the credentials needed to verify tokens.
19+
20+
### Configuration
21+
- **Client Type**: Confidential (has a client secret)
22+
- **Client ID**: `mcp-server-api`
23+
- **Service Accounts**: Enabled (for token introspection if needed)
24+
- **Standard Flow**: Disabled (does not authenticate users directly)
25+
- **Direct Access Grants**: Disabled
26+
- **Audience**: Self-referencing (tokens issued to other clients must include `mcp-server-api` as audience)
27+
28+
### Usage in Code
29+
The API server validates incoming JWT tokens using the client secret and authority URL configured in `appsettings.yaml`:
30+
31+
```yaml
32+
Mcp:
33+
Auth:
34+
Authority: http://localhost:7080/realms/mcpserver
35+
Audience: mcp-server-api
36+
ClientSecret: <retrieved-from-keycloak>
37+
RequireHttpsMetadata: false
38+
```
39+
40+
**Location**: `lib/McpServer/appsettings.yaml` (or environment variables/user secrets)
41+
42+
**Code Reference**: `lib/McpServer/src/McpServer.Services/Options/OidcAuthOptions.cs`
43+
44+
## 2. mcp-director (Director CLI Client)
45+
46+
### Purpose
47+
This client authenticates CLI users via the OAuth 2.0 Device Authorization Flow. Users run the Director CLI, receive a user code, navigate to a verification URL in their browser, and authenticate through Keycloak. Once authenticated, the CLI receives an access token.
48+
49+
### Configuration
50+
- **Client Type**: Public (no client secret required)
51+
- **Client ID**: `mcp-director`
52+
- **Device Authorization Grant**: Enabled
53+
- **Standard Flow**: Disabled
54+
- **Direct Access Grants**: Disabled
55+
- **Service Accounts**: Disabled
56+
57+
### Protocol Mappers
58+
- **mcp-server-api-audience**: Adds `mcp-server-api` to the `aud` claim
59+
- **realm-roles**: Maps user realm roles into the `realm_roles` claim
60+
61+
### OAuth 2.0 Device Flow
62+
63+
The Device Authorization Flow is a multi-step process designed for CLI tools and devices without a web browser:
64+
65+
1. **Device Authorization Request**: The CLI calls `/auth/device` (proxied to Keycloak) with the client ID and scopes
66+
2. **User Code Displayed**: Keycloak returns a `user_code`, `verification_uri`, and `device_code`
67+
3. **User Authentication**: The user navigates to the `verification_uri` in a browser, enters the `user_code`, and authenticates with Keycloak
68+
4. **Token Polling**: The CLI polls `/auth/token` (proxied to Keycloak) with the `device_code` until the user completes authentication
69+
5. **Token Issued**: Once authenticated, Keycloak returns an `access_token` and `refresh_token`
70+
71+
The Director CLI caches tokens locally and automatically refreshes them when expired.
72+
73+
### Usage in Code
74+
75+
**CLI Implementation**: `src/McpServer.Director/Auth/OidcAuthService.cs`
76+
77+
The Director CLI discovers OIDC configuration from the MCP server:
78+
79+
```bash
80+
director auth login
81+
```
82+
83+
This:
84+
1. Calls `GET /auth/config` on the MCP server to retrieve authority and client ID
85+
2. Initiates the Device Flow by calling `POST /auth/device`
86+
3. Displays the user code and verification URI
87+
4. Polls `POST /auth/token` until the user completes authentication
88+
5. Caches the token locally in `~/.mcp-director/token.json`
89+
90+
**Configuration Discovery**: `lib/McpServer/src/McpServer.Support.Mcp/Controllers/AuthConfigController.cs`
91+
92+
The MCP server exposes `/auth/config` to provide OIDC metadata to CLI clients:
93+
94+
```json
95+
{
96+
"enabled": true,
97+
"authority": "http://localhost:7080/realms/mcpserver",
98+
"clientId": "mcp-director",
99+
"scopes": "openid profile email",
100+
"deviceAuthorizationEndpoint": "http://localhost:7147/auth/device",
101+
"tokenEndpoint": "http://localhost:7147/auth/token"
102+
}
103+
```
104+
105+
The MCP server proxies Device Flow requests to Keycloak so CLI clients only need to know the MCP server URL (e.g., `http://localhost:7147`), not the Keycloak URL.
106+
107+
## 3. mcp-web (Web UI Client)
108+
109+
### Purpose
110+
This client authenticates browser-based users via the OAuth 2.0 Authorization Code Flow. Users navigate to the McpServer Web UI, click "Login", are redirected to Keycloak for authentication, and redirected back to the Web UI with an authorization code that is exchanged for tokens.
111+
112+
### Configuration
113+
- **Client Type**: Confidential (has a client secret)
114+
- **Client ID**: `mcp-web`
115+
- **Standard Flow**: Enabled (Authorization Code Flow)
116+
- **Direct Access Grants**: Disabled
117+
- **Service Accounts**: Disabled
118+
- **Redirect URIs**:
119+
- `http://localhost:*` (for local development)
120+
- `<MCP_SERVER_URL>/*` (for deployed environments)
121+
- **Web Origins**:
122+
- `http://localhost:*`
123+
- `<MCP_SERVER_URL>`
124+
125+
### Protocol Mappers
126+
- **mcp-server-api-audience**: Adds `mcp-server-api` to the `aud` claim
127+
- **realm-roles**: Maps user realm roles into the `realm_roles` claim
128+
129+
### OAuth 2.0 Authorization Code Flow
130+
131+
The Authorization Code Flow is the standard OAuth 2.0 flow for browser-based applications:
132+
133+
1. **Login Initiated**: User clicks "Login" in the Web UI
134+
2. **Redirect to Keycloak**: User is redirected to Keycloak's authorization endpoint
135+
3. **Authentication**: User enters credentials in Keycloak
136+
4. **Authorization Code**: Keycloak redirects back to the Web UI with an authorization code
137+
5. **Token Exchange**: The Web UI backend exchanges the authorization code for an access token and refresh token using the client secret
138+
6. **Token Storage**: Tokens are stored in an HTTP-only session cookie
139+
140+
### Usage in Code
141+
142+
**Web UI Configuration**: `src/McpServer.Web/appsettings.Development.json`
143+
144+
```json
145+
{
146+
"Authentication": {
147+
"Schemes": {
148+
"OpenIdConnect": {
149+
"Authority": "http://localhost:7080/realms/mcpserver",
150+
"ClientId": "mcp-web",
151+
"ClientSecret": "<retrieved-from-keycloak>"
152+
}
153+
}
154+
}
155+
}
156+
```
157+
158+
**Code Reference**: `src/McpServer.Web/Program.cs`
159+
160+
The Web UI uses ASP.NET Core's OpenID Connect middleware to handle the Authorization Code Flow automatically. The middleware:
161+
- Redirects unauthenticated requests to Keycloak
162+
- Handles the callback with the authorization code
163+
- Exchanges the code for tokens using the client secret
164+
- Stores tokens in a secure session cookie
165+
- Automatically refreshes tokens when expired
166+
167+
The Web UI can also discover OIDC configuration from the MCP server by setting `DiscoverAuthorityFromMcpAuthConfig: true` in `appsettings.json`.
168+
169+
## Token Validation
170+
171+
All three clients issue tokens that include:
172+
- **Audience (`aud`)**: `mcp-server-api` (via the audience mapper)
173+
- **Realm Roles (`realm_roles`)**: User's assigned roles (`admin`, `agent-manager`, `viewer`)
174+
- **Subject (`sub`)**: Unique user identifier
175+
- **Username (`preferred_username`)**: User's username
176+
- **Email (`email`)**: User's email address
177+
178+
The MCP server validates these tokens using the `mcp-server-api` client credentials and the Keycloak authority URL.
179+
180+
## Security Considerations
181+
182+
### mcp-server-api
183+
- **Client secret must be kept secure** and never exposed in client applications
184+
- Configured via environment variables, user secrets, or secure configuration management
185+
- Only used server-side for token validation
186+
187+
### mcp-director
188+
- **Public client** (no client secret)
189+
- Device Flow ensures user authentication happens in a browser, not in the CLI
190+
- Tokens cached locally in `~/.mcp-director/token.json` with appropriate file permissions
191+
192+
### mcp-web
193+
- **Client secret must be kept secure** and never exposed to the browser
194+
- Stored server-side only (in `appsettings.Development.json` or user secrets)
195+
- Tokens stored in HTTP-only, secure session cookies (not accessible to JavaScript)
196+
197+
## Next Steps
198+
199+
- [Setup Scripts](./SetupScripts.md) — Running the Keycloak setup automation
200+
- [Helper Scripts](./HelperScripts.md) — Syncing client secrets into application configuration

0 commit comments

Comments
 (0)