This page is the task-oriented integration guide. For the conceptual background and how federation compares to the built-in social providers (Google, Apple, …), see Social logins → Custom OIDC providers. The mechanics below reuse the same enclave verification path described there.
When to use federation
Use OIDC Federation
You operate your own IdP (or an Auth0/Cognito/Okta tenant) and want it to be the identity source of truth. You want to onboard existing users into embedded wallets without asking them to re-authenticate through a third-party social button.
Use built-in social logins
You just want “Sign in with Google/Apple/…”. Those issuers are trusted by default — no registration needed. See Social logins.
How it fits together
Trust is established once, at the organization level; identity binding and login happen per user. The enclave never trusts the token at face value: on every login or binding it independently fetches the issuer’s discovery document and JWKS over HTTPS (through the TLS Fetcher) and re-verifies the signature,iss, aud, exp, and nonce.
Prerequisites for your IdP
Your Identity Provider must expose the standard OIDC discovery surface:- OIDC Discovery at
https://<your-issuer>/.well-known/openid-configuration, returning at minimumissuerandjwks_uri. - JWKS served live over HTTPS at the discovered
jwks_uri. 0xkey enforces full public-CA TLS validation — there is no self-signed / plain-HTTP / private-CA bypass, even in dev. - Same-host
jwks_uri. For self-hosted (non-built-in) issuers, thejwks_urihost must match the issuer host. A mismatch is rejected (see JWKS host binding). - RS256 or ES256 signatures. Other algorithms are rejected.
- Stable
sub. Thesubclaim must be a stable, non-reassignable per-user identifier —(iss, sub)is 0xkey’s identity primary key. If your IdP allows account merging orsubreassignment, review it carefully (see the Auth0 caveat). - Key rotation grace period. When you rotate signing keys, publish the new key and keep the old key in the JWKS until in-flight tokens expire. 0xkey simply trusts whatever the JWKS currently serves.
Integration guide
1
Register your issuer (one-time, root-quorum)
Registering an issuer establishes trust for the whole parent organization. It is a root-quorum-only operation.From the Dashboard: go to Wallet Kit → OIDC Providers → Add provider, fill in a name, the issuer URL, and at least one audience. The UI wraps the same activity behind a passkey-gated flow.Or via the API with
CREATE_OIDC_PROVIDER:cURL — CreateOidcProvider
issuermust exactly match theissclaim of your IdP’s tokens, and is immutable — delete and re-create if it ever changes.audiencesis a required, non-empty allowlist ofaudvalues (your OAuth2 client IDs). There is no wildcard — see fail-closed audiences.
2
Mint an id_token bound to the session key
Before starting your IdP login flow, the frontend generates an API keypair (this becomes the session key). When it authenticates the user against your IdP, it sets the OIDC
nonce to sha256(publicKey) so the resulting id_token is cryptographically bound to that key.If your IdP cannot customize nonce, 0xkey also accepts a tknonce claim — only one of the two needs to equal sha256(publicKey). Registration-only flows (CREATE_OIDC_PROVIDER, CREATE_SUB_ORGANIZATION) don’t carry a publicKey, so the nonce binding is not enforced there.3
Onboard or log the user in
With the
id_token and the API publicKey, your backend:- Looks up the sub-organization(s) associated with that identity.
- If none exists, creates one with
CREATE_SUB_ORGANIZATION, seeding the user’s OAuth provider from the token. - Calls
oauth_loginwith theid_token+publicKeyto obtain a session JWT.
oauth_login doesn’t care whether the issuer is Google or your own IdP.4
Bind additional identities to an existing user (optional)
To add your IdP as a login method for a user who already exists (e.g. they signed up with a passkey), submit
CREATE_OAUTH_PROVIDERS with the raw id_token:cURL — CreateOauthProviders
Managing the registry
All three write operations are root-quorum-only.
Security model
- Fail-closed audiences. An empty audience list is rejected outright; there is no “trust this issuer for any audience” option.
- JWKS host binding. For federated (non-built-in) issuers, the JWKS fetch host must match the issuer host — this blocks a tampered discovery document from redirecting key-fetching to an attacker-controlled host.
- Live verification. Signature,
iss,aud,exp,nbf(with 60s skew), andnonceare checked inside the enclave against the issuer’s live JWKS on every login/binding — nothing is cached as “already trusted”.
Troubleshooting
The verification errors below surface from the enclave duringoauth_login, CREATE_SUB_ORGANIZATION, or CREATE_OAUTH_PROVIDERS. They are all fail-closed — a rejected token never results in a session or a bound identity.
OIDC issuer ’…’ is not a registered/enabled trusted IdP for this organization
Cause: the token’siss doesn’t match any enabled provider registered for the organization.
Fix:
- Confirm you ran
CREATE_OIDC_PROVIDERfor this issuer against the correct parent organization, and that the provider isenabled. - The registered
issuermust be byte-for-byte equal to the token’siss(watch for a trailing slash,httpvshttps, or a tenant path). The issuer is immutable — if it’s wrong, delete and re-create. - Verify the registry with
GET_OIDC_PROVIDERS.
JWT ‘iss’ mismatch: expected ’…’, got ’…’
Cause: the token’s issuer differs from the provider’s configured issuer. Fix: align your IdP’siss claim with the exact string you registered. These must match character-for-character.
JWT ‘aud’ mismatch: expected one of […]
Cause: the token’saud isn’t in the registered audience allowlist.
Fix: add the OAuth2 client ID your app uses to the provider’s audiences via UPDATE_OIDC_PROVIDER. If you use different client IDs per platform (web/iOS/Android), register each one you intend to accept — or standardize on a single client ID (see the aud note).
JWT ‘nonce’ does not match sha256(public_key)
Cause: the token’snonce (or tknonce) isn’t the SHA-256 of the API publicKey used in oauth_login.
Fix: set the OIDC nonce to sha256(publicKey) when you start the IdP login flow, using the same keypair you later pass to oauth_login. This binding is enforced only during authentication, not during registration-only flows.
JWT has expired
Cause: theid_token’s exp is in the past by the time the enclave verifies it.
Fix: OIDC id_tokens are short-lived (often ~1 hour). Mint a fresh token immediately before the 0xkey call rather than reusing a stale one.
JWKS host does not match issuer host
Full message:OIDC JWKS fetch URL host does not match issuer host: issuer='…', jwks_url='…'.
Cause: for a federated issuer, the jwks_uri in your discovery document points at a different host than the issuer.
Fix: serve your JWKS on the same host as your issuer. (Built-in social providers like Google are exempt because they legitimately publish JWKS on a separate host; this check applies only to your own registered issuers.)
Unsupported JWT algorithm / JWK not found for kid
Cause: the token is signed with an unsupported algorithm (onlyRS256 and ES256 are accepted), or its kid isn’t present in the currently-served JWKS.
Fix: sign tokens with RS256 or ES256, and keep every currently-valid signing key published in your JWKS. During rotation, retain the previous key until all tokens signed with it have expired.
invalid OIDC token
Cause: malformed JWT (not three.-separated parts) or missing a required claim (aud, sub, iss).
Fix: see invalid OIDC token in the error reference.
missing field oidcToken
Cause: a CREATE_OAUTH_PROVIDERS request whose oauthProviders[] entries don’t carry a top-level oidcToken string.
Fix: put oidcToken as a flat field on each provider object (as in the binding example above), not nested under tokenOrClaims. The enclave expects the raw id_token string per provider entry.
Related
Social logins
Built-in providers, OAuth 2.0-only wrapper flow, and the full OIDC verification model.
Create OIDC provider
API reference for registering an issuer.
Create OAuth providers
API reference for binding an identity to a user.
Errors
Full error-code reference and troubleshooting hub.