Skip to content

OIDC applications

Sign people in to your own web, single-page and native apps with OpenID Connect, and configure their clients, tokens and claims.

Updated View as Markdown

An oidc application is an OpenID Connect client: your own app, or a SaaS app that supports OIDC, signing your directory’s people in. Each directory is its own OpenID Provider, with the issuer https://<alias>.sso.ezgh.app (endpoints). Only users you assign to the application, directly or through a group, can sign in to it.

A directory can have up to 200 applications, of every kind. You need sso.applications.create. For SAML 2.0 service providers, see SAML applications; to push an application’s assigned users to it over SCIM, see Outbound provisioning.

Application types

applicationType For Client authentication (tokenEndpointAuthMethod)
web Apps with a server that keeps a secret client_secret_basic (default), client_secret_post or private_key_jwt
spa Single-page apps in the browser none; PKCE (S256) required
native Desktop, mobile and command-line apps none; PKCE (S256) required

The type can’t change after creation. Every application uses the authorization code flow; web applications may use PKCE too, and it’s checked whenever it’s sent.

Create an application

In the console, open EZGH SSO > Applications, select Create application, and choose Web application, Single-page application or Native application.

ezgh sso applications create --name "Wiki" --type web --redirect-uri https://wiki.acme.com/callback

Or call CreateApplication:

{
  "name": "Wiki",
  "kind": "oidc",
  "oidc": {
    "applicationType": "web",
    "redirectUris": ["https://wiki.acme.com/callback"]
  }
}

The answer is { "application", "clientSecret" }. The client ID is application.oidc.clientId, the application’s ID. For client_secret_basic and client_secret_post, clientSecret is the first client secret, ezgh_scs_ and 43 letters and digits. It’s shown only in this answer (and replays of it with the same Idempotency-Key); store it now.

Then assign users or groups, and configure your app with the issuer, the client ID and the secret. Most OIDC libraries need only the issuer: they read the rest from discovery.

Settings

oidc holds the client’s settings. On update, only the fields you send change, and null clears an optional one.

Field Description Default
redirectUris 1 to 20 exact redirect URIs. See Redirect URIs. Required
postLogoutRedirectUris Up to 20 exact URIs that logout may return to. None
initiateLoginUri An https URL the portal tile opens. See Portal tiles. null
backchannelLogoutUri A public https URL that receives back-channel logout tokens. See Logout. null
jwks, jwksUri The client’s public keys, for private_key_jwt only (exactly one of them). See private_key_jwt. null
idTokenSignedResponseAlg RS256 or ES256. Signs the ID token and the access token. RS256
accessTokenLifetimeSeconds The access token’s lifetime: 300 to 3,600. 900
accessTokenAudience The access token’s aud: 1 to 256 characters without spaces. null is the client ID. null
groupsClaim Which groups the groups claim lists: assigned (the user’s groups that are assigned to this application) or all (all their groups). assigned

The answer also shows issuer, clientId, clientSecrets (metadata only), and for jwksUri when the keys were last fetched (jwksFetchedAt) and the last fetch’s error (jwksFetchError).

Redirect URIs

Redirect URIs match exactly: no wildcards and no fragments.

  • https URLs.
  • http only on a loopback address (127.0.0.1, [::1] or localhost).
  • native applications may also use a private-use scheme in reverse-domain form (com.acme.app:/callback), and a loopback IP redirect URI matches any port, as RFC 8252 says.

Client secrets

web applications with client_secret_basic or client_secret_post authenticate with a client secret. An application has at most two, so you can rotate without downtime:

  1. CreateApplicationSecret creates a second secret, shown once (409 too_many_secrets when there are already two).
  2. Deploy it to your app.
  3. DeleteApplicationSecret deletes the old one, which stops working at once.
ezgh sso applications secrets create Wiki
ezgh sso applications secrets delete Wiki <secret ID or prefix>

clientSecrets lists each secret’s ID, prefix (its first 13 characters), creator, creation time and when it was last used. Secrets are stored only as hashes: a lost secret can’t be shown again.

private_key_jwt

A web application can instead sign a JWT with its own private key (RFC 7523) and send it as client_assertion, with client_assertion_type urn:ietf:params:oauth:client-assertion-type:jwt-bearer. Give its public keys as one of:

  • jwks: a JWK set by value, { "keys": [...] }, of 1 to 10 RSA (2048 bits or more) or EC (P-256, P-384, P-521) signing keys, each with a kid when there are several;
  • jwksUri: a public https URL of the JWK set. It’s fetched hourly, and again (at most once a minute) when an assertion names a kid it doesn’t have.

The assertion’s iss and sub are the client ID, its aud the issuer or the token endpoint; it lives at most 10 minutes and can be used once. Algorithms: RS256–512, PS256–512, ES256–512.

Tokens

Token Format Lifetime
Authorization code Opaque 60 seconds, single use. A second use revokes the grant.
ID token JWT signed with idTokenSignedResponseAlg 1 hour
Access token JWT, RFC 9068 (typ: at+jwt) accessTokenLifetimeSeconds, 15 minutes by default
Refresh token Opaque, ezgh_srt_…; only with the offline_access scope Rotated on every use. 30 days without use, and 90 days at most from sign-in.
  • A refresh token can be used once: its answer carries a new one. Presenting a used refresh token again revokes the grant, and every token in it.
  • A refresh may ask for fewer scopes, never more.
  • Every code exchange and refresh checks that the user is still active and still assigned; if not, the grant is revoked.
  • Your APIs can validate access tokens with the directory’s JWKS, or call introspection to see revocation at once.
  • Signing keys rotate every 90 days. The next key is published in the JWKS 7 days before it’s used, and the old one stays there 7 days after. Read keys from the JWKS by kid; don’t pin them.

Scopes and claims

Scope Claims (ID token and userinfo)
openid Required for an ID token: sub (the user’s ID)
profile name, preferred_username (the email address), given_name, family_name (when set), updated_at
email email, email_verified (true once the user has activated)
groups groups: group names, filtered by groupsClaim
offline_access A refresh token

The ID token also has iss, aud (the client ID), azp, exp, iat, auth_time, nonce (when sent), acr, amr, sid (the portal session’s ID) and at_hash. A refreshed ID token keeps auth_time and has no nonce.

The access token has iss, sub, aud (accessTokenAudience, or the client ID), client_id, jti, iat, exp, auth_time, acr, amr, scope, sid, and groups with the groups scope.

Claim Values
amr pwd (password), otp (TOTP), hwk (passkey), fed (identity provider), mfa (identity provider MFA you trust)
acr urn:ezgh:sso:acr:mfa for a passkey, two factors, or trusted upstream MFA; otherwise urn:ezgh:sso:acr:sfa

Logout

  • RP-initiated logout: send the person to the logout endpoint with id_token_hint, and optionally a post_logout_redirect_uri registered in postLogoutRedirectUris and state.
  • Back-channel logout: with backchannelLogoutUri, EZGH SSO POSTs an OpenID Connect back-channel logout token (typ: logout+jwt, with sid and events) whenever a session the application has a grant in ends: logout, sign-out on the account page, signing in again in the same browser, an administrator revoking sessions, or the user being suspended, deleted or having their factors reset. Answer 200 or 204. Other answers (except 400) are retried for up to 20 attempts.

Ending a session also revokes the application grants made in it, except those with offline_access. Offboarding a user revokes all of them.

Portal tiles

With initiateLoginUri, assigned users see a tile for the application in the portal. The tile opens initiateLoginUri?iss=<issuer> (OpenID Connect third-party-initiated login), and your app starts a normal sign-in. Without it, there’s no tile.

Every application, of any kind, can have its own logo on its portal tile. Without one, the tile shows the application’s initials, or, for an EZGH product, the product’s own mark. An uploaded logo replaces either.

Upload it in the console, on the Logo card of the application’s Settings tab, with the CLI, or with UpdateApplicationLogo: { "data": "<base64>" }, a PNG or JPEG of at most 512 KiB and 1024 × 1024 pixels. It’s converted to a PNG. SVG files and links to images elsewhere are refused. DeleteApplicationLogo removes it. Both answer the application, and need sso.applications.update.

ezgh sso applications update wiki --logo wiki.png
ezgh sso applications update wiki --remove-logo

The application’s logoUrl is where the logo is served, on your directory’s own host: https://<alias>.sso.ezgh.app/branding/applications/<id>/logo?v=…, or null. It’s read-only, and a new logo gets a new URL. An application MCP Central manages can’t be given a logo (409 managed_by_product).

Delete an application

DeleteApplication revokes every grant and token it holds, and deletes its secrets, assignments and provisioning settings.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close