This is an automated email from the ASF dual-hosted git repository. acosentino pushed a commit to branch keycloak-introspection in repository https://gitbox.apache.org/repos/asf/camel-jbang-examples.git
commit 0e6d381e021816088fccc90e87ec16317f9a7597 Author: Andrea Cosentino <[email protected]> AuthorDate: Tue Oct 21 12:10:24 2025 +0200 Keycloak Token Introspection Support Example Signed-off-by: Andrea Cosentino <[email protected]> --- keycloak-introspection-rest/README.adoc | 691 +++++++++++++++++++++ keycloak-introspection-rest/application.properties | 39 ++ keycloak-introspection-rest/rest-api.camel.yaml | 207 ++++++ 3 files changed, 937 insertions(+) diff --git a/keycloak-introspection-rest/README.adoc b/keycloak-introspection-rest/README.adoc new file mode 100644 index 0000000..c57e47c --- /dev/null +++ b/keycloak-introspection-rest/README.adoc @@ -0,0 +1,691 @@ += Keycloak Token Introspection REST API + +This example demonstrates how to secure REST APIs using Apache Camel with Keycloak OAuth 2.0 Token Introspection (RFC 7662). +It shows the difference between token introspection and standard JWT validation, highlighting the benefits of real-time token validation. + +== Features + +* Public endpoint accessible without authentication +* Protected endpoints with two validation methods: + ** Token Introspection (RFC 7662) - Real-time validation via Keycloak + ** Standard JWT validation - Local signature verification +* Integration with Keycloak using OAuth2/OpenID Connect +* Real-time token revocation detection +* Configurable introspection caching +* Role-based access control (RBAC) +* Comparison endpoint showing configuration details + +== What is Token Introspection? + +Token introspection is an OAuth 2.0 endpoint (RFC 7662) that allows you to validate tokens in real-time by querying the authorization server (Keycloak). + +=== Introspection vs. Local JWT Validation + +[cols="1,2,2", options="header"] +|=== +|Feature +|Token Introspection +|Local JWT Validation + +|*Validation Speed* +|Network call required (cached) +|Very fast (local) + +|*Revocation Detection* +|✅ Real-time detection +|❌ No detection until expiration + +|*Offline Support* +|❌ Requires Keycloak connectivity +|✅ Works offline + +|*Security* +|Excellent - always up-to-date +|Good - trusts JWT signature + +|*Best For* +|Security-critical operations, token revocation scenarios +|High-throughput, trusted environments +|=== + +=== When to Use Introspection + +Use token introspection when: + +* You need to detect revoked tokens before their expiration +* Security is critical (e.g., payment processing, admin operations) +* You want centralized token validation +* You have compliance requirements for real-time validation +* You need to immediately invalidate sessions + +=== Performance Optimization + +This example includes caching to minimize performance impact: + +* **Cache Enabled**: Results are cached to reduce API calls +* **Configurable TTL**: Balance security (lower TTL) and performance (higher TTL) +* **Default TTL**: 120 seconds (2 minutes) + +== Prerequisites + +* JBang installed (https://www.jbang.dev) +* Docker installed for running Keycloak +* Basic understanding of OAuth2/OpenID Connect +* Understanding of JWT tokens + +== Dependencies + +This example requires the `camel-keycloak` component. + +== Install JBang + +First install JBang according to https://www.jbang.dev + +When JBang is installed then you should be able to run from a shell: + +[source,sh] +---- +$ jbang --version +---- + +This will output the version of JBang. + +To run this example you can either install Camel on JBang via: + +[source,sh] +---- +$ jbang app install camel@apache/camel +---- + +Which allows to run Camel JBang with `camel` as shown below. + +== Running Keycloak + +=== Option 1: Using Camel JBang Infra (Recommended) + +Starting from Camel JBang 4.16.0-SNAPSHOT, you can easily run Keycloak using the built-in infrastructure support: + +[source,sh] +---- +$ jbang -Dcamel.jbang.version=4.16.0-SNAPSHOT camel@apache/camel infra run keycloak +---- + +This will automatically start Keycloak configured with: +* Admin username: `admin` +* Admin password: `admin` +* Port: `8080` + +Wait a few seconds for Keycloak to fully start before proceeding to configuration. + +To stop Keycloak later: + +[source,sh] +---- +$ jbang -Dcamel.jbang.version=4.16.0-SNAPSHOT camel@apache/camel infra stop keycloak +---- + +=== Option 2: Using Docker Manually + +Alternatively, you can run Keycloak manually with Docker: + +[source,sh] +---- +$ docker run -d \ + --name keycloak \ + -p 8180:8080 \ + -e KEYCLOAK_ADMIN=admin \ + -e KEYCLOAK_ADMIN_PASSWORD=admin \ + quay.io/keycloak/keycloak:latest \ + start-dev +---- + +Wait a few seconds for Keycloak to fully start before proceeding to configuration. + +== Keycloak Configuration + +After Keycloak starts, you need to configure it: + +=== 1. Access Keycloak Admin Console + +Open your browser and navigate to: +* If using Camel JBang infra: http://localhost:8080 +* If using Docker manually: http://localhost:8180 + +Login with: +* Username: `admin` +* Password: `admin` + +=== 2. Create a Realm + +1. Click on the dropdown in the top left (says "master") +2. Click "Create Realm" +3. Enter realm name: `camel` +4. Click "Create" + +=== 3. Create a Client + +Token introspection requires a confidential client with client credentials. + +1. In the left menu, click "Clients" +2. Click "Create client" +3. Enter Client ID: `camel-client` +4. Click "Next" +5. **Enable "Client authentication"** (required for introspection) +6. Enable "Service accounts roles" +7. Enable "Direct access grants" +8. Click "Next" +9. Add Valid Redirect URIs: `http://localhost:8080/*` +10. Click "Save" +11. Go to the "Credentials" tab +12. Copy the "Client Secret" value +13. Update the `application.properties` file with this secret: ++ +[source,properties] +---- +keycloak.client.secret=<your-client-secret> +---- + +IMPORTANT: Client authentication must be enabled for token introspection to work. This is because the introspection endpoint requires authentication with client credentials. + +=== 4. Create an Admin Role + +1. In the left menu, click "Realm roles" +2. Click "Create role" +3. Enter role name: `admin` +4. Click "Save" + +=== 5. Create Users + +==== Create Regular User (without admin role) + +1. In the left menu, click "Users" +2. Click "Add user" +3. Enter username: `testuser` +4. Enter email: `[email protected]` +5. Enter first name: `Test` +6. Enter last name: `User` +7. Click "Create" +8. Go to "Credentials" tab +9. Click "Set password" +10. Enter password: `password` +11. Disable "Temporary" toggle +12. Click "Save" + +==== Create Admin User (with admin role) + +1. In the left menu, click "Users" +2. Click "Add user" +3. Enter username: `admin-user` +4. Enter email: `[email protected]` +5. Enter first name: `Admin` +6. Enter last name: `User` +7. Click "Create" +8. Go to "Credentials" tab +9. Click "Set password" +10. Enter password: `password` +11. Disable "Temporary" toggle +12. Click "Save" +13. Go to "Role mapping" tab +14. Click "Assign role" +15. Select the `admin` role +16. Click "Assign" + +== Running the Example + +After Keycloak is configured, start the Camel application: + +[source,sh] +---- +$ jbang -Dcamel.jbang.version=4.16.0-SNAPSHOT camel@apache/camel run * +---- + +The application will start on port 8081 with the following endpoints: + +* `http://localhost:8081/api/public` - Public endpoint (no auth required) +* `http://localhost:8081/api/info` - Info endpoint showing configuration +* `http://localhost:8081/api/protected-introspection` - Protected with token introspection +* `http://localhost:8081/api/protected-standard` - Protected with local JWT validation + +== Testing the Endpoints + +=== Test Info Endpoint + +View the configuration and available endpoints: + +[source,sh] +---- +$ curl http://localhost:8081/api/info | jq +---- + +Expected response: +[source,json] +---- +{ + "example": "Keycloak Token Introspection REST API", + "endpoints": { + "public": { + "path": "/api/public", + "security": "none", + "description": "Public endpoint, no authentication required" + }, + "protected_introspection": { + "path": "/api/protected-introspection", + "security": "token_introspection", + "role": "admin", + "description": "Real-time token validation via Keycloak introspection endpoint (RFC 7662)", + "features": [ + "Detects revoked tokens", + "Centralized validation", + "Caching enabled" + ] + }, + "protected_standard": { + "path": "/api/protected-standard", + "security": "local_jwt", + "role": "admin", + "description": "Local JWT signature verification without introspection", + "features": [ + "Fast offline validation", + "No network overhead", + "Cannot detect token revocation" + ] + } + }, + "introspection_config": { + "enabled": "true", + "cache_enabled": "true", + "cache_ttl_seconds": "120" + } +} +---- + +=== Test Public Endpoint (No Authentication) + +[source,sh] +---- +$ curl http://localhost:8081/api/public | jq +---- + +Expected response: +[source,json] +---- +{ + "message": "This is a public endpoint, no authentication required", + "timestamp": "2024-10-20T10:30:00", + "security": "none" +} +---- + +=== Obtain Access Token + +Set the Keycloak port based on your setup: + +[source,sh] +---- +# Use 8080 for camel infra, 8180 for manual Docker +export KEYCLOAK_PORT=8080 +---- + +Get an access token for the admin user: + +[source,sh] +---- +export ADMIN_TOKEN=$(curl -X POST http://localhost:${KEYCLOAK_PORT}/realms/camel/protocol/openid-connect/token \ + -H "Content-Type: application/x-www-form-urlencoded" \ + -d "username=admin-user" \ + -d "password=password" \ + -d "grant_type=password" \ + -d "client_id=camel-client" \ + -d "client_secret=<your-client-secret>" \ + | jq -r '.access_token') + +echo "Token obtained: ${ADMIN_TOKEN:0:50}..." +---- + +=== Test Protected Endpoint with Introspection + +[source,sh] +---- +$ curl -H "Authorization: Bearer $ADMIN_TOKEN" \ + http://localhost:8081/api/protected-introspection | jq +---- + +Expected response: +[source,json] +---- +{ + "message": "This is a protected endpoint using Token Introspection (RFC 7662)", + "timestamp": "2024-10-20T10:30:00", + "security": "introspection", + "role_required": "admin", + "validation": "real-time via Keycloak introspection endpoint", + "features": [ + "Detects revoked tokens before expiration", + "Centralized validation", + "Caching enabled (TTL: 120s)" + ] +} +---- + +=== Test Protected Endpoint with Standard JWT + +[source,sh] +---- +$ curl -H "Authorization: Bearer $ADMIN_TOKEN" \ + http://localhost:8081/api/protected-standard | jq +---- + +Expected response: +[source,json] +---- +{ + "message": "This is a protected endpoint using standard JWT validation", + "timestamp": "2024-10-20T10:30:00", + "security": "local-jwt", + "role_required": "admin", + "validation": "local JWT signature verification", + "features": [ + "Fast offline validation", + "No network calls to Keycloak", + "Cannot detect revoked tokens before expiration" + ] +} +---- + +=== Test with Regular User (Should Fail) + +Try with a user who doesn't have the admin role: + +[source,sh] +---- +export USER_TOKEN=$(curl -X POST http://localhost:${KEYCLOAK_PORT}/realms/camel/protocol/openid-connect/token \ + -H "Content-Type: application/x-www-form-urlencoded" \ + -d "username=testuser" \ + -d "password=password" \ + -d "grant_type=password" \ + -d "client_id=camel-client" \ + -d "client_secret=<your-client-secret>" \ + | jq -r '.access_token') + +# This should fail with 403 Forbidden +$ curl -i -H "Authorization: Bearer $USER_TOKEN" \ + http://localhost:8081/api/protected-introspection +---- + +This will return a **403 Forbidden** error because `testuser` does not have the `admin` role. + +Expected error response: +[source,json] +---- +{ + "error": "Forbidden", + "message": "Access denied. User does not have required roles: [admin]", + "timestamp": "2024-10-20T10:30:00", + "status": 403 +} +---- + +=== Test with Invalid/Missing Token + +[source,sh] +---- +# Missing token - should return 403 +curl -i http://localhost:8081/api/protected-introspection + +# Invalid token - should return 403 +curl -i -H "Authorization: Bearer invalid_token_here" \ + http://localhost:8081/api/protected-introspection +---- + +Both requests will return **403 Forbidden** with an appropriate error message. + +== Demonstrating Token Revocation + +One of the key benefits of token introspection is the ability to detect revoked tokens in real-time. + +=== Scenario: Logout a User and Test Token Validity + +1. **Get an access token** for admin-user (as shown above) + +2. **Verify the token works** with introspection: ++ +[source,sh] +---- +# This should succeed +curl -H "Authorization: Bearer $ADMIN_TOKEN" \ + http://localhost:8081/api/protected-introspection +---- + +3. **Logout the user in Keycloak**: ++ + * Go to Keycloak Admin Console + * Navigate to Users → Find "admin-user" + * Click on the user + * Go to "Sessions" tab + * Click "Sign out" to invalidate all sessions + +4. **Test with introspection endpoint** (should fail immediately): ++ +[source,sh] +---- +# This will return 401/403 because the token is no longer active +curl -i -H "Authorization: Bearer $ADMIN_TOKEN" \ + http://localhost:8081/api/protected-introspection +---- ++ +The introspection endpoint will detect that the token has been revoked. + +5. **Test with standard JWT endpoint** (may still work until expiration): ++ +[source,sh] +---- +# This might still succeed because local JWT validation +# doesn't check with Keycloak about revocation +curl -i -H "Authorization: Bearer $ADMIN_TOKEN" \ + http://localhost:8081/api/protected-standard +---- ++ +The standard JWT endpoint may still allow access because it only validates the JWT signature locally without checking if the session is still active in Keycloak. + +This demonstrates the key advantage of token introspection: **real-time revocation detection**. + +== How It Works + +=== Security Policies + +The example defines two security policies in `rest-api.camel.yaml`: + +1. **Introspection Policy** - Uses Keycloak's introspection endpoint: + +[source,yaml] +---- +- name: keycloakIntrospectionPolicy + type: org.apache.camel.component.keycloak.security.KeycloakSecurityPolicy + properties: + serverUrl: "{{keycloak.server.url}}" + realm: "{{keycloak.realm}}" + clientId: "{{keycloak.client.id}}" + clientSecret: "{{keycloak.client.secret}}" + requiredRoles: "admin" + useTokenIntrospection: true + introspectionCacheEnabled: true + introspectionCacheTtl: 120 +---- + +2. **Standard Policy** - Uses local JWT parsing: + +[source,yaml] +---- +- name: keycloakStandardPolicy + type: org.apache.camel.component.keycloak.security.KeycloakSecurityPolicy + properties: + serverUrl: "{{keycloak.server.url}}" + realm: "{{keycloak.realm}}" + clientId: "{{keycloak.client.id}}" + clientSecret: "{{keycloak.client.secret}}" + requiredRoles: "admin" + # useTokenIntrospection is false by default +---- + +=== Route Protection + +Routes are protected by referencing the appropriate policy: + +[source,yaml] +---- +- route: + id: protected-introspection-api + from: + uri: "platform-http:/api/protected-introspection" + steps: + - policy: + ref: keycloakIntrospectionPolicy + - setBody: + simple: | + { + "message": "Protected with introspection" + } +---- + +=== Introspection Flow + +When a request arrives at the introspection-protected endpoint: + +1. **Extract Token**: Policy extracts the Bearer token from Authorization header +2. **Check Cache**: If caching is enabled, check if result is cached +3. **Call Introspection Endpoint**: If not cached, POST to Keycloak's introspection endpoint +4. **Validate Response**: Check if token is active and has required roles +5. **Cache Result**: Store result with TTL for subsequent requests +6. **Allow/Deny**: Proceed with route or return 403 Forbidden + +== Monitoring Introspection Calls + +To see introspection in action, enable debug logging: + +Add to `application.properties`: + +[source,properties] +---- +# Enable debug logging for Keycloak component +logging.level.org.apache.camel.component.keycloak=DEBUG +---- + +You'll see logs like: + +[source] +---- +[INFO] Introspecting token at: http://localhost:8080/realms/camel/protocol/openid-connect/token/introspect +[DEBUG] Returning cached introspection result for token +[DEBUG] Token is active: true, username: admin-user, scope: email profile +---- + +== Stopping + +To stop the Camel application, press `Ctrl+C`. + +To stop Keycloak: + +If you used Camel JBang infra: +[source,sh] +---- +$ jbang -Dcamel.jbang.version=4.16.0-SNAPSHOT camel@apache/camel infra stop keycloak +---- + +If you used Docker manually: +[source,sh] +---- +$ docker stop keycloak +$ docker rm keycloak +---- + +== Troubleshooting + +=== 401 Unauthorized + +* Verify the access token is valid and not expired +* Check that the Authorization header is properly formatted: `Bearer <token>` +* Ensure the client secret in `application.properties` matches Keycloak +* Verify client authentication is enabled in Keycloak (required for introspection) + +=== 403 Forbidden + +* Verify the user has the required role (admin role) +* Check role assignments in Keycloak Admin Console +* Ensure the token was obtained with the correct user credentials + +=== Connection Refused / Network Errors + +* Ensure Keycloak is running on the correct port (8080 for camel infra, 8180 for Docker) +* Verify the Keycloak server URL in `application.properties` matches your setup +* Check network connectivity between Camel app and Keycloak + +=== Invalid Client Credentials + +* Check that the client ID and secret in `application.properties` match the Keycloak client configuration +* Verify the realm name is correct +* Ensure "Client authentication" is enabled in the Keycloak client settings + +=== Introspection Not Working + +* Verify `keycloak.introspection.enabled=true` in application.properties +* Check that client is configured as confidential (client authentication enabled) +* Ensure client has proper permissions for introspection +* Review Keycloak logs for errors + +=== Slow Performance + +* Enable caching: `keycloak.introspection.cache.enabled=true` +* Increase cache TTL: `keycloak.introspection.cache.ttl=300` (5 minutes) +* Check network latency to Keycloak +* Consider using standard JWT for non-critical endpoints + +== Architecture + +This example demonstrates: + +1. **Platform HTTP Component**: Provides HTTP server capabilities +2. **Keycloak Security Policy**: Validates OAuth2 JWT tokens +3. **Token Introspection (RFC 7662)**: Real-time token validation +4. **Introspection Caching**: Reduces load on Keycloak +5. **Role-Based Access Control**: Restricts endpoints based on user roles +6. **RESTful API Design**: Multiple endpoints with different security levels + +== Best Practices + +1. **Use Introspection Selectively**: Apply introspection to security-critical endpoints, use JWT for high-throughput APIs +2. **Enable Caching**: Always enable caching in production to reduce Keycloak load +3. **Tune Cache TTL**: Balance security requirements with performance needs +4. **Monitor Performance**: Track introspection call latency and adjust caching +5. **Use HTTPS in Production**: Protect token transmission and introspection calls +6. **Secure Client Secrets**: Use environment variables or secret management systems +7. **Handle Failures Gracefully**: Implement error handling for network failures +8. **Consider Circuit Breakers**: Protect against Keycloak downtime + +== Next Steps + +* Add refresh token handling +* Implement multiple security levels for different endpoints +* Add API documentation with OpenAPI/Swagger +* Implement request/response logging +* Add metrics and monitoring +* Implement rate limiting +* Add CORS configuration for web applications +* Test token revocation scenarios +* Implement session management + +== Learn More + +* https://tools.ietf.org/html/rfc7662[OAuth 2.0 Token Introspection (RFC 7662)] +* https://camel.apache.org/components/latest/keycloak-component.html[Camel Keycloak Component Documentation] +* https://www.keycloak.org/docs/latest/securing_apps/[Keycloak Securing Applications] +* https://www.keycloak.org/docs/latest/authorization_services/[Keycloak Authorization Services] + +== Help and Contributions + +If you hit any problem using Camel or have some feedback, then please +https://camel.apache.org/community/support/[let us know]. + +We also love contributors, so +https://camel.apache.org/community/contributing/[get involved] :-) + +The Camel riders! diff --git a/keycloak-introspection-rest/application.properties b/keycloak-introspection-rest/application.properties new file mode 100644 index 0000000..27b4d20 --- /dev/null +++ b/keycloak-introspection-rest/application.properties @@ -0,0 +1,39 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# Keycloak Server Configuration +# These properties are referenced by the security policy beans defined in rest-api.camel.yaml +# Port 8080 is used when running Keycloak via: camel infra run keycloak +# Port 8180 is used when running Keycloak manually via Docker (see README.adoc) +keycloak.server.url=http://localhost:8080 +keycloak.realm=camel +keycloak.client.id=camel-client +keycloak.client.secret=************ + +# Token Introspection Configuration +# Enable token introspection for real-time validation +# This allows detection of revoked tokens before their expiration +keycloak.introspection.enabled=true +# Enable caching to reduce load on Keycloak +keycloak.introspection.cache.enabled=true +# Cache TTL in seconds (120 = 2 minutes) +# Balance between security (lower TTL) and performance (higher TTL) +keycloak.introspection.cache.ttl=120 + +# Additional Camel configuration +camel.main.name = KeycloakIntrospectionRestExample +camel.server.port=8081 diff --git a/keycloak-introspection-rest/rest-api.camel.yaml b/keycloak-introspection-rest/rest-api.camel.yaml new file mode 100644 index 0000000..a29fae2 --- /dev/null +++ b/keycloak-introspection-rest/rest-api.camel.yaml @@ -0,0 +1,207 @@ +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## --------------------------------------------------------------------------- + +# camel-k: dependency=camel:keycloak + +# Global exception handler for authorization failures +# Handles CamelAuthorizationException to return proper HTTP status codes +- onException: + exception: + - "org.apache.camel.CamelAuthorizationException" + handled: + constant: true + steps: + - setHeader: + name: CamelHttpResponseCode + constant: 403 + - setHeader: + name: Content-Type + constant: application/json + - setBody: + simple: | + { + "error": "Forbidden", + "message": "Access denied. ${exception.message}", + "timestamp": "${date:now:yyyy-MM-dd'T'HH:mm:ss}", + "status": 403 + } + - log: + message: "Authorization failed: ${exception.message}" + +# Bean definition for Keycloak security policy with Token Introspection enabled +# This policy validates tokens in real-time via Keycloak's introspection endpoint (RFC 7662) +# Introspection allows detection of revoked tokens before their expiration time +- beans: + - name: keycloakIntrospectionPolicy + type: org.apache.camel.component.keycloak.security.KeycloakSecurityPolicy + properties: + serverUrl: "{{keycloak.server.url}}" + realm: "{{keycloak.realm}}" + clientId: "{{keycloak.client.id}}" + clientSecret: "{{keycloak.client.secret}}" + requiredRoles: "admin" + # Enable OAuth 2.0 Token Introspection (RFC 7662) + useTokenIntrospection: "{{keycloak.introspection.enabled}}" + # Enable caching to reduce Keycloak API calls + introspectionCacheEnabled: "{{keycloak.introspection.cache.enabled}}" + # Cache TTL in seconds + introspectionCacheTtl: "{{keycloak.introspection.cache.ttl}}" + + # Standard policy without introspection for comparison + - name: keycloakStandardPolicy + type: org.apache.camel.component.keycloak.security.KeycloakSecurityPolicy + properties: + serverUrl: "{{keycloak.server.url}}" + realm: "{{keycloak.realm}}" + clientId: "{{keycloak.client.id}}" + clientSecret: "{{keycloak.client.secret}}" + requiredRoles: "admin" + # Uses local JWT parsing (default, no introspection) + +# Public endpoint - no authentication required +- route: + id: public-api + from: + uri: "platform-http:/api/public" + steps: + - setBody: + simple: | + { + "message": "This is a public endpoint, no authentication required", + "timestamp": "${date:now:yyyy-MM-dd'T'HH:mm:ss}", + "security": "none" + } + - setHeader: + name: Content-Type + constant: application/json + - log: + message: "Public API called" + +# Protected endpoint with Token Introspection - requires admin role +# Tokens are validated in real-time via Keycloak's introspection endpoint +# This allows detection of revoked tokens before their expiration +- route: + id: protected-introspection-api + from: + uri: "platform-http:/api/protected-introspection" + steps: + - policy: + ref: keycloakIntrospectionPolicy + - setBody: + simple: | + { + "message": "This is a protected endpoint using Token Introspection (RFC 7662)", + "timestamp": "${date:now:yyyy-MM-dd'T'HH:mm:ss}", + "security": "introspection", + "role_required": "admin", + "validation": "real-time via Keycloak introspection endpoint", + "features": [ + "Detects revoked tokens before expiration", + "Centralized validation", + "Caching enabled (TTL: {{keycloak.introspection.cache.ttl}}s)" + ] + } + - setHeader: + name: Content-Type + constant: application/json + - log: + message: "Protected API with Introspection called successfully" + +# Protected endpoint with standard JWT validation (for comparison) +# Uses local JWT parsing without contacting Keycloak +- route: + id: protected-standard-api + from: + uri: "platform-http:/api/protected-standard" + steps: + - policy: + ref: keycloakStandardPolicy + - setBody: + simple: | + { + "message": "This is a protected endpoint using standard JWT validation", + "timestamp": "${date:now:yyyy-MM-dd'T'HH:mm:ss}", + "security": "local-jwt", + "role_required": "admin", + "validation": "local JWT signature verification", + "features": [ + "Fast offline validation", + "No network calls to Keycloak", + "Cannot detect revoked tokens before expiration" + ] + } + - setHeader: + name: Content-Type + constant: application/json + - log: + message: "Protected API with standard JWT called successfully" + +# Info endpoint - shows the difference between introspection and standard validation +- route: + id: info-api + from: + uri: "platform-http:/api/info" + steps: + - setBody: + simple: | + { + "example": "Keycloak Token Introspection REST API", + "endpoints": { + "public": { + "path": "/api/public", + "security": "none", + "description": "Public endpoint, no authentication required" + }, + "protected_introspection": { + "path": "/api/protected-introspection", + "security": "token_introspection", + "role": "admin", + "description": "Real-time token validation via Keycloak introspection endpoint (RFC 7662)", + "features": [ + "Detects revoked tokens", + "Centralized validation", + "Caching enabled" + ] + }, + "protected_standard": { + "path": "/api/protected-standard", + "security": "local_jwt", + "role": "admin", + "description": "Local JWT signature verification without introspection", + "features": [ + "Fast offline validation", + "No network overhead", + "Cannot detect token revocation" + ] + } + }, + "introspection_config": { + "enabled": "{{keycloak.introspection.enabled}}", + "cache_enabled": "{{keycloak.introspection.cache.enabled}}", + "cache_ttl_seconds": "{{keycloak.introspection.cache.ttl}}" + }, + "keycloak_config": { + "server_url": "{{keycloak.server.url}}", + "realm": "{{keycloak.realm}}", + "client_id": "{{keycloak.client.id}}" + } + } + - setHeader: + name: Content-Type + constant: application/json + - log: + message: "Info API called"
