OIDC flow
If you have an existing application that already speaks OpenID Connect, or you want to use an off-the-shelf OIDC library, you can integrate with Sudomimus as a standard OIDC relying party (RP). The Sudomimus OIDC provider lives at oidc.sudomimus.com and supports the authorization code flow with PKCE, the canonical modern OIDC integration shape.
Use this guide when:
- Your framework or platform has a first-class OIDC integration (Next-Auth, Spring Security, Keycloak adapter, etc.) and you want to slot Sudomimus in as the IdP.
- You’re integrating with a partner system that already expects an OIDC provider.
- You prefer the OIDC mental model (clients, scopes, ID tokens) to the Connect protocol.
If you’re starting fresh and just want the smallest custom integration, the Connect protocol is usually shorter.
Discovery
Section titled “Discovery”Sudomimus publishes a standard OIDC discovery document. Point your library at the issuer URL https://oidc.sudomimus.com and it will fetch the rest from there:
curl https://oidc.sudomimus.com/.well-known/openid-configurationThe document advertises:
response_types_supported:["code"](authorization code flow only).grant_types_supported:["authorization_code", "refresh_token"].scopes_supported:["openid", "email", "profile", "offline_access"].id_token_signing_alg_values_supported:["RS256"].code_challenge_methods_supported:["S256"]— PKCE is required, plain not supported.token_endpoint_auth_methods_supported:["private_key_jwt", "client_secret_basic", "client_secret_post", "none"].
Register your application
Section titled “Register your application”In with.sudomimus.com, on the application you want to expose via OIDC:
-
Add a Layer 3 OIDC return rule:
{"returnMethod": "OIDC","payload": {"redirectUris": ["https://app.example.com/oidc/callback"],"postLogoutRedirectUris": ["https://app.example.com/"],"allowedScopes": ["openid", "email", "profile", "offline_access"],"tokenEndpointAuthMethod": "private_key_jwt"}} -
Add the Layer 1 and Layer 2 rules you would for any other application — at least one authentication method (e.g.
PASSKEY_USERNAMELESSorPASSKEY_REASONED) and at least one realize rule (e.g.EMAILwith the addresses or domain pattern you accept). The OIDC flow runs through the same authentication challenge as the rest of the platform. -
Choose a client authentication method:
private_key_jwt(recommended for confidential clients) — your RP holds a private key and signs a JWT assertion at/token. The signing key is your application’s client-auth key (the same key used to sign/establishrequests on the native protocol).client_secret_basic(confidential clients) — your RP presents its shared secret in the HTTPAuthorization: Basicheader at/token.client_secret_post(confidential clients) — your RP sends its shared secret in the/tokenform body (client_id+client_secretparameters).none— only for public clients (SPAs, mobile apps without a backend). PKCE is required.
The application’s applicationAnchor is your client_id.
The OIDC flow
Section titled “The OIDC flow”1. Authorization request
Section titled “1. Authorization request”Redirect the user’s browser to /authorize with the standard OIDC parameters:
https://oidc.sudomimus.com/authorize ?client_id=my-app &redirect_uri=https%3A%2F%2Fapp.example.com%2Foidc%2Fcallback &response_type=code &scope=openid%20email%20profile &state=<csrf-token> &nonce=<random-nonce> &code_challenge=<S256-of-verifier> &code_challenge_method=S256Required: client_id, redirect_uri, response_type=code, scope (must include openid), code_challenge, code_challenge_method=S256.
Optional but recommended: state, nonce.
Sudomimus redirects the user to via.sudomimus.com where they authenticate via the methods allowed by your Layer 1 rules. After a successful authentication and realize, the browser returns to your redirect_uri with a code and the original state.
2. Token exchange
Section titled “2. Token exchange”POST the authorization code to /token. The body is application/x-www-form-urlencoded, per OIDC:
curl -X POST https://oidc.sudomimus.com/token \ -H "Content-Type: application/x-www-form-urlencoded" \ --data-urlencode "grant_type=authorization_code" \ --data-urlencode "code=$AUTH_CODE" \ --data-urlencode "redirect_uri=https://app.example.com/oidc/callback" \ --data-urlencode "code_verifier=$PKCE_VERIFIER" \ --data-urlencode "client_id=my-app" \ --data-urlencode "client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer" \ --data-urlencode "client_assertion=$CLIENT_ASSERTION_JWT"The client_assertion is a JWT you sign with your application’s client-auth private key. Required claims: iss = client_id, sub = client_id, aud = the exact token endpoint URL (https://oidc.sudomimus.com/token in production), fresh jti, iat, exp (within 300s of iat). RS256.
curl -X POST https://oidc.sudomimus.com/token \ -H "Content-Type: application/x-www-form-urlencoded" \ --data-urlencode "grant_type=authorization_code" \ --data-urlencode "code=$AUTH_CODE" \ --data-urlencode "redirect_uri=https://app.example.com/oidc/callback" \ --data-urlencode "code_verifier=$PKCE_VERIFIER" \ --data-urlencode "client_id=my-app"No client assertion is sent. PKCE (code_verifier matching the code_challenge from the authorization request) is the only client authentication.
Successful response (JSON):
{ "access_token": "<JWT>", "token_type": "Bearer", "expires_in": 10800, "id_token": "<JWT>", "scope": "openid email profile", "refresh_token": "<JWT — only if 'offline_access' was requested>"}id_token— signed by Sudomimus’s platform-wide OIDC key; verify againsthttps://oidc.sudomimus.com/.well-known/jwks.json. Standard OIDC claims (iss,sub,aud,exp,iat, optionalnonce,email,name).access_token— signed by the application’s token-signing key, same shape as a native Connect access token.refresh_token— included only if you requested theoffline_accessscope.
3. Userinfo
Section titled “3. Userinfo”curl https://oidc.sudomimus.com/userinfo \ -H "Authorization: Bearer $ACCESS_TOKEN"Returns the claims permitted by the granted scopes:
{ "sub": "<sector subject>", "email": "<only if 'email' scope was granted>", "name": "<only if 'profile' scope was granted>"}sub is the sector subject — the per-sector, application-visible identifier (the same value as the id_token sub), not the raw account UUID. /userinfo accepts both GET and POST.
4. Refresh
Section titled “4. Refresh”If you requested offline_access and got a refresh token, exchange it at /token:
curl -X POST https://oidc.sudomimus.com/token \ -H "Content-Type: application/x-www-form-urlencoded" \ --data-urlencode "grant_type=refresh_token" \ --data-urlencode "refresh_token=$REFRESH_TOKEN" \ --data-urlencode "client_id=my-app" # plus client_assertion for confidential clientsYou can optionally pass scope to request a narrowed subset of the originally-granted scopes. Per OIDC §12.1, the ID token from a refresh does not include a new nonce.
5. End session
Section titled “5. End session”https://oidc.sudomimus.com/end-session ?id_token_hint=<id_token> &client_id=my-app &post_logout_redirect_uri=https%3A%2F%2Fapp.example.com%2F &state=<optional>/end-session signals the end of an OIDC session and redirects to the post_logout_redirect_uri (which must match one of the registered URIs exactly). It does not revoke refresh tokens on its own — to invalidate the underlying session, call Connect’s /logout (single session) or /revoke-all (every session for an account). See Managing sessions.
Using an OIDC library
Section titled “Using an OIDC library”Most modern OIDC libraries (Node’s openid-client, Python’s authlib, Java’s nimbus-jose-jwt, etc.) discover everything from the issuer URL and handle PKCE, JWKS, and token verification automatically. A minimal Node example:
import { Issuer } from "openid-client";
const issuer = await Issuer.discover("https://oidc.sudomimus.com");const client = new issuer.Client({ client_id: "my-app", redirect_uris: ["https://app.example.com/oidc/callback"], response_types: ["code"], token_endpoint_auth_method: "none", // or "private_key_jwt"});
// At login:const codeVerifier = generators.codeVerifier();const codeChallenge = generators.codeChallenge(codeVerifier);const authUrl = client.authorizationUrl({ scope: "openid email profile", code_challenge: codeChallenge, code_challenge_method: "S256", state, nonce,});
// At callback:const params = client.callbackParams(req);const tokenSet = await client.callback(redirectUri, params, { code_verifier: codeVerifier, state, nonce });const userinfo = await client.userinfo(tokenSet.access_token);Token verification reminder
Section titled “Token verification reminder”OIDC ID tokens are verified against the JWKS at oidc.sudomimus.com/.well-known/jwks.json — that’s the standard OIDC mechanism, and your library does it for you.
The access_token returned by /token is not a JWKS-verifiable token in the platform-wide sense; it’s a per-application access token of the same shape as the Connect flow, verified against your application’s applicationPublicKey from POST /info. If your library tries to verify the access token, point it at /info for the key, or simply treat the access token as opaque and use /userinfo for claims. See Tokens and verification.