Oauth
Since Camel 4.12
The camel-oauth module comes with Processors that can be added to a route on the client and resource owner side. These processors intercept the message flow and perform the necessary authentication steps against an Identity Provider (IdP) in some specs it also called Authorization Server. Our primary choice of IdP is Keycloak
The idea is that a "Resource Owner" can give a "User Agent" access to some protected resources without sharing credentials directly with the agent.
For example, Alice has an account with Spotify and now wishes to use a cool service from Acme which compiles a daily playlist according based on Alice’s preferences. Instead of giving Acme her Spotify credentials (i.e. username/password) directly, Acme can obtain an access token from an Identity Provider that encodes the scope and duration for Acme to access Alice’s Spotify account. Alice can revoke access any time - Acme never sees more information than what Alice has granted and is necessary to perform the wanted service.
Maven users will need to add the following dependency to their
pom.xml
for this component:
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-oauth</artifactId>
<version>x.x.x</version>
<!-- use the same version as your Camel core version -->
</dependency>
Authentication/Authorization Flow Types
OIDC Authorization Code Flow
The Authorization Code Flow returns an Authorization Code to the Client, which can then exchange it for an ID Token and an Access Token directly. The Authorization Code flow is suitable for Clients that can securely maintain a Client Secret between themselves and the Authorization Server.
This code flow relies on user interaction with a browser based application. It is not suitable for fully automated authorization for example in the case of REST based service interaction.
For details see the OIDC 1.0 spec.
Configuration Properties
| Name | Description |
|---|---|
|
|
The base URL to the identity provider (e.g. https://oauth.localtest.me/kc/realms/camel ) |
|
|
Valid URI pattern a browser can redirect to after a successful login (e.g. http://127.0.0.1:8080/auth ). Must be registered with the identity provider. |
|
|
The client identifier registered with the identity provider. |
|
|
The client secret provided by the identity provider. |
|
|
(Optional) Valid URI pattern a browser can redirect to after a successful logout. Can be registered with the identity provider. |
Client Credentials Grant
A client can request an access token using only the client id and secret shared with the identity provider.
This flow is suitable for fully automated authorization, for example in the case of REST based service interaction.
For details see the OAuth 2.0 spec.
Configuration Properties
| Name | Description |
|---|---|
|
|
The base URL to the identity provider (e.g. https://oauth.localtest.me/kc/realms/camel ) |
|
|
The client identifier registered with the identity provider. |
|
|
The client secret provided by the identity provider. |
OAuth SPI for Component Integration
The camel-oauth module provides an SPI (
OAuthClientAuthenticationFactory
) that allows other Camel components to use OAuth Client Credentials authentication without a compile-time dependency on camel-oauth. Components discover the factory at runtime via Camel’s FactoryFinder mechanism. If camel-oauth is not on the classpath, a clear error message instructs the user to add it.
Components that support this integration include: camel-openai, camel-huggingface, camel-docling and camel-ibm-watsonx-ai. Each of these components exposes an
oauthProfile
parameter.
Named Profiles (Multiple Identity Providers)
Named profiles allow configuring multiple identity providers in a single application. Each profile is identified by a name and has its own set of properties.
Configuration Properties
Properties are resolved from
camel.oauth.<profileName>.*
:
| Name | Required | Description |
|---|---|---|
|
|
Yes |
The client identifier registered with the identity provider. |
|
|
Yes |
The client secret provided by the identity provider. |
|
|
Yes |
The OAuth 2.0 token endpoint URL. |
|
|
No |
The OAuth 2.0 scope to request. |
|
|
No |
Whether to cache tokens. Default:
|
|
|
No |
Default token expiry if
|
|
|
No |
Safety margin subtracted from token expiry to refresh early. Default:
|
Example: Multiple Identity Providers
# Keycloak for backend services
camel.oauth.keycloak.client-id=backend-client
camel.oauth.keycloak.client-secret=backend-secret
camel.oauth.keycloak.token-endpoint=https://keycloak.example.com/realms/main/protocol/openid-connect/token
# Azure AD for OpenAI
camel.oauth.azure.client-id=openai-client
camel.oauth.azure.client-secret=openai-secret
camel.oauth.azure.token-endpoint=https://login.microsoftonline.com/tenant/oauth2/v2.0/token
camel.oauth.azure.scope=https://cognitiveservices.azure.com/.default
Components reference a profile by name via the
oauthProfile
parameter:
- route:
from:
uri: "direct:start"
steps:
- to:
uri: "openai:chat-completion"
parameters:
model: "gpt-4"
oauthProfile: "azure"
Incoming Bearer Token Validation
The camel-oauth module also provides an
OAuthTokenValidationFactory
SPI for validating incoming
Authorization: Bearer
tokens. HTTP consumer components such as
Platform HTTP
,
Servlet
,
Jetty
,
Netty HTTP
, and
Undertow HTTP/WebSocket
can use this SPI without a compile-time dependency on
camel-oauth. This module is the default provider for the SPI; runtime-specific integrations can provide their own
implementation backed by their native security stack.
JWT tokens are validated locally with JWKS. The validator verifies the JWS signature, checks
exp
and
nbf
, and validates
iss
and
aud
. Opaque tokens are validated with RFC 7662 introspection
using a strict bounded HTTP/JSON implementation.
HTTP consumer integrations share the same request contract. The inbound header must use
Authorization: Bearer <token>
. The token part must use the RFC 6750
b64token
character set
(
A-Z
,
a-z
,
0-9
,
-
,
.
,
_
,
~
,
+
,
/
) with optional trailing
=
padding, and is
limited to 8192 characters. Malformed Bearer syntax and oversized tokens are rejected before the
validation factory is called. When an HTTP transport exposes duplicate
Authorization
header
values to Camel, they are also treated as malformed.
HTTP integrations reject missing credentials and invalid tokens with HTTP 401. Missing
credentials receive the bare
WWW-Authenticate: Bearer
challenge, while invalid tokens receive
the RFC 6750
WWW-Authenticate: Bearer error="invalid_token"
challenge. Malformed
Authorization
headers are rejected with HTTP 400 and the
RFC 6750
WWW-Authenticate: Bearer error="invalid_request"
challenge. Token validation
infrastructure failures are rejected with HTTP 503. For valid tokens, the validation result is
stored on the exchange as the
CamelOAuthTokenValidationResult
exchange property and the raw
Authorization
header is removed before route processors run. Token validation is an
authentication check only: scope, role, or per-operation authorization is not enforced, and route
code must check scopes or claims itself from the
CamelOAuthTokenValidationResult
exchange
property.
Identity-Provider Failure Behavior
Calls to identity-provider infrastructure (OIDC discovery, JWKS fetch, and token introspection) are bounded by the configured connect and read timeouts. After a transport-level failure such as a connection refusal, timeout, or HTTP 5xx response, further attempts against the same endpoint URL are skipped for 30 seconds and requests fail fast; the resulting HTTP 503 rejections include the original failure as the cause in debug logs. The fail-fast window is shared JVM-wide across Camel contexts. It also applies to OIDC discovery performed during route startup, so restarting a route within 30 seconds of a startup discovery failure fails with the recorded cause until the window elapses.
JWT validation degrades gracefully during an identity-provider outage: when a JWKS refresh fails and a previously fetched key set is cached, the cached (possibly expired) keys are used so that requests with valid signatures continue to be accepted. HTTP 503 is returned only when no key set was ever fetched. The JWKS cache contains public key material only.
Incoming Token Validation Properties
Properties are resolved from
camel.oauth.<profileName>.*
when the HTTP endpoint or route starts.
Updates to property sources such as
application.properties
, secrets, config maps, or vault-backed
values require restarting the route or Camel context before they are used for token validation.
| Name | Required | Description |
|---|---|---|
|
|
No |
Base URL used to discover OIDC metadata from
|
|
|
For JWT if
|
JWKS URL used to validate JWT signatures. |
|
|
For opaque tokens if
|
RFC 7662 introspection endpoint used to validate opaque tokens. |
|
|
For opaque tokens |
Client identifier for introspection. If omitted,
|
|
|
For opaque tokens |
Client secret for introspection. If omitted,
|
|
|
No |
Fallback client identifier used by introspection when
|
|
|
No |
Fallback client secret used by introspection when
|
|
|
Yes, unless
|
Expected
|
|
|
Yes, unless
|
Comma-separated accepted
|
|
|
No |
Expected JWT
|
|
|
No |
Clock skew leeway for temporal claims. Default:
|
|
|
No |
JWKS cache TTL. Default:
|
|
|
No |
OIDC discovery metadata cache TTL. Default:
|
|
|
No |
Connect timeout for OIDC discovery, JWKS fetch, and introspection calls. Default:
|
|
|
No |
Read timeout for OIDC discovery, JWKS fetch, and introspection calls. Default:
|
|
|
No |
Whether JWT tokens must include an
|
|
|
No |
Comma-separated JWS algorithm allowlist, for example
|
|
|
No |
Set to
|
|
|
No |
Set to
|
|
|
No |
Set to
|
Choosing JWT Validation, OIDC Discovery, or Opaque Introspection
Camel chooses the validation path from the incoming token and the configured profile:
-
If only
introspection-endpointis configured, Camel introspects the token, regardless of token shape. -
If only
jwks-endpointis configured, Camel validates the token as a JWT. -
If both JWKS and introspection are configured, parseable signed JWT tokens are validated locally and other tokens are introspected. If a signed JWT fails local validation, Camel rejects it and does not fall back to introspection.
Local JWT validation avoids a per-request identity-provider call and is appropriate when the issuer
publishes signing keys through JWKS. Opaque introspection performs a blocking outbound HTTP call for
each validation and is appropriate when the issuer does not expose token contents to the resource
server. For production resource-server routes, configure
expected-issuer
and
expected-audience
and use HTTPS endpoints.
JWT Validation with an Explicit JWKS Endpoint
camel.oauth.myprofile.jwks-endpoint=https://idp.example.com/.well-known/jwks.json
camel.oauth.myprofile.expected-issuer=https://idp.example.com
camel.oauth.myprofile.expected-audience=my-api
camel.oauth.myprofile.connect-timeout-seconds=5
camel.oauth.myprofile.read-timeout-seconds=10
OIDC Discovery
Use
base-uri
when the identity provider exposes OIDC metadata at
/.well-known/openid-configuration
.
Camel discovers
jwks_uri
when it is not configured explicitly.
Camel discovers and enables
introspection_endpoint
only when introspection credentials are also configured.
This lets JWT-only resource servers use OIDC discovery against providers that advertise introspection metadata
without requiring unused client credentials.
When
expected-issuer
is not set, the configured
base-uri
is used as the expected issuer.
The discovery document
issuer
must match the configured expected issuer. When using the
programmatic
OAuthTokenValidationConfig.setOidcDiscoveryUrl()
API directly without
expected-issuer
, the discovery URL must use the standard
/.well-known/openid-configuration
path so Camel can derive and validate the expected issuer. For non-standard discovery URLs,
configure
expected-issuer
explicitly.
camel.oauth.myprofile.base-uri=https://idp.example.com/realms/main
camel.oauth.myprofile.expected-audience=my-api,my-api-v2
camel.oauth.myprofile.connect-timeout-seconds=5
camel.oauth.myprofile.read-timeout-seconds=10
Opaque Token Introspection
camel.oauth.myprofile.introspection-endpoint=https://idp.example.com/oauth2/introspect
camel.oauth.myprofile.introspection-client-id=resource-server-client
camel.oauth.myprofile.introspection-client-secret=resource-server-secret
camel.oauth.myprofile.expected-issuer=https://idp.example.com
camel.oauth.myprofile.expected-audience=my-api
camel.oauth.myprofile.connect-timeout-seconds=5
camel.oauth.myprofile.read-timeout-seconds=10
The introspection client secret is sensitive. Store it with the same secret-management mechanism used for other Camel secrets.
Mixed JWT and Opaque Tokens
camel.oauth.myprofile.jwks-endpoint=https://idp.example.com/.well-known/jwks.json
camel.oauth.myprofile.introspection-endpoint=https://idp.example.com/oauth2/introspect
camel.oauth.myprofile.introspection-client-id=resource-server-client
camel.oauth.myprofile.introspection-client-secret=resource-server-secret
camel.oauth.myprofile.expected-issuer=https://idp.example.com
camel.oauth.myprofile.expected-audience=my-api
camel.oauth.myprofile.connect-timeout-seconds=5
camel.oauth.myprofile.read-timeout-seconds=10
To use a runtime-specific or application-specific validator for one profile, bind an
OAuthTokenValidationFactory
bean and reference it from the profile:
camel.oauth.myprofile.validation-factory=#bean:myTokenValidationFactory
For the default unnamed profile, use
camel.oauth.validation-factory
.
If this property is configured, the referenced bean must exist and implement
OAuthTokenValidationFactory
; otherwise the route fails to start.
Use HTTPS endpoints for OIDC discovery, JWKS, and introspection in production. Plain HTTP endpoints
are rejected by default; set
allow-insecure-http=true
only for local testing or other trusted
development environments.
Opaque-token introspection performs a blocking outbound HTTP call for each token validation, so tune the timeout properties for the expected request rate and identity-provider latency.
Validation error details are intended for logs and diagnostics. Do not return
OAuthTokenValidationResult.getError()
directly to external clients; HTTP responses should use
generic messages such as
Unauthorized
.
Stable error categories are available through
OAuthTokenValidationResult.getErrorCode()
:
INVALID_TOKEN
,
INACTIVE_TOKEN
,
EXPIRED_TOKEN
,
NOT_YET_VALID
,
MISSING_EXPIRATION
,
INVALID_SIGNATURE
,
NO_MATCHING_KEY
,
UNSUPPORTED_ALGORITHM
,
INVALID_ISSUER
, and
INVALID_AUDIENCE
.
Using OAuth Programmatically
The
OAuthHelper
utility from
camel-support
can be used for both outgoing client credentials token acquisition and incoming bearer token validation.
Validate an Incoming Bearer Token
import org.apache.camel.spi.OAuthTokenValidationResult;
import org.apache.camel.support.OAuthHelper;
OAuthTokenValidationResult result;
try {
result = OAuthHelper.validateOAuthToken(camelContext, "myprofile", bearerToken);
} catch (RuntimeException e) {
// Treat configuration, network, and identity-provider failures as infrastructure errors.
throw new IllegalStateException("Token validation is unavailable", e);
}
if (!result.isValid()) {
OAuthTokenValidationResult.ErrorCode errorCode = result.getErrorCode();
// Use generic responses at HTTP boundaries. Do not expose result.getError() to external clients.
throw new IllegalArgumentException("Unauthorized");
}
String subject = result.getSubject();
String principalName = result.getName();
boolean canRead = result.hasScope("read");
String email = result.getClaim("email", String.class);
For the default unnamed profile, use:
OAuthTokenValidationResult result = OAuthHelper.validateOAuthToken(camelContext, bearerToken);
For advanced validation use cases, resolve
OAuthTokenValidationFactory
directly and pass an explicit
OAuthTokenValidationConfig
.
import org.apache.camel.spi.OAuthTokenValidationConfig;
import org.apache.camel.spi.OAuthTokenValidationFactory;
import org.apache.camel.spi.OAuthTokenValidationResult;
import org.apache.camel.support.OAuthHelper;
OAuthTokenValidationFactory factory = OAuthHelper.resolveOAuthTokenValidationFactory(camelContext, "myprofile");
OAuthTokenValidationConfig config = new OAuthTokenValidationConfig()
.setJwksEndpoint("https://idp.example.com/.well-known/jwks.json")
.setExpectedIssuer("https://idp.example.com")
.setExpectedAudience("my-api");
OAuthTokenValidationResult result = factory.validateToken(config, bearerToken);
Resolve an Outgoing Client Credentials Token
import org.apache.camel.support.OAuthHelper;
String token = OAuthHelper.resolveOAuthToken(camelContext, "keycloak");
For advanced use cases, the
OAuthClientAuthenticationFactory
can be used directly with an explicit
OAuthClientConfig
:
import org.apache.camel.spi.OAuthClientAuthenticationFactory;
import org.apache.camel.spi.OAuthClientConfig;
import org.apache.camel.support.ResolverHelper;
OAuthClientAuthenticationFactory factory = ResolverHelper.resolveMandatoryService(
context, OAuthClientAuthenticationFactory.FACTORY,
OAuthClientAuthenticationFactory.class, "camel-oauth");
String token = factory.resolveToken(
new OAuthClientConfig()
.setClientId("my-client")
.setClientSecret("my-secret")
.setTokenEndpoint("https://idp.example.com/token"));
Trusted Certificates
Naturally, we want all communication between camel and the identity provider to be secured at the transport layer (TLS). For this, the Camel service need’s to trust the identity provider’s certificate.
# Fetch the certificate from the IdP endpoint
openssl s_client -connect oauth.localtest.me:443 | openssl x509 > cluster.crt
# Import certificate to Java Keystore (i.e. trust the certificate)
sudo keytool -import -alias keycloak -file cluster.crt -keystore $JAVA_HOME/lib/security/cacerts -storepass changeit
# Trust this cert on macOS
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain cluster.crt
# Trust this cert on Linux
sudo cp cluster.crt /etc/pki/ca-trust/source/anchors/ && sudo update-ca-trust
OAuth for Kafka
For Kafka we can use strimzi-kafka-oauth directly, for example like this …
Supported Runtimes
Camel OAuth is supported in all Camel Runtimes
-
camel-main
-
spring-boot
-
quarkus
Specifically, it provides an abstraction for the various http-platforms that are native to these runtimes.
Supported Cluster Environments
Camel applications requiring OAuth authentication are likely part of a larger more complex system architecture, which also likely are part of some larger Kubernetes cluster deployment. In our examples we support these Kubernetes environments …
-
Local Cluster (e.g. DockerDesktop Kubernetes )
-
Remote K3S Cluster
-
Red Hat OpenShift
As part of this project we provide a set of Helm charts that install the required infrastructure components for the respective cluster environment. For details, have a look at the dedicated readme .
Keycloak is already configured in such a way that below examples should run without further ado.
Camel OAuth Examples
There is a comprehensive set of camel-oauth examples as part of camel-cloud-examples . You’ll find camel-jbang kubernetes examples for every OAuth flow, for every runtime, on every supported cluster.
For example in the following makefile :
k8s-fetch-cert:
@mkdir -p tls
@echo -n | openssl s_client -connect oauth.localtest.me:443 | openssl x509 > tls/cluster.crt
k8s-export: k8s-fetch-cert
@$(CAMEL_CMD) kubernetes export platform-http-files/* tls/* \
--dep=org.apache.camel:camel-oauth:4.22.1-SNAPSHOT \
--gav=examples:platform-http-oauth:1.0.0 \
--property=camel.oauth.base-uri=https://oauth.localtest.me/kc/realms/camel \
--property=camel.oauth.redirect-uri=http://127.0.0.1:8080/auth \
--property=camel.oauth.logout.redirect-uri=http://127.0.0.1:8080/ \
--property=camel.oauth.client-id=camel-client \
--property=camel.oauth.client-secret=camel-client-secret \
--property=ssl.truststore.certificates=tls/cluster.crt \
--ignore-loading-error=true \
--image-builder=docker \
--image-push=false \
--trait container.image-pull-policy=IfNotPresent \
--runtime=camel-main