Securing Custom REST API Endpoints

How to secure custom REST API endpoints in IntegrationManager using Basic Auth (partition credentials) or OAuth2 (JWT / opaque tokens).


Overview

When an external system calls IM's REST API (the Camel routes exposed via integration.rest.*), IM must verify who the caller is. Two authentication modes are supported:

Mode

integration.rest.auth-mode

How it works

Basic Auth (default)

basic

Caller sends username:password. IM validates credentials against the Pricefx partition API.

OAuth2

oauth2

Caller sends a Bearer token issued by their own Identity Provider. IM validates the token locally (JWT) or via introspection (opaque).

Both modes enforce the same authorization rules defined by integration.rest.endpoints.secured, integration.rest.endpoints.roles, and integration.rest.doc.* properties.


Where to Configure

All authentication properties go into your instance's config/application.properties file. REST endpoints are defined as Camel routes in the routes/ directory.

/
├── config/
│   └── application.properties    # ← auth properties go here
├── routes/
│   └── my-rest-api.xml           # ← your REST endpoint routes
├── connections/
│   └── pricefx.json              # ← Pricefx connection (used by Basic Auth)
└── ...

For details on the IM project structure, see Project Structure.


Quick Start

Basic Auth (default):

integration.rest.enabled=true
# auth-mode defaults to "basic"
integration.rest.auth-mode=basic
integration.rest.endpoints.path=/rest
integration.rest.endpoints.secured=true
# endpoints.roles defaults to ADMIN
integration.rest.endpoints.roles=ADMIN

OAuth2 with JWT:

integration.rest.enabled=true
integration.rest.endpoints.path=/rest
integration.rest.endpoints.secured=true
# clear the default (ADMIN) — accept any authenticated user
integration.rest.endpoints.roles=
integration.rest.auth-mode=oauth2
# token-type defaults to "jwt"
integration.rest.oauth2.token-type=jwt
integration.rest.oauth2.jwt.issuer-uri=https://{your-idp-domain}/...

That's it — any valid token from your IdP will be accepted. To restrict access to specific roles/scopes, see Authorization Rules below.

Note: The default value for endpoints.roles is ADMIN (used by Basic Auth). When switching to OAuth2, you must either clear it (accept any authenticated user) or set it to a role/scope from your IdP along with roles-claim.


Authentication Modes

1. Basic Auth (Partition Credentials)

This is the default mode. Your external system sends Pricefx partition credentials with every request. IM validates the credentials against the Pricefx partition API using the connection defined in connections/pricefx.json (or via integration.pfx.* properties).

What your external system sends:

POST https://{im-host}/rest/myEndpoint
Authorization: Basic <base64-encoded username:password>
Content-Type: application/json

The username can be in two formats:

  • username (e.g. admin) — authenticates against the default partition from the pricefx connection

  • partition/username (e.g. company/admin) — authenticates against the specified partition (overrides the connection)

Minimal configuration:

integration.rest.enabled=true
integration.rest.endpoints.path=/rest
integration.rest.endpoints.secured=true
# endpoints.roles defaults to ADMIN — caller must have ADMIN role on the partition

With custom role:

integration.rest.enabled=true
integration.rest.endpoints.path=/rest
integration.rest.endpoints.secured=true
integration.rest.endpoints.roles=DATAINTEGRATION

2. OAuth2 (JWT)

Your external system obtains a token from your Identity Provider (Azure AD, Okta, Keycloak, etc.) and sends it to IM as a Bearer token. IM validates the token locally using your IdP's public keys — no per-request calls to the IdP.

What your external system sends:

POST https://{im-host}/rest/myEndpoint
Authorization: Bearer eyJhbGciOiJSUzI1NiJ9...
Content-Type: application/json

What IM needs from you:

  • The IdP's issuer URI or JWKS endpoint URL

Minimal configuration

integration.rest.enabled=true
integration.rest.endpoints.path=/rest
integration.rest.endpoints.secured=true
integration.rest.endpoints.roles=
integration.rest.auth-mode=oauth2
integration.rest.oauth2.token-type=jwt
integration.rest.oauth2.jwt.issuer-uri=https://{your-idp-domain}/...

Or with a direct JWKS endpoint (preferred — avoids OIDC discovery):

integration.rest.enabled=true
integration.rest.endpoints.path=/rest
integration.rest.endpoints.secured=true
integration.rest.endpoints.roles=
integration.rest.auth-mode=oauth2
integration.rest.oauth2.token-type=jwt
integration.rest.oauth2.jwt.jwk-set-uri=https://{your-idp-domain}/.well-known/jwks.json

Property details

Property

Required

Description

integration.rest.auth-mode

Yes

Set to oauth2

integration.rest.oauth2.jwt.issuer-uri

Yes (or jwk-set-uri)

Your IdP's issuer URL. IM discovers the signing keys automatically.

integration.rest.oauth2.jwt.jwk-set-uri

Yes (or issuer-uri)

Direct URL to your IdP's JWKS (signing keys) endpoint. Preferred over issuer-uri.

Note: If both jwk-set-uri and issuer-uri are set, jwk-set-uri takes precedence for key retrieval. The issuer-uri is still used for issuer validation.

Examples by Identity Provider

Azure AD:

integration.rest.auth-mode=oauth2
integration.rest.oauth2.token-type=jwt
integration.rest.oauth2.jwt.jwk-set-uri=https://login.microsoftonline.com/{tenant-id}/discovery/v2.0/keys
integration.rest.oauth2.jwt.issuer-uri=https://login.microsoftonline.com/{tenant-id}/v2.0

Okta:

integration.rest.auth-mode=oauth2
integration.rest.oauth2.token-type=jwt
integration.rest.oauth2.jwt.issuer-uri=https://{your-domain}.okta.com/oauth2/default

Keycloak:

integration.rest.auth-mode=oauth2
integration.rest.oauth2.token-type=jwt
integration.rest.oauth2.jwt.jwk-set-uri=https://{host}/realms/{realm}/protocol/openid-connect/certs
integration.rest.oauth2.jwt.issuer-uri=https://{host}/realms/{realm}

3. OAuth2 (Opaque Token / Introspection)

Instead of validating the token locally, IM forwards it to your IdP's introspection endpoint on every request. The IdP confirms whether the token is valid and returns its scopes.

This mode is useful for testing or when your IdP doesn't expose a JWKS endpoint.

Important — Token format restriction: The Bearer token must conform to the RFC 6750 b64token format. Only the following characters are allowed: A-Z a-z 0-9 - . _ ~ + / and = for padding. Tokens containing other characters (e.g. !, #, @) will be rejected with a 401 Unauthorized error and the message Bearer token is malformed. This affects providers like Salesforce, whose opaque tokens use the format orgId!sessionId. Salesforce tokens are not compatible with this mode.

Minimal configuration:

integration.rest.enabled=true
integration.rest.endpoints.path=/rest
integration.rest.endpoints.secured=true
integration.rest.endpoints.roles=
integration.rest.auth-mode=oauth2
integration.rest.oauth2.token-type=opaque
integration.rest.oauth2.opaque.introspection-uri=https://{your-idp}/oauth2/introspect
integration.rest.oauth2.opaque.client-id=im-client
integration.rest.oauth2.opaque.client-secret={{oauth2.client.secret}}

Property

Required

Description

integration.rest.oauth2.token-type

Yes

Set to opaque

integration.rest.oauth2.opaque.introspection-uri

Yes

Your IdP's token introspection endpoint URL

integration.rest.oauth2.opaque.client-id

Yes

Client ID for authenticating to the introspection endpoint

integration.rest.oauth2.opaque.client-secret

Yes

Client secret. Use an encrypted property placeholder.


Authorization Rules

By default, REST endpoints require the ADMIN role (configured via integration.rest.endpoints.roles=ADMIN). To change this, configure audience validation and/or role/scope requirements below.

Audience validation (JWT only)

Ensures the token was issued for your IM instance, not for a different application:

integration.rest.oauth2.jwt.audience=api://my-im-app-id

Restricting by role/scope

For OAuth2 JWT — set the JWT claim containing roles and the required value:

integration.rest.oauth2.jwt.roles-claim=scp
integration.rest.endpoints.roles=im.call

The roles-claim property tells IM where to find roles in the JWT token. It depends on your IdP:

IdP

roles-claim value

Example role

Azure AD

roles

DATAINTEGRATION

Okta (scopes)

scp

im.call

Okta (groups)

groups

IM_Users

Keycloak

realm_access.roles

DATAINTEGRATION

For OAuth2 opaque tokens — roles come from the scope field in the introspection response automatically. No roles-claim needed:

integration.rest.endpoints.roles=im.call

For Basic Auth — roles come from the Pricefx user's role assignments automatically:

integration.rest.endpoints.roles=DATAINTEGRATION

Authorization logic

# REST endpoints — require authentication and specific role
integration.rest.endpoints.secured=true
integration.rest.endpoints.roles=DATAINTEGRATION

# API documentation — public access
integration.rest.doc.secured=false

secured

roles

Who can access

false

(ignored)

Everyone — no authentication required

true

(empty)

Any authenticated user — no specific role required

true

ROLE1,ROLE2

Only users with at least one of the listed roles

Role matching is case-insensitive. endpoints.roles=im.call matches a token scope of im.call, IM.CALL, or Im.Call.


Choosing Between OAuth2 and Basic Auth


Basic Auth

OAuth2 (JWT)

Setup

Works out of the box

Requires IdP configuration

Security

Password in every request (over HTTPS)

Signed token — no password in transit

Performance

First call requires Pricefx API calls (then cached)

Token validated locally — no external calls

Token expiry

No automatic expiry

Tokens expire automatically

Access management

Via Pricefx user accounts

Via your own Identity Provider

Recommendation: Use OAuth2 JWT when you have an Identity Provider. Use Basic Auth for simple setups.


Troubleshooting

Problem

Possible cause

Solution

IM fails to start with IllegalStateException mentioning issuer-uri or jwk-set-uri

OAuth2 JWT mode enabled but no JWKS/issuer URI configured

Set integration.rest.oauth2.jwt.issuer-uri or jwk-set-uri

IM fails to start with IllegalStateException mentioning roles-claim

endpoints.roles is set (default ADMIN) but roles-claim is empty

Either clear endpoints.roles= or set roles-claim to your IdP's claim name

IM fails to start with IllegalStateException mentioning introspection-uri

Opaque mode enabled but introspection properties missing

Set introspection-uri, client-id, and client-secret

401 UnauthorizedBearer token is malformed

Token contains characters outside the RFC 6750 b64token format (e.g. !, #, @). Common with Salesforce tokens (orgId!sessionId).

Opaque tokens must only contain A-Z a-z 0-9 - . _ ~ + / and =. Tokens from providers like Salesforce that use non-standard characters are not supported.

401 Unauthorized with OAuth2

Token is invalid, expired, or from wrong issuer

Check that issuer-uri matches your IdP; verify the token hasn't expired

401 Unauthorized with OAuth2 + audience

Token's aud claim doesn't match configured audience

Verify integration.rest.oauth2.jwt.audience matches the token's audience

401 Unauthorized with Basic Auth

Wrong username/password or partition unreachable

Check credentials; verify pricefx.json connection or integration.pfx.* properties

403 Forbidden with OAuth2

Token is valid but missing the required role/scope

Check roles-claim points to the correct JWT claim; verify the token contains the expected scope

403 Forbidden with Basic Auth

User authenticated but doesn't have the required role

Assign the required role to the user in the Pricefx partition

REST endpoints return 401 but should be public

endpoints.secured=true (default)

Set integration.rest.endpoints.secured=false

OAuth2 chain not active (Basic Auth prompt instead)

auth-mode not set to oauth2

Set integration.rest.auth-mode=oauth2

Tip: Enable logging.level.org.springframework.security=DEBUG in application.properties to see detailed authentication/authorization decisions in the logs.


Complete Property Reference

Property

Default

Description

integration.rest.auth-mode

basic

basic or oauth2

integration.rest.oauth2.token-type

jwt

jwt or opaque

integration.rest.oauth2.jwt.issuer-uri

(empty)

IdP issuer URI

integration.rest.oauth2.jwt.jwk-set-uri

(empty)

Direct JWKS endpoint URL

integration.rest.oauth2.jwt.audience

(empty)

Expected audience value

integration.rest.oauth2.jwt.roles-claim

(empty)

JWT claim with roles/scopes (required when endpoints.roles is set)

integration.rest.oauth2.opaque.introspection-uri

(empty)

Introspection endpoint URL

integration.rest.oauth2.opaque.client-id

(empty)

Introspection client ID

integration.rest.oauth2.opaque.client-secret

(empty)

Introspection client secret

integration.rest.endpoints.path

/custom

Base path for REST endpoints (e.g. /rest)

integration.rest.endpoints.secured

true

Require authentication for REST endpoints

integration.rest.endpoints.roles

ADMIN

Required roles (comma-separated). Clear for OAuth2 or set to your IdP's role/scope.

integration.rest.doc.secured

false

Require authentication for API docs

integration.rest.doc.roles

(empty)

Required roles for API docs