Skip to content

OIDC clients

An OIDC client is the OAuth2 / OpenID Connect face of an application — it defines how a relying party obtains tokens from the realm's issuer.

What it is

OIDC clients speak OAuth2 and OpenID Connect against the realm issuer (https://<host>/realms/<realm>). Each client is either:

  • Confidential — holds a client secret and authenticates at the token endpoint (server-side apps, backends).
  • Public — has no secret and must use PKCE (SPAs, mobile, native apps).

Grant types

Clients can be enabled for any of:

  • authorization_code (with PKCE) — interactive login
  • refresh_token — long-lived sessions
  • client_credentials — service-to-service (uses a service account)
  • password — resource owner password (legacy; avoid for new apps)
  • device — device authorization grant for input-constrained devices
  • CIBA — Client-Initiated Backchannel Authentication
  • token-exchange (RFC 8693) — exchange a token for another, including on-behalf-of delegation where an agent acts for a user (see Agent authorization)

Key settings

The newer per-client settings (JWK Set by value, signed userinfo, request URIs, session management, login initiation, logo/policy/terms links, and the opt-in implicit and hybrid flows) are on OIDC client settings and OpenID Connect flows.

  • Redirect URIs — exact-match callback URLs for the code flow.
  • Web origins — allowed CORS origins; these are enforced at runtime, not cosmetic.
  • Scopes — the client scopes this client may request.
  • Per-client keys / JWKS — client-specific signing keys or a registered JWKS URL for private_key_jwt. The JWKS URL must be https (http://localhost / http://127.0.0.1 only in the dev profile); the token endpoint fetches it through the egress guard without following redirects, and a client stored with an http JWKS URL cannot authenticate outside dev.
  • Service accounts — the identity used for client_credentials; can hold its own roles.
  • Authorization Services (UMA) — fine-grained resource/policy/permission model for resource-server authorization.
  • Advanced token settings — token lifetimes, subject-claim override, audience handling, and resource indicators (RFC 8707) to narrow a token's audience to a specific resource server.
  • Agent binding — an OIDC client can be the issuance binding for an agent (non-human identity); the agent authenticates through this client (e.g. client_credentials or a workload JWT) and its tokens gain the nhi marker and lifecycle gate.

Warning

Public clients must use PKCE. Never embed a client secret in a browser app, SPA, or mobile binary — register it as a public client instead.

In the console

OIDC clients live under Manage, either via the parent application or the clients list. Clients are always shown by their application/client display name, never by a raw UUID.

Common tasks

  1. Register a client — open Applications → Create (or Clients → Create), pick confidential or public, and add redirect URIs and web origins.
  2. Enable grant types — turn on only the flows the client actually uses.
  3. Assign scopes — attach the client scopes that define which claims appear in tokens.
  4. Configure a service account — for client_credentials, enable the service account and assign it roles.
  5. Enable Authorization Services — define resources, policies, and permissions for UMA-style authorization.
  6. Rotate the secret / keys — regenerate the client secret or rotate per-client keys when needed.
  7. Who may act for users here (the client's Credentials tab) — the RFC 8693 token-exchange allow-list: the clients that may exchange a user's token for one whose audience is this client, shown in plain words ("Customer portal may act for its users at Orders API"). The default is none. The console warns about an allowed client that lacks the token-exchange grant, or that no longer exists.

Tip

Dynamic Client Registration is supported at /connect/register for programmatic, on-the-fly client creation per RFC 7591.

Manage over the API

All client management is available under /admin/realms/{realm}/clients. The examples below assume you have set $HELIX_URL / $REALM and logged in — see Authenticating to the API.

List clients

curl -s -b cookies.txt "$HELIX_URL/admin/realms/$REALM/clients"
[
  { "clientId": "helix-cli",     "name": "HelixIAM CLI",        "publicClient": true, "grantTypes": ["authorization_code","refresh_token","urn:ietf:params:oauth:grant-type:device_code"], … },
  { "clientId": "helix-console", "name": "Helix Admin Console", "publicClient": true, "grantTypes": ["authorization_code"], … }
]

Create a confidential client

publicClient: false mints a client secret, returned once in the create response.

# Refresh CSRF after any GET, then POST (see the auth guide for the helper)
CSRF=$(awk '($6=="__Host-XSRF-TOKEN"||$6=="XSRF-TOKEN")&&$7~/^r1\./{t=$7} END{print t}' cookies.txt)

curl -s -b cookies.txt \
  -H "Content-Type: application/json" \
  -H "X-XSRF-TOKEN: $CSRF" \
  -d '{
        "clientId": "acme-web",
        "name": "Acme Web",
        "publicClient": false,
        "grantTypes": ["authorization_code", "refresh_token"],
        "redirectUris": ["https://app.example.com/callback"],
        "webOrigins": ["https://app.example.com"]
      }' \
  "$HELIX_URL/admin/realms/$REALM/clients"
{
  "id": "19d467c0-fe42-46e2-a708-eb2c450cb17a",
  "clientId": "acme-web",
  "name": "Acme Web",
  "publicClient": false,
  "secret": "CyISWxzvGCkhqUld5Pm-GCVtcYvCtSu3",
  "grantTypes": ["authorization_code", "refresh_token"],
  "redirectUris": ["https://app.example.com/callback"],
  "webOrigins": ["https://app.example.com"],
  "scopes": []
}

Save the secret now

The secret is shown only in the create (and secret-rotation) response. Store it in your secret manager immediately. A public client ("publicClient": true) has no secret and must use PKCE.

Update or delete a client

Use the returned id for subsequent calls:

# Update (e.g. add a redirect URI) — PUT the full desired state
curl -s -b cookies.txt -X PUT \
  -H "Content-Type: application/json" -H "X-XSRF-TOKEN: $CSRF" \
  -d '{ "clientId": "acme-web", "redirectUris": ["https://app.example.com/callback", "https://app.example.com/silent-renew"] }' \
  "$HELIX_URL/admin/realms/$REALM/clients/19d467c0-fe42-46e2-a708-eb2c450cb17a"

# Delete — returns 204 No Content
curl -s -b cookies.txt -X DELETE -H "X-XSRF-TOKEN: $CSRF" \
  "$HELIX_URL/admin/realms/$REALM/clients/19d467c0-fe42-46e2-a708-eb2c450cb17a"

Full endpoint set

Method & path (under /admin/realms/{realm}) Purpose
GET /clients · POST /clients List / create clients
GET · PUT · DELETE /clients/{id} Read / update / delete a client
GET · PUT /clients/{clientId}/token-exchange The clients allowed to exchange tokens for this client ({"allowedClients": [...]}; clientId is the OAuth client id)
GET · POST /clients/{id}/authz/resources UMA resources
GET · POST /clients/{id}/authz/policies UMA policies
GET · POST /clients/{id}/authz/permissions UMA permissions
POST /realms/{realm}/connect/register (protocol endpoint, initial access token) Dynamic Client Registration

See the API reference for the complete schema of every field.

See also