Skip to content

OIDC

Every instance is a standard OpenID Connect provider, for apps and tools that sign users in with OIDC instead of the Zautha SDKs. Its users sign in on the hosted login; the relying party gets ID and access tokens.

On the instance’s host (https://{frontend host}):

Discovery /.well-known/openid-configuration
Keys (JWKS) /.well-known/jwks.json
Authorization /oauth/authorize
Token /oauth/token
UserInfo /oauth/userinfo
Revocation /oauth/revoke
Logout /oauth/end_session

Create a client on the instance’s OAuth clients page:

  • Confidential (a server that can keep a secret): authenticates with its client secret. It may use the client credentials grant.
  • Public (a single-page or native app): no secret (oauth_client_public); PKCE with S256 is required.

Each client lists its redirect URIs (see invalid_redirect_uri) and its scopes. A client marked first-party signs users in without a consent screen. Any other asks the user to allow it: a confidential client once (until the user’s consent is revoked), a public client every time, since nothing proves a public client is the app it claims to be. Reusing an authorization code revokes the tokens its first use issued.

Every client that is not first-party and still holds a grant for the user - a remembered consent, or a sign-in from a session that is still active - is one of the user’s authorized apps. The user sees them under Authorized apps in <UserProfile />’s Security tab and removes one there (after verifying again); your server does the same with GET /v1/backend/users/{userId}/authorized_apps and POST /v1/backend/users/{userId}/authorized_apps/{clientId}/revoke, and the dashboard on the user’s page. Removing an app revokes its grants, refresh tokens and authorization codes at once and the next sign-in asks for consent again; the user stays signed in. An access token already issued is a JWT valid for up to 15 minutes: a resource server that checks it offline accepts it until then, so one that needs revocation to take effect at once asks /oauth/userinfo, which refuses it. A removal by your server or a dashboard member is in the audit log; one by the user is not.

  • Grants: authorization_code, refresh_token and client_credentials. Responses come in the query or as a form_post.
  • Scopes: openid, profile, email and offline_access (for a refresh token).
  • Tokens: an authorization code lives 5 minutes, access and ID tokens 15 minutes. A refresh token is rotated on use, never outlives the session it came from, and reusing a redeemed one revokes the whole family.
  • Claims: sub, sid and nonce, and auth_time (an impersonation session carries act instead); with profile, name, given_name, family_name and preferred_username when the user has them, and updated_at; with email, email (the primary address) and email_verified.
  • prompt=none never shows a page: without a signed-in session the answer is login_required, without consent consent_required.
  • prompt=login, or a max_age the session’s sign-in is older than, sends the user through a fresh sign-in.
  • prompt=consent asks for consent again.

Send the browser to /oauth/end_session with id_token_hint, an ID token issued for the browser’s current session, and optionally post_logout_redirect_uri (one of the client’s). A logout that cannot be tied to the current session is refused with id_token_hint_invalid.

On a production instance the session cookie is SameSite=Lax, so a cross-site POST to /oauth/end_session (a form on another site posting to it) arrives without the cookie and is refused with 400 id_token_hint_invalid. Use a GET redirect to the endpoint instead.