Skip to content

Conversation

@madebygps
Copy link
Collaborator

This pull request introduces significant improvements to Keycloak integration for OAuth authentication, especially for VS Code and GitHub Copilot Chat users. The changes standardize Keycloak's route under /auth, simplify and enhance the realm configuration, update deployment scripts and infrastructure to match, and improve documentation for end users. Additionally, the authentication logic is refactored for better maintainability and debugging.

Keycloak Integration and Route Standardization:

  • Keycloak is now configured to serve all content under /auth using the --http-relative-path=/auth flag in its Dockerfile, ensuring consistent routing for authentication endpoints.
  • All infrastructure and environment variables referencing Keycloak URLs are updated to include the /auth path, ensuring correct routing and token issuer values for both server and agent components. [1] [2] [3] [4] [5] [6] [7]

Keycloak Realm and Client Configuration Updates:

  • The Keycloak realm JSON is restructured to define a vscode-mcp-client with pre-configured redirect URIs for VS Code, and a demo user for easier testing. Trusted hosts are expanded to include VS Code domains and Docker networking addresses, and additional protocol mappers are added for compatibility. [1] [2] [3] [4] [5]

Authentication Logic and Middleware Improvements:

  • The MCP server now uses a new KeycloakAuthProvider with Dynamic Client Registration (DCR) support, replacing direct JWT verification, and introduces a debug middleware to log token claims for easier troubleshooting. [1] [2] [3]
  • The agent disables Keycloak authentication headers by default and cleans up import logic, making it easier to run with or without authentication. [1] [2] [3]
  • Documentation in the Keycloak DCR registration helper clarifies that the direct container URL should be used.

Documentation and Developer Experience:

  • The README is updated with a detailed, step-by-step guide (with screenshots) for using the Keycloak-backed MCP server with GitHub Copilot Chat and VS Code, including how to authenticate and verify expense tracking.

- Add "Use Keycloak OAuth MCP server with GitHub Copilot" section to README
- Add step-by-step instructions for connecting VS Code to deployed MCP server
- Include screenshots for authentication flow (allow access, sign-in, redirect)
- Add Spanish translation of the new section to spanish/README.md
- Configure VS Code redirect URIs in Keycloak realm for DCR support
- Update infra and keycloak configs for VS Code OAuth redirect handling
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request implements comprehensive Keycloak OAuth 2.0 integration for the MCP (Model Context Protocol) server, enabling VS Code and GitHub Copilot Chat to authenticate users dynamically. The changes standardize Keycloak routing under /auth, introduce a custom authentication provider with Dynamic Client Registration (DCR) support, and provide detailed end-user documentation with screenshots.

Key Changes:

  • Keycloak now serves all content under /auth path via --http-relative-path=/auth flag, with corresponding updates to all infrastructure and environment configurations
  • New KeycloakAuthProvider class implements a minimal DCR proxy to work around Keycloak's token authentication method limitation
  • VS Code redirect URIs and a demo test user are pre-configured in the Keycloak realm for seamless integration

Reviewed changes

Copilot reviewed 13 out of 18 changed files in this pull request and generated 16 comments.

Show a summary per file
File Description
servers/keycloak_provider.py New authentication provider with DCR proxy and debug endpoints for Keycloak integration
servers/auth_mcp.py Refactored to use KeycloakAuthProvider with debug middleware for token claim inspection
keycloak/realm.json Updated with VS Code client configuration, demo user credentials, and expanded trusted hosts
keycloak/Dockerfile Added --http-relative-path=/auth flag to serve Keycloak under /auth path
infra/main.bicep Updated all Keycloak URLs to include /auth prefix for proper routing
infra/http-routes.bicep Removed path rewriting since Keycloak now handles /auth internally
infra/write_env.sh Added KEYCLOAK_AGENT_REALM_URL to environment script
infra/write_env.ps1 Added KEYCLOAK_AGENT_REALM_URL to PowerShell environment script
agents/agentframework_http.py Removed Keycloak authentication headers to simplify agent deployment
agents/keycloak_auth.py Added clarification about using direct container URLs
README.md Added step-by-step guide with screenshots for GitHub Copilot Chat authentication
spanish/README.md Spanish translation of the new Copilot Chat authentication guide
.vscode/mcp.json Added production MCP server configuration
screenshots/kc-select-tools-4.jpg Screenshot for documentation (binary file)
Comments suppressed due to low confidence (1)

servers/keycloak_provider.py:216

  • This import of module json is redundant, as it was previously imported on line 27.
                    request_data = json.loads(body)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

required_scopes=[],
# Audience should match the value in Keycloak's mcp:tools scope
# Default to the base_url for local dev
audience=None
Copy link

Copilot AI Dec 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Setting audience=None explicitly disables audience validation for JWT tokens, which reduces security. The comment mentions this should match Keycloak's audience mapper, so consider setting a proper audience value or documenting why audience validation is intentionally disabled.

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 18 changed files in this pull request and generated 10 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

.vscode/mcp.json Outdated
"0.0.0.0:5678",
"servers/basic_mcp_stdio.py"
]
},
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a trailing comma after the last server definition (line 30) which is followed by an empty line before the closing brace. While some JSON parsers tolerate trailing commas, this violates the JSON specification and may cause parsing errors in strict JSON parsers. Remove the comma on line 30 or the empty line 31 to ensure valid JSON.

Suggested change
},
}

Copilot uses AI. Check for mistakes.
Comment on lines +91 to +92
"http://127.0.0.1/*",
"http://localhost/*",
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The redirectUris configuration includes overly permissive wildcards for localhost ("http://127.0.0.1/" and "http://localhost/"). This allows any path on localhost to be used as a redirect URI, which could be exploited in authorization code interception attacks. Consider restricting these to specific ports or paths, such as "http://localhost:*/redirect" or "http://127.0.0.1:*/redirect" to limit the attack surface.

Suggested change
"http://127.0.0.1/*",
"http://localhost/*",
"http://127.0.0.1:*/redirect",
"http://localhost:*/redirect",

Copilot uses AI. Check for mistakes.
Comment on lines +136 to +141
except Exception as e:
logger.error(f"Failed to fetch Keycloak metadata: {e}")
return JSONResponse(
{
"error": "server_error",
"error_description": f"Failed to fetch Keycloak metadata: {e}",
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message includes the raw exception details using an f-string, which could potentially leak sensitive information such as connection details, internal paths, or stack traces to clients. Consider logging the full exception details server-side but returning a more generic error message to the client, or at minimum sanitizing the exception message before including it in the response.

Suggested change
except Exception as e:
logger.error(f"Failed to fetch Keycloak metadata: {e}")
return JSONResponse(
{
"error": "server_error",
"error_description": f"Failed to fetch Keycloak metadata: {e}",
except Exception:
logger.exception("Failed to fetch Keycloak metadata")
return JSONResponse(
{
"error": "server_error",
"error_description": "Failed to fetch Keycloak metadata. Please contact the server administrator.",

Copilot uses AI. Check for mistakes.
output KEYCLOAK_REALM_URL string = useKeycloak ? '${httpRoutes!.outputs.routeConfigUrl}/auth/realms/${keycloakRealmName}' : ''
output KEYCLOAK_ADMIN_CONSOLE string = useKeycloak ? '${httpRoutes!.outputs.routeConfigUrl}/auth/admin' : ''
output KEYCLOAK_ADMIN_CONSOLE string = useKeycloak ? '${httpRoutes!.outputs.routeConfigUrl}/auth/admin/master/console' : ''
output KEYCLOAK_DIRECT_URL string = keycloak.outputs.uri
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The output KEYCLOAK_DIRECT_URL is not conditional on useKeycloak, which means it will attempt to access keycloak.outputs.uri even when Keycloak is not deployed. This will cause a deployment error when mcpAuthProvider is not set to "keycloak". Consider making this conditional like the other Keycloak outputs: output KEYCLOAK_DIRECT_URL string = useKeycloak ? keycloak.outputs.uri : ''

Suggested change
output KEYCLOAK_DIRECT_URL string = keycloak.outputs.uri
output KEYCLOAK_DIRECT_URL string = useKeycloak ? keycloak.outputs.uri : ''

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 18 changed files in this pull request and generated 18 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


Para usar el servidor MCP desplegado con GitHub Copilot Chat:

1. Para evitar conflictos, detén los servidores MCP de `mcp.json` y deshabilitá los servidores MCP de gastos en las herramientas de GitHub Copilot Chat.
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The verb form should use "Detenga" instead of "detené" for consistency with formal instructions, and "desactive" instead of "deshabilitá". The instruction mixes formal and informal Spanish (vos/tú forms).

Suggested change
1. Para evitar conflictos, detén los servidores MCP de `mcp.json` y deshabilitá los servidores MCP de gastos en las herramientas de GitHub Copilot Chat.
1. Para evitar conflictos, Detenga los servidores MCP de `mcp.json` y desactive los servidores MCP de gastos en las herramientas de GitHub Copilot Chat.

Copilot uses AI. Check for mistakes.

1. Para evitar conflictos, detén los servidores MCP de `mcp.json` y deshabilitá los servidores MCP de gastos en las herramientas de GitHub Copilot Chat.
2. Seleccioná "MCP: Add Server" desde la Paleta de Comandos de VS Code
3. Seleccioná "HTTP" como tipo de servidor
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The verb form should use "Seleccione" instead of "Seleccioná" for consistency with formal instructions throughout the documentation.

Suggested change
3. Seleccioná "HTTP" como tipo de servidor
3. Seleccione "HTTP" como tipo de servidor

Copilot uses AI. Check for mistakes.
1. Para evitar conflictos, detén los servidores MCP de `mcp.json` y deshabilitá los servidores MCP de gastos en las herramientas de GitHub Copilot Chat.
2. Seleccioná "MCP: Add Server" desde la Paleta de Comandos de VS Code
3. Seleccioná "HTTP" como tipo de servidor
4. Ingresá la URL del servidor MCP desde `azd env get-value MCP_SERVER_URL`
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The verb form should use "Ingrese" instead of "Ingresá" for consistency with formal instructions throughout the documentation.

Suggested change
4. Ingresá la URL del servidor MCP desde `azd env get-value MCP_SERVER_URL`
4. Ingrese la URL del servidor MCP desde `azd env get-value MCP_SERVER_URL`

Copilot uses AI. Check for mistakes.
2. Seleccioná "MCP: Add Server" desde la Paleta de Comandos de VS Code
3. Seleccioná "HTTP" como tipo de servidor
4. Ingresá la URL del servidor MCP desde `azd env get-value MCP_SERVER_URL`
5. Deberías ver una pantalla de autenticación de Keycloak abrirse en tu navegador. Seleccioná "Allow access":
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The verb form should use "Debería" instead of "Deberías" for consistency with formal instructions throughout the documentation.

Suggested change
5. Deberías ver una pantalla de autenticación de Keycloak abrirse en tu navegador. Seleccioná "Allow access":
5. Debería ver una pantalla de autenticación de Keycloak abrirse en tu navegador. Seleccioná "Allow access":

Copilot uses AI. Check for mistakes.
9. Probálo con una consulta de seguimiento de gastos:

```text
Registrá un gasto de 75 dólares de útiles de oficina en mi visa el viernes pasado
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The verb form should use "Registre" instead of "Registrá" for consistency with formal instructions throughout the documentation.

Suggested change
Registrá un gasto de 75 dólares de útiles de oficina en mi visa el viernes pasado
Registre un gasto de 75 dólares de útiles de oficina en mi visa el viernes pasado

Copilot uses AI. Check for mistakes.

![Redirección de VS Code después del inicio de sesión de Keycloak](../screenshots/kc-redirect-3.jpg)

8. Habilitá el servidor MCP en las herramientas de GitHub Copilot Chat:
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The verb form should use "Habilite" instead of "Habilitá" for consistency with formal instructions throughout the documentation.

Suggested change
8. Habilitá el servidor MCP en las herramientas de GitHub Copilot Chat:
8. Habilite el servidor MCP en las herramientas de GitHub Copilot Chat:

Copilot uses AI. Check for mistakes.
Comment on lines +85 to +95
"enabled": true,
"email": "testuser@example.com",
"firstName": "Test",
"lastName": "User",
"credentials": [
{
"type": "password",
"value": "testpass",
"temporary": false
}
]
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The demo user has a hardcoded password in plain text. While acceptable for development/demo purposes, this configuration file appears to be used in production deployments based on the infrastructure code. Consider documenting that this user should be removed or the password changed immediately after deployment, or use environment variable substitution for the password value.

Suggested change
"enabled": true,
"email": "testuser@example.com",
"firstName": "Test",
"lastName": "User",
"credentials": [
{
"type": "password",
"value": "testpass",
"temporary": false
}
]
"enabled": false,
"email": "testuser@example.com",
"firstName": "Test",
"lastName": "User",
"attributes": {
"note": [
"Demo user. Remove this user or set a secure password immediately after deployment."
]
}

Copilot uses AI. Check for mistakes.
body = await request.body()

# Forward to Keycloak's DCR endpoint
async with httpx.AsyncClient(timeout=10.0) as client:
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The timeout value of 10.0 seconds is hardcoded. Consider making this configurable through a class parameter or constant, as network conditions and Keycloak response times may vary across different deployment environments.

Copilot uses AI. Check for mistakes.
Comment on lines +416 to +419
2. Seleccioná "MCP: Add Server" desde la Paleta de Comandos de VS Code
3. Seleccioná "HTTP" como tipo de servidor
4. Ingresá la URL del servidor MCP desde `azd env get-value MCP_SERVER_URL`
5. Deberías ver una pantalla de autenticación de Keycloak abrirse en tu navegador. Seleccioná "Allow access":
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The verb form should use "Seleccione" instead of "Seleccioná" for consistency with formal instructions throughout the documentation.

Suggested change
2. Seleccioná "MCP: Add Server" desde la Paleta de Comandos de VS Code
3. Seleccioná "HTTP" como tipo de servidor
4. Ingresá la URL del servidor MCP desde `azd env get-value MCP_SERVER_URL`
5. Deberías ver una pantalla de autenticación de Keycloak abrirse en tu navegador. Seleccioná "Allow access":
2. Seleccione "MCP: Add Server" desde la Paleta de Comandos de VS Code
3. Seleccione "HTTP" como tipo de servidor
4. Ingresá la URL del servidor MCP desde `azd env get-value MCP_SERVER_URL`
5. Deberías ver una pantalla de autenticación de Keycloak abrirse en tu navegador. Seleccione "Allow access":

Copilot uses AI. Check for mistakes.
Comment on lines +72 to +73
"http://127.0.0.1/*",
"http://localhost/*",
Copy link

Copilot AI Dec 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The wildcard redirect URIs for localhost and 127.0.0.1 are overly permissive. Consider restricting to specific port ranges or paths to reduce the attack surface. For example, use specific ports like "http://127.0.0.1:5000/" instead of "http://127.0.0.1/".

Copilot uses AI. Check for mistakes.
@madebygps madebygps merged commit ba0dc32 into Azure-Samples:main Dec 18, 2025
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants