This is an automated email from the ASF dual-hosted git repository.

epugh pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr-mcp.git


The following commit(s) were added to refs/heads/main by this push:
     new 38e3d10  feat(security): add configurable security bypass for HTTP 
mode (#44)
38e3d10 is described below

commit 38e3d10cb0ffe1f8e9ec5ecb10b5c288b6ee2b57
Author: Aditya Parikh <[email protected]>
AuthorDate: Wed Feb 4 16:20:20 2026 -0500

    feat(security): add configurable security bypass for HTTP mode (#44)
    
    Add ability to disable OAuth2 security in HTTP mode for local development
    and testing scenarios. Security can be toggled via spring.security.enabled
    property:
    
    - spring.security.enabled=true (default): Full OAuth2 authentication
    - spring.security.enabled=false: All requests permitted, @PreAuthorize 
bypassed
    
    Comprehensive guide for setting up Keycloak as OAuth2/OIDC identity
    provider for Solr MCP Server in HTTP mode.
    
    ---------
    
    Co-authored-by: Claude Opus 4.5 <[email protected]>
---
 keycloak.md | 534 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 534 insertions(+)

diff --git a/keycloak.md b/keycloak.md
new file mode 100644
index 0000000..2338c80
--- /dev/null
+++ b/keycloak.md
@@ -0,0 +1,534 @@
+# Keycloak Authentication Guide for Solr MCP Server
+
+This guide covers setting up [Keycloak](https://www.keycloak.org/) as an 
OAuth2/OpenID Connect identity provider for the Solr MCP Server running in HTTP 
mode.
+
+## Table of Contents
+
+- [Overview](#overview)
+- [Prerequisites](#prerequisites)
+- [Quick Start](#quick-start)
+- [Keycloak Setup](#keycloak-setup)
+  - [Running Keycloak](#running-keycloak)
+  - [Creating a Realm](#creating-a-realm)
+  - [Creating Clients](#creating-clients)
+  - [Creating Test Users](#creating-test-users)
+- [Spring Boot Configuration](#spring-boot-configuration)
+- [Running the Server](#running-the-server)
+- [Testing Authentication](#testing-authentication)
+- [User Management Options](#user-management-options)
+  - [Manual User Creation](#manual-user-creation)
+  - [User Federation (LDAP/AD)](#user-federation-ldapad)
+  - [Identity Brokering (GitHub, Google, 
etc.)](#identity-brokering-github-google-etc)
+  - [Self-Registration](#self-registration)
+  - [REST API](#rest-api)
+  - [JSON Import](#json-import)
+  - [Custom User Storage SPI](#custom-user-storage-spi)
+- [GitHub Identity Provider Setup](#github-identity-provider-setup)
+- [Role-Based Access Control (Optional)](#role-based-access-control-optional)
+- [Troubleshooting](#troubleshooting)
+
+## Overview
+
+The Solr MCP Server supports OAuth2 authentication via JWT tokens. Keycloak 
acts as the authorization server, issuing tokens that the MCP server validates. 
This enables:
+
+- Centralized user management
+- Single Sign-On (SSO) across multiple applications
+- Integration with external identity providers (GitHub, Google, LDAP, etc.)
+- Fine-grained role-based access control
+
+### Authentication Flow
+
+```
+User                    MCP Client            Keycloak              Solr MCP 
Server
+  │                        │                     │                        │
+  │─── Request Access ────►│                     │                        │
+  │                        │─── Auth Request ───►│                        │
+  │◄────────────────────── Login Page ◄──────────│                        │
+  │─── Credentials ───────────────────────────────►                       │
+  │                        │◄─── JWT Token ──────│                        │
+  │                        │─── API Request + Token ────────────────────►│
+  │                        │                     │      (validates JWT)   │
+  │                        │◄─────────────────────────── Response ────────│
+  │◄─── Result ────────────│                     │                        │
+```
+
+## Prerequisites
+
+- Docker (for running Keycloak)
+- Java 25 (for Solr MCP Server)
+- Gradle (for building the server)
+
+## Quick Start
+
+```bash
+# 1. Start Keycloak
+docker run -d --name keycloak \
+  -p 8180:8080 \
+  -e KC_BOOTSTRAP_ADMIN_USERNAME=admin \
+  -e KC_BOOTSTRAP_ADMIN_PASSWORD=admin \
+  quay.io/keycloak/keycloak:26.0 start-dev
+
+# 2. Configure Keycloak (see detailed steps below)
+# - Create realm: solr-mcp
+# - Create client: solr-mcp-client
+
+# 3. Run Solr MCP Server with security enabled
+export PROFILES=http
+export SECURITY_ENABLED=true
+export OAUTH2_ISSUER_URI=http://localhost:8180/realms/solr-mcp
+./gradlew bootRun
+```
+
+## Keycloak Setup
+
+### Running Keycloak
+
+**Development Mode (Docker):**
+
+```bash
+docker run -d --name keycloak \
+  -p 8180:8080 \
+  -e KC_BOOTSTRAP_ADMIN_USERNAME=admin \
+  -e KC_BOOTSTRAP_ADMIN_PASSWORD=admin \
+  quay.io/keycloak/keycloak:26.0 start-dev
+```
+
+Access the admin console at `http://localhost:8180` and log in with 
`admin/admin`.
+
+**Production Mode:**
+
+For production deployments, use a proper database backend and TLS:
+
+```bash
+docker run -d --name keycloak \
+  -p 8443:8443 \
+  -e KC_BOOTSTRAP_ADMIN_USERNAME=admin \
+  -e KC_BOOTSTRAP_ADMIN_PASSWORD=<secure-password> \
+  -e KC_DB=postgres \
+  -e KC_DB_URL=jdbc:postgresql://db-host:5432/keycloak \
+  -e KC_DB_USERNAME=keycloak \
+  -e KC_DB_PASSWORD=<db-password> \
+  -e KC_HOSTNAME=keycloak.example.com \
+  quay.io/keycloak/keycloak:26.0 start
+```
+
+### Creating a Realm
+
+1. Log into Keycloak Admin Console: `http://localhost:8180/admin`
+2. Click the dropdown in the top-left (shows "master")
+3. Click **Create realm**
+4. Realm name: `solr-mcp`
+5. Click **Create**
+
+### Creating Clients
+
+You need at least one client for applications to authenticate against.
+
+#### Resource Server Client (for the MCP Server)
+
+1. Navigate to **Clients** → **Create client**
+2. Configure:
+   - Client ID: `solr-mcp-server`
+   - Client type: `OpenID Connect`
+3. Click **Next**
+4. Client authentication: **ON** (confidential client)
+5. Authentication flow: Enable **Service accounts roles**
+6. Click **Next** → **Save**
+
+#### Public Client (for testing/MCP Inspector)
+
+1. Navigate to **Clients** → **Create client**
+2. Configure:
+   - Client ID: `solr-mcp-client`
+   - Client type: `OpenID Connect`
+3. Click **Next**
+4. Client authentication: **OFF** (public client)
+5. Click **Next**
+6. Configure access settings:
+   - Valid redirect URIs: `http://localhost:6274/*`, `http://localhost:*`
+   - Web origins: `*` or `http://localhost:6274`
+7. Click **Save**
+
+### Creating Test Users
+
+1. Navigate to **Users** → **Add user**
+2. Configure:
+   - Username: `testuser`
+   - Email: `[email protected]`
+   - Email verified: **ON**
+3. Click **Create**
+4. Go to the **Credentials** tab
+5. Click **Set password**
+6. Enter password and disable **Temporary**
+7. Click **Save**
+
+## Spring Boot Configuration
+
+The Solr MCP Server is pre-configured to work with any OAuth2/OIDC provider. 
Update `application-http.properties`:
+
+```properties
+# Security toggle - set to true to enable OAuth2 authentication
+spring.security.enabled=${SECURITY_ENABLED:true}
+
+# Keycloak OAuth2 Configuration
+# Format: https://<keycloak-host>/realms/<realm-name>
+spring.security.oauth2.resourceserver.jwt.issuer-uri=${OAUTH2_ISSUER_URI:http://localhost:8180/realms/solr-mcp}
+```
+
+No code changes are required—the existing `McpServerConfiguration` handles JWT 
validation automatically by discovering Keycloak's JWKS endpoint from the 
issuer URI.
+
+## Running the Server
+
+**With Security Enabled:**
+
+```bash
+export PROFILES=http
+export SECURITY_ENABLED=true
+export OAUTH2_ISSUER_URI=http://localhost:8180/realms/solr-mcp
+./gradlew bootRun
+```
+
+**Without Security (Development Only):**
+
+```bash
+export PROFILES=http
+export SECURITY_ENABLED=false
+./gradlew bootRun
+```
+
+## Testing Authentication
+
+### Obtain a Token
+
+**Using Resource Owner Password Grant (for testing):**
+
+```bash
+curl -X POST 
"http://localhost:8180/realms/solr-mcp/protocol/openid-connect/token"; \
+  -H "Content-Type: application/x-www-form-urlencoded" \
+  -d "client_id=solr-mcp-client" \
+  -d "username=testuser" \
+  -d "password=yourpassword" \
+  -d "grant_type=password"
+```
+
+Response:
+
+```json
+{
+  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
+  "expires_in": 300,
+  "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
+  "token_type": "Bearer"
+}
+```
+
+### Call the MCP Server
+
+```bash
+# Store token in variable
+TOKEN=$(curl -s -X POST 
"http://localhost:8180/realms/solr-mcp/protocol/openid-connect/token"; \
+  -d "client_id=solr-mcp-client" \
+  -d "username=testuser" \
+  -d "password=yourpassword" \
+  -d "grant_type=password" | jq -r '.access_token')
+
+# Call MCP endpoint
+curl -X POST http://localhost:8080/mcp \
+  -H "Authorization: Bearer $TOKEN" \
+  -H "Content-Type: application/json" \
+  -d '{"jsonrpc":"2.0","method":"tools/list","id":1}'
+```
+
+## User Management Options
+
+Keycloak provides multiple ways to manage users beyond manual creation.
+
+### Manual User Creation
+
+As described above, create users via **Users** → **Add user** in the admin 
console.
+
+
+### Identity Brokering (GitHub, Google, etc.)
+
+Allow users to authenticate via external identity providers. See [GitHub 
Identity Provider Setup](#github-identity-provider-setup) for a detailed 
example.
+
+Supported providers include:
+- GitHub
+- Google
+- Microsoft/Azure AD
+- Facebook
+- Twitter/X
+- Any SAML 2.0 or OIDC provider
+
+### Self-Registration
+
+Allow users to create their own accounts:
+
+1. Navigate to **Realm settings** → **Login** tab
+2. Enable **User registration**
+3. Optionally enable:
+   - Email verification
+   - Terms and conditions
+   - reCAPTCHA
+
+### REST API
+
+Programmatically create users:
+
+```bash
+# Get admin token
+TOKEN=$(curl -s -X POST 
"http://localhost:8180/realms/master/protocol/openid-connect/token"; \
+  -d "client_id=admin-cli" \
+  -d "username=admin" \
+  -d "password=admin" \
+  -d "grant_type=password" | jq -r '.access_token')
+
+# Create user
+curl -X POST "http://localhost:8180/admin/realms/solr-mcp/users"; \
+  -H "Authorization: Bearer $TOKEN" \
+  -H "Content-Type: application/json" \
+  -d '{
+    "username": "newuser",
+    "email": "[email protected]",
+    "enabled": true,
+    "emailVerified": true,
+    "credentials": [{
+      "type": "password",
+      "value": "temppassword",
+      "temporary": true
+    }]
+  }'
+```
+
+### JSON Import
+
+Bulk import users during realm setup:
+
+```json
+{
+  "realm": "solr-mcp",
+  "users": [
+    {
+      "username": "user1",
+      "email": "[email protected]",
+      "enabled": true,
+      "credentials": [{"type": "password", "value": "pass123"}],
+      "realmRoles": ["user", "solr-query"]
+    },
+    {
+      "username": "user2",
+      "email": "[email protected]",
+      "enabled": true,
+      "credentials": [{"type": "password", "value": "pass456"}],
+      "realmRoles": ["admin"]
+    }
+  ]
+}
+```
+
+Import via CLI:
+
+```bash
+/opt/keycloak/bin/kc.sh import --file realm-export.json
+```
+
+### Custom User Storage SPI
+
+For databases or custom backends, implement Keycloak's User Storage SPI to 
authenticate against your existing user database without migration.
+
+## GitHub Identity Provider Setup
+
+### Step 1: Create GitHub OAuth App
+
+1. Go to GitHub → **Settings** → **Developer settings** → **OAuth Apps** → 
**New OAuth App**
+
+   Direct link: https://github.com/settings/applications/new
+
+2. Fill in the form:
+
+| Field | Value |
+|-------|-------|
+| Application name | `Solr MCP Server` |
+| Homepage URL | `http://localhost:8180` |
+| Authorization callback URL | 
`http://localhost:8180/realms/solr-mcp/broker/github/endpoint` |
+
+> **Note:** The callback URL format is: 
`https://<keycloak-host>/realms/<realm-name>/broker/github/endpoint`
+
+3. Click **Register application**
+4. Note the **Client ID** and generate a **Client Secret**
+
+### Step 2: Configure Keycloak
+
+1. Log into Keycloak Admin Console
+2. Select your realm (`solr-mcp`)
+3. Navigate to **Identity Providers** → **Add provider** → **GitHub**
+4. Configure:
+
+| Field | Value |
+|-------|-------|
+| Client ID | `<from GitHub>` |
+| Client Secret | `<from GitHub>` |
+| Default Scopes | `user:email` (optional) |
+
+5. Click **Save**
+
+### Step 3: Test GitHub Login
+
+1. Open: `http://localhost:8180/realms/solr-mcp/account`
+2. Click the **GitHub** button
+3. Authorize on GitHub
+4. You're logged into Keycloak with your GitHub account
+
+### Optional: Map GitHub Data
+
+Add mappers to import GitHub profile data:
+
+1. Navigate to **Identity Providers** → **GitHub** → **Mappers**
+2. Click **Add mapper**
+3. Example configurations:
+
+| Name | Mapper Type | Claim | User Attribute |
+|------|-------------|-------|----------------|
+| GitHub Username | Attribute Importer | `login` | `github_username` |
+| GitHub Avatar | Attribute Importer | `avatar_url` | `avatar` |
+| GitHub Email | Attribute Importer | `email` | `email` |
+
+### Optional: Auto-Assign Roles
+
+Automatically assign roles to GitHub users:
+
+1. Navigate to **Identity Providers** → **GitHub** → **Mappers**
+2. Click **Add mapper**
+3. Configure:
+   - Mapper Type: `Hardcoded Role`
+   - Role: Select a role (e.g., `solr-query`)
+
+## Role-Based Access Control (Optional)
+
+To use Keycloak roles with Spring Security's `@PreAuthorize` annotations, add 
a JWT converter.
+
+### Create Roles in Keycloak
+
+1. Navigate to **Realm roles** → **Create role**
+2. Create roles like `admin`, `solr-query`, `solr-admin`
+3. Assign roles to users via **Users** → select user → **Role mappings**
+
+### Configure Spring Security
+
+Add to `McpServerConfiguration.java`:
+
+```java
+import 
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
+import 
org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
+
+@Bean
+@ConditionalOnProperty(name = "spring.security.enabled", havingValue = "true", 
matchIfMissing = true)
+public JwtAuthenticationConverter jwtAuthenticationConverter() {
+    JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new 
JwtGrantedAuthoritiesConverter();
+    // Keycloak stores realm roles in realm_access.roles
+    grantedAuthoritiesConverter.setAuthoritiesClaimName("realm_access.roles");
+    grantedAuthoritiesConverter.setAuthorityPrefix("ROLE_");
+
+    JwtAuthenticationConverter jwtConverter = new JwtAuthenticationConverter();
+    
jwtConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);
+    return jwtConverter;
+}
+```
+
+Wire it into the security filter chain:
+
+```java
+@Bean
+@ConditionalOnProperty(name = "spring.security.enabled", havingValue = "true", 
matchIfMissing = true)
+SecurityFilterChain securityFilterChain(HttpSecurity http, 
JwtAuthenticationConverter jwtAuthenticationConverter) throws Exception {
+    return http
+        .authorizeHttpRequests(auth -> {
+            auth.requestMatchers("/actuator").permitAll();
+            auth.requestMatchers("/actuator/*").permitAll();
+            auth.requestMatchers("/mcp").permitAll();
+            auth.anyRequest().authenticated();
+        })
+        .oauth2ResourceServer(oauth2 -> oauth2
+            .jwt(jwt -> 
jwt.jwtAuthenticationConverter(jwtAuthenticationConverter))
+        )
+        .with(McpServerOAuth2Configurer.mcpServerOAuth2(), (mcpAuthorization) 
-> {
+            mcpAuthorization.authorizationServer(issuerUrl);
+        })
+        .cors(cors -> cors.configurationSource(corsConfigurationSource()))
+        .csrf(CsrfConfigurer::disable)
+        .build();
+}
+```
+
+### Use Role Annotations
+
+```java
+@PreAuthorize("hasRole('admin')")
+public String deleteCollection(String name) { ... }
+
+@PreAuthorize("hasRole('solr-query')")
+public String executeQuery(String collection, String query) { ... }
+
+@PreAuthorize("hasAnyRole('solr-query', 'solr-admin')")
+public String getSchema(String collection) { ... }
+```
+
+### Keycloak Role Locations
+
+| Location | Claim Path | Use Case |
+|----------|------------|----------|
+| Realm roles | `realm_access.roles` | Global roles across all clients |
+| Client roles | `resource_access.<client-id>.roles` | Roles specific to a 
client |
+
+## Troubleshooting
+
+### Common Issues
+
+**"Invalid token" or 401 Unauthorized:**
+
+- Verify `OAUTH2_ISSUER_URI` matches your Keycloak realm URL exactly
+- Check that the token hasn't expired
+- Ensure Keycloak is accessible from the MCP server
+
+**"Unable to resolve issuer":**
+
+- Keycloak must be running and accessible
+- Check the issuer URL: 
`http://localhost:8180/realms/solr-mcp/.well-known/openid-configuration`
+
+**CORS errors with MCP Inspector:**
+
+- Ensure Web origins are configured in your Keycloak client
+- Add `http://localhost:6274` to Web origins
+
+**Token doesn't contain roles:**
+
+- Verify the user has roles assigned in Keycloak
+- Check the token contents at https://jwt.io
+- Ensure you're reading the correct claim path (`realm_access.roles`)
+
+### Useful Commands
+
+**Inspect a JWT token:**
+
+```bash
+# Decode token (without verification)
+echo $TOKEN | cut -d'.' -f2 | base64 -d 2>/dev/null | jq
+```
+
+**Check Keycloak OpenID configuration:**
+
+```bash
+curl http://localhost:8180/realms/solr-mcp/.well-known/openid-configuration | 
jq
+```
+
+**View Keycloak logs:**
+
+```bash
+docker logs -f keycloak
+```
+
+## References
+
+- [Keycloak Documentation](https://www.keycloak.org/documentation)
+- [Spring Security OAuth2 Resource 
Server](https://docs.spring.io/spring-security/reference/servlet/oauth2/resource-server/index.html)
+- [MCP Specification](https://spec.modelcontextprotocol.io/)

Reply via email to