Identity · OpenID Connect

Single sign-on, built in.

CodeB ships with its own OpenID Connect identity provider. Sign-in lives on every page: a button on the landing form gives joiners a verified-in-call badge, the same button on the SoftPhone PWA auto-registers their SIP identity, and admin + third-party apps (Nextcloud, custom RPs) all federate against the same per-tenant user database. Cookie-free, PKCE-only public clients or confidential clients with a secret — pick what fits.

OIDC is on by default. Discovery URL: https://<your-host>/.well-known/openid-configuration

Adding it to your own site? → Read the integration how-to · See the data flow

Platform integration guides
Nextcloud user_oidc · Social Login WordPress OpenID Connect Generic

What you get

EU Wallet sign-in LIVE

The OID4VP 1.0 verifier substrate is shipped. Any spec-compliant EU Digital Identity Wallet (EUDI Wallet) can sign in, sign up or subscribe on a CodeB tenant. ES256-signed Authorization Requests, DCQL queries, JWE-encrypted responses, SD-JWT VC selective disclosure — relayed through the OIDC userinfo your relying parties already consume.

Feature overview · Try it · Proof of work · API docs · Free training (dial 1844)

OID4VP 1.0 · eIDAS 2.0

Three methods, one screen

Password, passkey (FIDO2 / WebAuthn) and EU Wallet (EUDI) sign-in live side-by-side on the same /login.html — equal weight, no hierarchy, no third URL to remember. Magic-link email sign-in is one click below. Relying parties can deep-link a specific method with ?method=passkey / ?method=eu-wallet / ?method=password so Nextcloud, WordPress and other OIDC clients can route users straight to the right flow.

Try the picker · Passkey flow · EU Wallet flow

UX · one credential store

Standards-based

OpenID Connect Core 1.0, Authorization Code flow with PKCE (S256), RS256-signed JWTs. Discovery per RFC 8414, JWKS per RFC 7517. Any RP that speaks OIDC will work.

OIDC Core 1.0

Cookie-free, honestly

No cookies anywhere — not session, not tracking. Sign-in across multiple RPs uses a signed, 30-minute, same-origin assertion in localStorage at the IdP origin: never sent to other sites, never used for cross-site tracking, cleared on logout. Per-tab access & refresh tokens live in sessionStorage and vanish when the tab closes. Full design on the data-flow page.

Privacy

One credential store

Sign-in reuses the same HA1 digest your SIP softphones use. No plaintext password ever reaches the server — the browser hashes it before posting. One user record drives both voice and identity.

Why HA1 specifically: CodeB uses SIP-compatible HA1 digests so the same user record drives both the softphone (which requires digest-auth per RFC 3261) and OIDC sign-in. HA1 is treated as a password-equivalent secret and stored with the same care. A modern-KDF option (Argon2 / scrypt / PBKDF2-SHA256) for pure web/OIDC deployments without SIP softphones is on the roadmap.

No duplicate users

Per-tenant keys

Each tenant gets its own 2048-bit RSA signing key, generated on first need and persisted to App_Data/<tenant>/oidc/. Tokens minted for tenant A never verify against tenant B.

Multi-tenant

Roles, not just identities

Four roles out of the box: admin, user, siponly, guest. The role travels in the JWT as a custom claim and as a standard groups entry. Per-user custom groups can be set in the admin UI to feed Nextcloud / RP membership directly into the token. Admin pages enforce role === "admin" server-side on every request.

RBAC

Verified-in-call badge

Sign in on the landing page before joining and your conference tile shows the amber CodeB shield to every participant. Anti-impersonation for free, no third-party identity provider, no Zoom-style verified-name extension required.

Anti-impersonation

Passwordless email-link sign-in

Users who'd rather not type a password (or who forgot it on a phone they're handed) tap “Email me a sign-in link instead” on the login screen, enter their email, and a one-time link arrives in their inbox. Click it — signed in. Single‑use, 15-minute TTL, no enumeration on the start envelope. AMR ["user"] and ACR urn:codeb:acr:email-link mark it as a weaker factor than password so RPs can require step‑up for sensitive operations.

Passwordless

One passkey, every app

Register a single FIDO2 credential (TouchID/FaceID/Windows Hello/hardware token) on the CodeB IdP once. From then on every relying party — Nextcloud, WordPress, GitLab, your CRM, custom apps — gets phishing‑resistant authentication for free, because they all just do the standard OIDC dance against your CodeB host. The RP never touches the passkey, never sees the credential, never has to implement WebAuthn. It consumes a signed ID token with amr: ["hwk","user"] and acr: urn:codeb:acr:hwk-mfa, and that's it.

Compounding effect:

  • One credential to manage — one revoke if the laptop is lost, RP-Initiated Logout signs the user out of every connected app.
  • Any RP can demand step-up by checking the acr claim — sensitive operations require passkey, casual ones accept password.
  • Adding a new app to your stack costs zero auth work; it inherits the existing factor matrix.
  • If you also store EU Wallet verified claims at the IdP, those flow through too — the RP sees given_name, family_name, birthdate from a government‑rooted source without any wallet-specific code.
Federation win

Authenticator app (TOTP)

Users who don't want to register a passkey can still strengthen password sign-in with a standards-compliant time-based one-time password (TOTP, RFC 6238). Scan the QR code in /account.html with any authenticator app — Google Authenticator, Microsoft Authenticator, 1Password, Bitwarden, Authy — and the login form asks for a 6-digit code after the password.

8 single-use recovery codes are generated at enrolment for the “phone in the washing machine” case. An email fallback can send a short-lived 6-digit code to the address on file. The shared secret lives encrypted in App_Data/<tenant>/users/<u>/totp.json, never on a vendor's server. Enrol, verify and disable are all audit-logged to the same OIDC audit feed as everything else. Passkey and EU Wallet sign-ins skip the TOTP step — they are already strong factors on their own.

For the “lost everything” case — authenticator gone, recovery codes lost, email unreachable — an admin can clear a user's TOTP enrolment from the /register.html user-edit popup with one click. The override is audit-logged with both the admin and the target user. Brute-force protection: every failed verify bumps a per-session attempt counter, and the session is destroyed after 5 wrong codes — the attacker has to start a new password+TOTP flow from scratch, not just keep guessing the same session.

RFC 6238 · AMR otp

Authentication factor in the token

Every id_token + access_token carries amr (RFC 8176) and acr. RPs see the actual factor — ["pwd"] for password, ["hwk","user"] for FIDO2 passkey — and can require step-up auth where it matters.

RFC 8176

Hot key rotation

Two signing keys can be active at once. Rename private-key.xmlprivate-key-previous.xml; the next request generates a fresh active key. JWKS publishes both, the kid in each token pins it to the right one. No IIS recycle, no dropped sessions.

Zero-downtime

Token revocation (RFC 7009)

POST a refresh token to /oidc.ashx?action=revoke to end a session immediately on the IdP side. Confidential clients authenticate with their secret; public clients revoke without auth. Always returns 200 to prevent token-validity enumeration.

Operator

Integration guide

Adding CodeB sign-on to your own app takes a config block. The OIDC how-to walks through discovery, PKCE, token validation and refresh, with copy-paste samples in vanilla JS and Node/Express.

Developer

The endpoints

Standard OIDC URLs. RPs typically only need the discovery URL — everything else is auto-discovered.

EndpointPurpose
/.well-known/openid-configuration Discovery document. RFC 8414. Lists every other endpoint and the supported algorithms.
/.well-known/jwks.json JSON Web Key Set. RFC 7517. The tenant’s RSA public key, so any RP can verify signatures.
/oidc.ashx?action=authorize Authorization endpoint. Redirects to the login form, then back to the RP with an auth code.
/oidc.ashx?action=token Token endpoint. Exchanges the auth code (with PKCE verifier) for an access token, ID token, and refresh token.
/oidc.ashx?action=userinfo UserInfo endpoint. Returns the signed-in user’s sub, role, profile claims.
/oidc.ashx?action=end_session RP-Initiated Logout 1.0 (OIDC). Clears tokens client-side, redirects to post_logout_redirect_uri after validating it against the client's registered list.
/oidc.ashx?action=passkey-signin-start Passkey (FIDO2 / WebAuthn) sign-in — mints a PublicKeyCredentialRequestOptions JSON for the browser to feed to navigator.credentials.get(). No bearer required: the passkey itself is the authentication.
/oidc.ashx?action=passkey-signin-finish Receives the signed WebAuthn assertion, verifies it (rpIdHash, signature, counter regression check), and emits the same shape as /login{ok, redirect, user, role, sso_assertion, sso_max_age}. AMR ["hwk","user"], ACR urn:codeb:acr:hwk-mfa.
/oidc.ashx?action=recover-start Self‑service password recovery, step 1. POST {email}. Always returns the same 200 envelope regardless of whether the email matches a user (no enumeration). If found, mints a one‑time JWT (typ=recover, single‑use JTI, 15‑minute TTL) and emails the reset link. Rate‑limited per IP and per email.
/oidc.ashx?action=recover-finish Self‑service password recovery, step 2. POST {token, new_ha1}. Verifies the JWT (sig/exp/iss/typ), atomically claims the JTI in _usedRecoverJtis (single‑use), rejects the all‑zeros HA1 sentinel, then calls UpdateUserHa1Atomic. Returns {ok, redirect: "/login.html?recovered=1"}.

Wire up an RP in three lines

Any OIDC-compliant relying party works. Point it at the discovery URL, use the public client ID codeb-admin, and you’re done.

Issuer https://phone.codeb.io Client ID codeb-admin Client type public (PKCE-only, no secret) Redirect URI https://<your-rp>/oidc-callback Scope openid profile email PKCE method S256

The discovery document fills in authorization_endpoint, token_endpoint, jwks_uri, id_token_signing_alg_values_supported and the rest automatically.

What the ID token looks like

RS256-signed JWT. Standard OIDC claims plus a tenant-scoped role and any profile fields the admin filled in for that user.

{ "iss": "https://phone.codeb.io", "sub": "alice", "aud": "codeb-admin", "exp": 1748467200, "iat": 1748463600, "auth_time": 1748463600, "name": "Alice Walker", "preferred_username": "alice", "email": "alice@example.com", "email_verified": true, "role": "admin", "groups": ["admin"] }

Cookie-free, by design

OIDC implementations typically lean on a session cookie at the IdP to keep the user "signed in" across /authorize calls. CodeB doesn’t. The login form parses the original authorize URL, validates the PKCE challenge and redirect URI server-side, mints the auth code directly, and posts it back as JSON. The browser navigates to the RP’s callback without ever bouncing through a cookie-bearing redirect.

That keeps the privacy posture honest: no cookies anywhere on the CodeB site, OIDC included. If you advertise cookie-free, you stay cookie-free.

EU Digital Identity Wallet sign-in

CodeB’s OIDC layer accepts the EU Digital Identity Wallet (EUDI Wallet) as a first-class sign-in method. The verifier substrate — OID4VP 1.0 with the HAIP 1.0 client_id scheme, DCQL credential queries, ECDH-ES + A128GCM JWE-encrypted responses, SD-JWT VC parsing with KB-JWT holder binding — was shipped on 2026-06-08 and runs end-to-end against any spec-compliant wallet.

For relying parties this changes nothing: the verified credential claims are relayed through the same /oidc.ashx userinfo endpoint and ID token your application already consumes. A user sign-in via password, smart card, or EU Wallet looks identical at the RP layer — the amr claim tells you which factor was used.

From December 2027 every regulated EU private-sector business — banks, telcos, insurers, large platforms, healthcare, government suppliers — is legally required to accept the EU Wallet as a customer login (eIDAS 2.0 Article 5f sub 2). CodeB tenants have the verifier live today; on the day the rule turns on, the stack is already in place.

Full feature overview → · Try the live flow → · Verified proof of work → · Public verifier API →

What’s NOT done yet, honestly: the issuer-signature chain against the EU’s List of Trusted Lists (LoTL) is iter-2 and ships when the EUDI Wallet ecosystem matures further. Today the verifier validates the protocol but trusts the issuer key out-of-band. Production high-assurance identity proofing should wait for iter-2 plus a notified national wallet. OpenID Foundation self-certification for OID4VP 1.0 + HAIP 1.0 is in progress.

Multi-tenant from day one

Tenant identity is the request domain — same as the rest of CodeB. A user signing in at phone.acme.com hits the Acme RSA key and the Acme SIP credential file; a user at phone.contoso.com hits the Contoso key and the Contoso credentials. The two tenants share no state.

Adding a new tenant means adding a hostname. The first request to /oidc.ashx generates that tenant’s RSA key and persists it at App_Data/<tenant-domain>/oidc/private-key.xml. No schema migration, no restart, no downtime for the other tenants.

Operator notes

QuestionAnswer
How are passwords stored? As HA1 digests: MD5(user:realm:password) per RFC 2617, the format SIP softphones already use. Plaintext password never reaches the server — the login form hashes it in the browser. HA1 is treated as a password-equivalent secret. A modern-KDF option (Argon2 / scrypt) for pure web/OIDC deployments without SIP softphones is on the roadmap.
Where do user accounts live? App_Data/<tenant>/sip-credentials/<tenant-slug>.json. Manage them from the Register admin page.
Where does the private key live? App_Data/<tenant>/oidc/private-key.xml. Plain XML, not committed to source. Back it up with the rest of App_Data.
How do I rotate the key? Delete the file and recycle the IIS app pool. The next request regenerates a fresh key with a new kid. RPs re-fetch JWKS automatically.
Is there an audit log? Yes — App_Data/<tenant>/logs/codeb-oidc-YYYY-MM-DD.log (JSONL), and a parallel feed into the Windows Event Log under source CodeBOIDC.
How long do tokens live? Access tokens 1 hour. Refresh tokens 7 days. ID tokens 1 hour. Auth codes 60 seconds, single use.

Want the rest? All features →