# 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](https://www.keycloak.org)

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:

```xml
<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](https://openid.net/specs/openid-connect-core-1_0.md) 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](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](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](https://datatracker.ietf.org/doc/html/rfc6749#section-4.4) 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](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`. |

When token caching is enabled, profiles with the same token endpoint, client ID, client secret and requested scope can reuse a token across resolver instances. Different credentials or scopes use separate cache entries. Null, empty and whitespace-only scopes all omit the scope parameter and share the same cache identity; other scope values are matched exactly. Setting `cache-tokens=false` requests a fresh token on each resolution.

#### Example: Multiple Identity Providers

```properties
# 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:

```yaml
- 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](../platform-http-component.md), [Servlet](../servlet-component.md), [Jetty](../jetty-component.md), [Netty HTTP](../netty-http-component.md), and [Undertow HTTP/WebSocket](../undertow-component.md) 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 |
| --- | --- | --- |
| `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

```properties
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.

```properties
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

```properties
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

```properties
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:

```properties
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

_Java-only: programmatic token validation using OAuthHelper API_

```java
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:

_Java-only: default profile token validation_

```java
OAuthTokenValidationResult result = OAuthHelper.validateOAuthToken(camelContext, bearerToken);
```

For advanced validation use cases, resolve `OAuthTokenValidationFactory` directly and pass an explicit `OAuthTokenValidationConfig`.

_Java-only: advanced validation with explicit config and factory_

```java
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

_Java-only: resolving an OAuth token using OAuthHelper_

```java
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`:

_Java-only: advanced token resolution with explicit OAuthClientConfig_

```java
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.

```shell
# 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](https://github.com/strimzi/strimzi-kafka-oauth) directly, for example [like this](https://github.com/tdiesler/camel-cloud-examples/blob/main/camel-main/kafka-oauth/kafka-oauth-files/kafka-oauth-route.yaml) …​

## 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](https://docs.docker.com/desktop/features/kubernetes/))
    
-   Remote [K3S](https://k3s.io/) Cluster
    
-   Red Hat [OpenShift](https://www.redhat.com/en/technologies/cloud-computing/openshift)
    

As part of this project we provide a set of [Helm](https://helm.sh/) charts that install the required infrastructure components for the respective cluster environment. For details, have a look at the [dedicated readme](https://github.com/apache/camel/tree/main/components/camel-oauth/helm/README.md).

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](https://github.com/tdiesler/camel-cloud-examples). You’ll find [camel-jbang kubernetes](../../../manual/camel-jbang.md) examples for every OAuth flow, for every runtime, on every supported cluster.

For example in the following _makefile_:

```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
```