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

camel.oauth.base-uri

The base URL to the identity provider (e.g. https://oauth.localtest.me/kc/realms/camel)

camel.oauth.redirect-uri

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.

camel.oauth.client-id

The client identifier registered with the identity provider.

camel.oauth.client-secret

The client secret provided by the identity provider.

camel.oauth.logout.redirect-uri

(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

camel.oauth.base-uri

The base URL to the identity provider (e.g. https://oauth.localtest.me/kc/realms/camel)

camel.oauth.client-id

The client identifier registered with the identity provider.

camel.oauth.client-secret

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

camel.oauth.<profile>.client-id

Yes

The client identifier registered with the identity provider.

camel.oauth.<profile>.client-secret

Yes

The client secret provided by the identity provider.

camel.oauth.<profile>.token-endpoint

Yes

The OAuth 2.0 token endpoint URL.

camel.oauth.<profile>.scope

No

The OAuth 2.0 scope to request.

camel.oauth.<profile>.cache-tokens

No

Whether to cache tokens. Default: true.

camel.oauth.<profile>.cached-tokens-default-expiry-seconds

No

Default token expiry if expires_in is not in the response. Default: 3600.

camel.oauth.<profile>.cached-tokens-expiration-margin-seconds

No

Safety margin subtracted from token expiry to refresh early. Default: 5.

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. Components such as Platform HTTP 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.

Incoming Token Validation Properties

Properties are resolved from camel.oauth.<profileName>.*:

Name Required Description

camel.oauth.<profile>.base-uri

No

Base URL used to discover OIDC metadata from /.well-known/openid-configuration.

camel.oauth.<profile>.jwks-endpoint

For JWT if base-uri is not set

JWKS URL used to validate JWT signatures.

camel.oauth.<profile>.introspection-endpoint

For opaque tokens if base-uri is not set

RFC 7662 introspection endpoint used to validate opaque tokens.

camel.oauth.<profile>.introspection-client-id

For opaque tokens

Client identifier for introspection. If omitted, client-id is used.

camel.oauth.<profile>.introspection-client-secret

For opaque tokens

Client secret for introspection. If omitted, client-secret is used. Treat this value as a secret.

camel.oauth.<profile>.client-id

No

Fallback client identifier used by introspection when introspection-client-id is omitted.

camel.oauth.<profile>.client-secret

No

Fallback client secret used by introspection when introspection-client-secret is omitted. Treat this value as a secret.

camel.oauth.<profile>.expected-issuer

Yes, unless base-uri is set or allow-missing-issuer=true

Expected iss claim. When base-uri is set, the base URI is used as the expected issuer.

camel.oauth.<profile>.expected-audience

Yes, unless allow-missing-audience=true

Comma-separated accepted aud claim values, for example my-api,my-api-v2. The token is accepted when its aud claim matches at least one configured value. Configure this so tokens minted for another service are not accepted.

camel.oauth.<profile>.expected-token-type

No

Expected JWT typ header, for example at+jwt. When set, JWT tokens without this type are rejected.

camel.oauth.<profile>.clock-skew-seconds

No

Clock skew leeway for temporal claims. Default: 0.

camel.oauth.<profile>.jwks-cache-ttl-seconds

No

JWKS cache TTL. Default: 600.

camel.oauth.<profile>.oidc-discovery-cache-ttl-seconds

No

OIDC discovery metadata cache TTL. Default: 600.

camel.oauth.<profile>.connect-timeout-seconds

No

Connect timeout for OIDC discovery, JWKS fetch, and introspection calls. Default: 5.

camel.oauth.<profile>.read-timeout-seconds

No

Read timeout for OIDC discovery, JWKS fetch, and introspection calls. Default: 10.

camel.oauth.<profile>.require-expiration

No

Whether JWT tokens must include an exp claim. Default: true.

camel.oauth.<profile>.allowed-jws-algorithms

No

Comma-separated JWS algorithm allowlist, for example RS256,ES256. When omitted, compatible RSA and EC JWS algorithms supported by the verifier are accepted.

camel.oauth.<profile>.allow-missing-audience

No

Set to true to accept tokens without configuring accepted audiences. Default: false.

camel.oauth.<profile>.allow-missing-issuer

No

Set to true to accept tokens without configuring an expected issuer. Default: false.

camel.oauth.<profile>.allow-insecure-http

No

Set to true to allow plain HTTP OIDC discovery, JWKS, or introspection endpoints for local development. Default: false.

Choosing JWT Validation, OIDC Discovery, or Opaque Introspection

Camel chooses the validation path from the incoming token and the configured profile:

  • If only introspection-endpoint is configured, Camel introspects the token, regardless of token shape.

  • If only jwks-endpoint is 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 …​

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.21.0-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