Skip to main content

Secure ThunderID Using a Third-Party Identity Provider

By default, ThunderID only accepts tokens it issued itself. Your users might authenticate against an external authorization server: another ThunderID instance, an enterprise identity provider, or any standard OIDC provider. In that case, those users cannot call ThunderID's own APIs or sign in to the ThunderID Console unless ThunderID can validate their tokens.

The trusted issuer feature enables this. You configure ThunderID to trust a specific external authorization server, and ThunderID fetches that server's public signing keys to validate incoming tokens locally. ThunderID acts as the resource server: it accepts and validates tokens the external server issued, without issuing its own tokens for those users or duplicating the user directory.

You enable this by adding a server.security.trusted_issuer block to deployment.yaml on the resource-server instance.

When to Enable

Enable trusted issuer on a ThunderID instance when:

  • Users authenticate against an external authorization server and need to call ThunderID's own APIs or sign in to the ThunderID Console using those tokens.
  • You want ThunderID to act as a resource server for an external IdP without duplicating user directories or re-issuing tokens.
  • Incoming bearer tokens are signed by the external server and carry an iss claim that identifies it.

If a ThunderID instance only serves its own users and issues its own tokens, omit the server.security.trusted_issuer block.

Prerequisites

  • A running ThunderID instance that acts as the resource server.
  • An external authorization server (another ThunderID instance, an enterprise IdP, or any OIDC-compatible provider) that issues JWTs with an iss claim and exposes a JWKS endpoint.
  • The central server's issuer URL and JWKS endpoint URL.

Configure the Server

Add a server.security.trusted_issuer block to the tenant instance's deployment.yaml:

server:
security:
trusted_issuer:
issuer: "https://cloud.example.com"
jwks_url: "https://cloud.example.com/oauth2/jwks"
audience: "https://tenant.example.com"
required_claims:
- claim: "ouId"
value: "tenant-123"
FieldRequiredDescription
issuerYesThe issuer URL of the central authorization server. ThunderID matches this against the iss claim on every incoming token and rejects any token that does not match exactly, including differences in scheme, host, port, or trailing slash.
jwks_urlYesThe JWKS endpoint on the central server. ThunderID fetches the public signing keys from this URL and uses them to verify token signatures.
audienceYesThe aud value that tokens from the central server must carry. Set this to the resource-server ThunderID instance's own public URL. The client at the central server must include a matching resource parameter in the authorization request so the issued token targets this instance.
required_claimsNoA list of claims that every accepted token must contain, each paired with an expected value. Tokens that are missing a required claim or carry a different value are rejected. Use this to scope accepted tokens further, for example to a specific organization unit.

The JWKS cache time-to-live applies to signing keys fetched from this endpoint and is configured at server.security.jwks_cache_ttl, not inside the trusted_issuer block. See Security Configuration for details.

Configure the Console

The server.security.trusted_issuer block controls how ThunderID validates incoming API tokens. It does not affect how users sign in to the ThunderID Console. By default, the Console still authenticates users against the local ThunderID instance.

To delegate Console sign-in to the central authorization server, add a trusted_issuer entry to the Console's runtime configuration at apps/console/config.js:

window.__THUNDERID_RUNTIME_CONFIG__ = {
// ...existing client, server, brand entries...
trusted_issuer: {
hostname: "auth.example.com",
port: 443,
http_only: false,
public_url: "https://auth.example.com",
client_id: "FEDERATED_CONSOLE",
scopes: ['openid', 'profile', 'email', 'group', 'ou', 'system', 'system:user', 'system:group', 'system:ou:view', 'system:usertype:view'],
type: "generic",
},
};
FieldDescription
hostnameThe hostname of the external authorization server the Console redirects users to for sign-in.
portThe port on the external authorization server.
http_onlySet to true only for local or development authorization servers served over HTTP.
public_urlThe fully qualified URL of the external authorization server. The Console uses this to build authorization requests.
client_idThe OAuth client ID registered on the external authorization server for this ThunderID instance's Console.
scopesThe scopes the Console requests from the external authorization server. Include every scope that ThunderID's APIs require.
typeSet to "generic" when the central authorization server is a generic OIDC provider rather than another ThunderID instance. When set, the Console skips ThunderID-specific bootstrap calls (flow metadata, branding preferences) that would fail against a generic provider, and uses the provider's end_session_endpoint for sign-out. Defaults to "thunderid".

When this block is present, the Console delegates sign-in to the external authorization server. When it is absent, the Console authenticates against the local ThunderID instance.

note

The external authorization server must permit this ThunderID instance's origin in its CORS allowed-origins configuration. Otherwise, browser calls from the Console to the authorization server's /oauth2/token, /oauth2/jwks, and related endpoints are blocked.

How Validation Works

When a request arrives with a bearer token, ThunderID reads the token's iss claim to decide which verifier to use. For a token whose iss matches trusted_issuer.issuer, ThunderID performs the following checks in order:

  1. Parses the JWT and confirms its iss claim matches trusted_issuer.issuer.
  2. Fetches signing keys from trusted_issuer.jwks_url and verifies the signature.
  3. Checks the exp claim and rejects expired tokens.
  4. Checks the nbf (not before) claim if present. If nbf is absent, the token is still accepted.
  5. Confirms the aud claim matches trusted_issuer.audience. The aud claim may be a single string or a JSON array of strings; ThunderID accepts the token if the configured audience appears in either form.
  6. Checks that every entry in required_claims is present with the expected value.

If any check fails, ThunderID rejects the token.

Claim Details

iss (Issuer): Must match trusted_issuer.issuer exactly, including scheme, host, and any path.

exp (Expiry): Required. Tokens that omit exp or carry a non-numeric value are rejected.

nbf (Not Before): Optional per RFC 7519 Section 4.1.5. Many commercial OIDC providers omit this claim. When nbf is present, ThunderID enforces it with the configured clock-skew leeway.

aud (Audience): May be a single string or an array of strings per RFC 7519 Section 4.1.3. Some providers issue an array aud when a token targets more than one audience, for example both the resource API and the provider's own /userinfo endpoint. ThunderID matches the configured trusted_issuer.audience value against both forms.

required_claims: Each entry matches by exact string equality. The claim must be present on the token and its value must equal the configured value.

Accepted signature algorithms: RS256, PS256, RS512, ES256, ES384, ES512, EdDSA. Tokens signed with any other algorithm are rejected.

Self-Issued Tokens

Configuring server.security.trusted_issuer does not stop ThunderID from accepting the tokens it issues itself. ThunderID selects the verifier based on the token's iss claim:

  • iss matches trusted_issuer.issuer: the token is verified against the central server's JWKS endpoint.
  • iss matches this ThunderID instance's own issuer (configured at jwt.issuer, defaulting to the instance's public URL): the token is verified with the local signing key.
  • Any other iss value, or a token whose payload cannot be parsed, is rejected.

This means service accounts, the system bootstrap token, and Console sessions that are not delegated all continue to work unchanged. Verification never falls back between verifiers: a token routed to the JWKS verifier that fails signature validation is rejected immediately. ThunderID does not retry it against the local signing key.

JWKS responses are cached in-process for server.security.jwks_cache_ttl seconds (default: 300). Plan key rotations on the central server with at least that much overlap so tenant instances do not reject tokens signed with a new key before the cache refreshes.

Helm Configuration

When installing ThunderID with the Helm chart at install/helm, the same settings are available under the configuration key using camelCase field names.

configuration:
server:
security:
trustedIssuer:
issuer: "https://auth.example.com"
jwksUrl: "https://auth.example.com/oauth2/jwks"
audience: "https://thunder.example.com"
requiredClaims:
- claim: "ouId"
value: "tenant-123"

To configure the Console without editing apps/console/config.js directly, set configuration.consoleClient.trustedIssuer in the chart values. The fields mirror those in Configure the Console but use camelCase:

configuration:
consoleClient:
trustedIssuer:
hostname: "auth.example.com"
port: 443
httpOnly: false
publicUrl: "https://auth.example.com"
clientId: "FEDERATED_CONSOLE"
scopes: "['openid', 'profile', 'email', 'group', 'ou', 'system', 'system:user', 'system:group', 'system:ou:view', 'system:usertype:view']"
type: "generic"

Troubleshooting

SymptomLikely CauseFix
Console sign-in redirect hangs or token exchange fails with a CORS errorThunderID's origin is not in the external authorization server's CORS allowed-origins listAdd the ThunderID instance's origin to the authorization server's CORS configuration.
Tokens rejected with "issuer mismatch"trusted_issuer.issuer does not exactly match the iss claim on incoming tokensSet trusted_issuer.issuer to match exactly, including scheme, host, port, and trailing slash.
Tokens rejected with "audience mismatch"The authorization server is not receiving a resource parameter and falls back to a default resource server whose identifier does not match ThunderID's expected audienceConfigure the client to send resource=<server-url> in the authorization request, or set defaultResourceServer to the expected resource server.
Tokens rejected with "required claim missing"A claim listed under required_claims is absent from the token or its value does not matchRemove the entry from required_claims, or configure the authorization server to include the claim with the expected value.
JWKS fetch failstrusted_issuer.jwks_url is unreachable, or the authorization server uses a certificate that ThunderID does not trustVerify network connectivity to the URL, and install the authorization server's CA certificate on the ThunderID instance if needed.
Console does not redirect to the external authorization serverconsoleClient.trustedIssuer is not set, or publicUrl/hostname still point at the local ThunderID instanceVerify the consoleClient.trustedIssuer block and re-render the Helm chart.

Explore with AI

ThunderID LogoThunderID Logo

Product

DocsAPIsSDKs
© Copyright Linux Foundation Europe.For web site terms of use, trademark policy and other project policies please see https://linuxfoundation.eu/en/policies.