Add CodeB sign-on to your app.
This page walks you through everything you need to consume CodeB’s OpenID Connect identity provider from a third-party website — PKCE setup, the auth code exchange, JWT validation, refresh handling, and logout. Copy-paste samples in vanilla JavaScript and Node, plus a curl walkthrough for backend tinkering.
The whole IdP is standards-compliant OIDC Core 1.0. If you already use a library like oidc-client-ts, openid-client (Node), Authlib (Python), or .NET’s built-in OpenIdConnectAuthentication — point it at the discovery URL and configure your client ID. Most of the manual steps below collapse to a config block.
1. Prerequisites
- Your site must be served over HTTPS. The IdP refuses non-loopback
http://redirect URIs. - You need an HTTP client capable of
POSTwithapplication/x-www-form-urlencodedbodies, and the ability to verify RS256 JWT signatures (any OIDC/JOSE library handles both). - The host running CodeB must be reachable from the browser and from your server (the server-side token exchange is a back-channel call).
2. Register your application
CodeB requires every relying party to be pre-registered — this is the standard OIDC posture (RFC 6749 §3.1.2.1). Pre-registration locks down which redirect_uri values the IdP will honour, which is what prevents an attacker from intercepting your users’ auth codes.
The operator adds your client to App_Data/<tenant>/oidc/clients.json. The file is per-tenant and hot-reloaded on every change — no IIS recycle required. Example:
If you don’t operate the host, email info@codeb.io with your application name, the exact callback URL (HTTPS unless loopback), and whether you need test vs. production redirects. All CodeB clients are public PKCE-only — no client secret to manage.
The built-in codeb-admin client is always available and bound to https://<tenant>/oidc-callback.html — that’s what powers the admin UI. You can’t override or remove it from clients.json.
For experimentation against your own tenant, the built-in client codeb-admin is pre-registered with the redirect URI https://<your-host>/oidc-callback.html — that’s what the CodeB admin pages use. Don’t use it for production third-party integrations; request your own client ID.
3. Discover the endpoints
The IdP publishes everything you need at the standard well-known URL:
Response (abridged):
Most libraries fetch this once at startup and cache it. Re-fetch if you start seeing kid mismatches on token validation (operator rotated the key).
4. The sign-in flow
Standard Authorization Code with PKCE. Four moving parts: your front-end, your back-end, the user’s browser, and the CodeB IdP.
Generate a PKCE pair on the front-end
Random 32-byte code_verifier, base64url-encoded. code_challenge is the SHA-256 of the verifier, also base64url. Stash the verifier in sessionStorage for the callback to find.
Redirect the user to /authorize
With your client_id, your redirect_uri, requested scopes (always include openid), a random state token, the code_challenge, and code_challenge_method=S256.
CodeB prompts the user to sign in
The user lands on /login.html, types their CodeB username + password (hashed client-side to HA1; the plaintext password never leaves the browser). On success, CodeB redirects back to your redirect_uri with ?code=…&state=….
Validate the state, exchange the code
Front-end checks that state matches what it sent. Then either the front-end (public client) or the back-end (confidential client) POSTs the code + PKCE verifier to /token and receives access_token, id_token, refresh_token.
Verify the ID token, sign the user in
Validate the JWT signature against the IdP’s JWKS, check iss, aud, exp, nonce. The sub claim is the user’s ID; role is admin/user/siponly/guest.
5. Full code — vanilla JS (public SPA client)
For a single-page app that does the whole flow client-side. Two files: a launcher and a callback handler.
Launcher (the button that starts sign-in)
Callback handler (the page CodeB redirects back to)
Public clients must use PKCE. Without it, anyone who intercepts the redirect URL can exchange the code for tokens. We don’t accept the authorization_code grant without a code_verifier.
6. Full code — Node / Express (confidential client)
For a backend that does the token exchange server-side — the recommended pattern when you control your own server. Uses the openid-client library, which handles discovery, PKCE, token validation, and refresh.
7. curl walkthrough
Useful for poking at the IdP from a script or a Postman collection. Replace YOUR_CLIENT_ID, YOUR_REDIRECT_URI, and the PKCE values with your own.
Step 1 — open the authorize URL in a browser
Sign in. The browser lands on your callback URL with ?code=<long string>&state=abc123.
Step 2 — exchange the code for tokens
Response:
Step 3 — fetch the user profile
8. Validate the ID token
If you accept ID tokens server-side (for example, to identify the user without a back-channel call), you MUST verify them. Skipping validation lets anyone hand you a forged token.
Any OIDC/JOSE library does this for you. The checks you need:
- Signature — verify against the public key from
jwks_urimatching thekidin the JWT header. iss— must equal the discovery doc’s issuer (https://phone.codeb.io).aud— must equal yourclient_id.exp— must be in the future (allow a few seconds of clock skew).iat— sanity check: not far in the future.nonce— if you sent one in/authorize, it must echo back in the token.
Decoded ID token payload:
The role claim is the CodeB-specific role assignment (admin, user, siponly, or guest). The standard groups claim defaults to a single-element array containing the role, which is what most RPs expect. Admins can override per user via the Groups field in the user profile editor (register.html) — when set, those groups replace the role-as-group default, which is the recommended way to feed RP-specific membership (Nextcloud groups, app roles, departments) into the token.
9. Refresh + logout
Refresh the access token
Access tokens last 1 hour. Refresh tokens last 4 hours and are rotated on every use (per OAuth 2.1 best practice): each call to /token with grant_type=refresh_token returns a fresh refresh_token, and the old one is invalidated immediately. Store the new one, discard the old.
If you ever present an already-redeemed refresh token, the IdP returns invalid_grant. That’s the rotation security feature: it tells you the token has been used elsewhere (possible exfiltration). Force the user to re-sign-in.
Sign the user out
Clear your own session, then optionally redirect to the IdP’s end-session endpoint:
Because CodeB is cookie-free, the IdP-side logout is mostly a redirect — there’s no IdP session cookie to clear. The real logout work happens on your side: drop the tokens.
10. Token revocation (RFC 7009)
Need a user’s session to end immediately on the IdP side (admin action, password change, employee offboarding)? POST the refresh token to /oidc.ashx?action=revoke. The next time the RP tries to refresh, it gets invalid_grant and the user is forced to re-authenticate.
Returns 200 {} regardless of whether the token existed — that’s RFC 7009 §2.2 deliberately preventing valid-token enumeration. Confidential clients must include client_secret; public clients may revoke without auth.
Access tokens are stateless JWTs and have a 1-hour TTL, so RFC 7009 §2.2 makes server-side revocation optional — CodeB no-ops on access-token revocation (still returns 200). To force-end a session faster than that, revoke the refresh_token AND have the RP drop its in-memory access_token.
11. Authentication factor (amr / acr)
Every issued id_token and access_token carries two claims that describe how the user actually authenticated:
amr— array of RFC 8176 method codes. Common values CodeB emits:"pwd"(HA1 form login),"hwk"(hardware key, e.g. smart card),"mfa"(multi-factor),"swk"(software key),"otp"(one-time password).acr— a URI naming the authentication context class. CodeB usesurn:codeb:acr:pwd,urn:codeb:acr:hwk,urn:codeb:acr:hwk-mfa,urn:codeb:acr:mfa.
RPs that need step-up auth (e.g. require a smart-card login for a sensitive operation) can branch on acr:
The claims survive refresh-token rotation, so the factor stays accurate for the life of the session. They’re also carried through the cookieless SSO assertion, so when the user signs into a second RP within the 30-min window the new RP sees the same factor.
12. Key rotation runbook (operator)
The IdP signs every JWT with a per-tenant 2048-bit RSA key. To rotate without downtime, CodeB supports an overlap window: two keys can be active at once. Tokens signed before rotation keep verifying via the previous key for as long as you keep it on disk; new tokens get signed with the freshly-generated key.
- SSH / RDP into the IIS host.
- Move the current key aside:
cd D:\aloaha\phone\App_Data\phone.codeb.io\oidc move private-key.xml private-key-previous.xml
- The next request to
/oidc.ashxon this tenant detects the missingprivate-key.xmland generates a fresh one. The previous key stays loaded as the verify-only fallback. - JWKS now publishes both public keys (the new one first):
curl https://phone.codeb.io/.well-known/jwks.json | jq .keys
RPs that fetch JWKS regularly automatically pick up both. Tokens signed before the rotation continue to verify; new tokens use the new
kid. - Wait for the overlap to drain. Once every refresh token issued under the old key has been rotated (max 4 h with the default TTL), it’s safe to delete the previous key:
del D:\aloaha\phone\App_Data\phone.codeb.io\oidc\private-key-previous.xml
JWKS drops back to a single key on the next request; any straggler with a token signed by the old key will fail verification and have to re-authenticate.
The IdP mtime-watches both files so the rotation is hot — no IIS recycle, no dropped WebSocket sessions, no broken in-progress sign-ins. The kid in each JWT header pins it to the right key at verify time.
13. Troubleshooting
| Symptom | Likely cause |
|---|---|
invalid_client at /authorize |
Your client_id isn’t registered for this tenant, or your redirect_uri doesn’t match the registered value byte-for-byte (case, trailing slash, query string). |
invalid_request redirect |
Missing code_challenge or code_challenge_method != S256. PKCE is mandatory. |
invalid_grant: PKCE verifier invalid |
The code_verifier you sent doesn’t SHA-256 to the code_challenge you sent earlier. Usually a typo or accidentally sending the challenge instead of the verifier. |
invalid_grant: code invalid or expired |
Auth codes are single-use and live for 60 seconds. Don’t cache them; exchange immediately on callback. |
invalid_grant: refresh_token invalid |
Refresh tokens are single-use (rotation). You presented one that was already redeemed — either by you on a previous call, or by an attacker if it leaked. Force re-login. |
401 invalid_token on /userinfo |
Access token expired (1 hour), or the wrong tenant’s key signed it. Refresh and retry. |
429 rate_limited on /login |
Per-IP rate limit (10 failed attempts per minute). Honour the Retry-After header. |
| State mismatch on callback | Your front-end lost the state it stashed (e.g. user opened the auth URL in a different tab). Restart the flow. |
14. Quick reference
| What | Value |
|---|---|
| Discovery | /.well-known/openid-configuration |
| JWKS | /.well-known/jwks.json |
| Authorize | /oidc.ashx?action=authorize |
| Token | /oidc.ashx?action=token |
| UserInfo | /oidc.ashx?action=userinfo |
| End session | /oidc.ashx?action=end_session |
| Signing alg | RS256 (2048-bit RSA, per-tenant key) |
| PKCE | required for all flows, S256 only |
| Grant types | authorization_code, refresh_token |
| Access token TTL | 3600s (1 hour) |
| ID token TTL | 3600s (1 hour) |
| Refresh token TTL | 14400s (4 hours), rotated on every use |
| Auth code TTL | 60s, single-use |
| Scopes | openid, profile, email, groups, phone, address |
| Revocation endpoint | /oidc.ashx?action=revoke |
| amr values | pwd, hwk, mfa, swk, otp |
| acr values | urn:codeb:acr:pwd, urn:codeb:acr:hwk, urn:codeb:acr:hwk-mfa, urn:codeb:acr:mfa |
| Key rotation | overlap window: rename private-key.xml → private-key-previous.xml; new key auto-generated; both published in JWKS |
Roles (in role claim) | admin, user, siponly, guest |
Need help wiring this up? Email info@codeb.io · Back to OIDC overview →
See also · platform integration guides
- Nextcloud single sign-on with CodeB — user_oidc + Social Login walk-through with the group-overwrite warning.
- WordPress single sign-on with CodeB — OpenID Connect Generic plugin, claim mapping, and a role-mapping snippet for functions.php.