FerrisKeyDocs

Clients

A client represents an application that authenticates through FerrisKey. Every OAuth2 and OIDC flow starts with one: it says which application is asking for access, and it determines which authentication methods and token settings apply.

Client types

There are three:

TypeSecretUse case
ConfidentialYesServer-side applications that can keep a secret
PublicNoSingle-page apps, mobile apps, CLI tools
SystemNoneInternal FerrisKey clients, created automatically and not user-managed

Choosing a client type

Pick confidential when your application has a backend that can hold the secret safely. Pick public for anything running in a browser or on a device, where the secret would end up in the user’s hands anyway.

A client also records where it came from in registration_source: admin for a client created through the console or the administration API, dynamic for one that registered itself anonymously, and metadata_document for one resolved from a client ID metadata document. The last two always require consent and never get access to the administration API. See MCP.

Capability config

Which flows a client may take is set on its settings screen, and two of the choices are permanent.

FerrisKey client settings showing general fields and the capability config with public/confidential and grant switches FerrisKey client settings showing general fields and the capability config with public/confidential and grant switches

Client authentication is set at creation and final. A client cannot move between confidential and public afterwards. Pick confidential when a backend can hold a secret server-side, public for SPAs, mobile apps and CLIs where a secret would end up in the user’s hands anyway. The protocol (OpenID Connect or SAML 2.0) is the other permanent choice.

The grant switches are not permanent, and each one widens the attack surface:

SwitchTurn it on when
Direct access grantsNever, for anything new. It is the deprecated password grant
OAuth 2.0 device authorization grantThe client is browserless: a CLI, an IoT device, a TV app
OAuth 2.0 token exchangeThe client is a backend service that trades a user’s access token for one aimed at another service. See token exchange

Require PKCE goes the other way: leave it on. A client created through the console or the API requires PKCE unless the request says otherwise, and an authorization request without an S256 code challenge is then refused. S256 is the only method discovery advertises.

A disabled client keeps its whole configuration and rejects every request, which is what makes it different from maintenance mode, where a whitelist still gets through.

Client properties

PropertyDescription
client_idPublic identifier used in OAuth2 flows
secretClient secret (confidential clients only)
protocolProtocol type, openid-connect or saml. Fixed at creation
enabledWhether the client can initiate authentication
redirect_urisAllowed redirect URIs after authentication
direct_access_grants_enabledAllow the deprecated password grant type
oauth_device_code_grant_enabledAllow the device code grant, used by CLIs and other browserless clients
require_pkceRequire an S256 code challenge on the authorization code flow. true by default on new clients
public_clientPublic client: no secret, so PKCE carries the protection
service_account_enabledEnable client credentials grant
token_exchange_enabledAllow the RFC 8693 token exchange grant. false by default
backchannel_logout_uriWhere FerrisKey POSTs a logout token when a session this client took part in ends. Unset by default, which opts out. See back-channel logout
backchannel_logout_session_requiredRecords that the client needs the sid claim in its logout tokens. true by default, and FerrisKey always sends sid
consent_requiredAsk the user’s consent when the client requests optional scopes. false by default
registration_sourceadmin, dynamic or metadata_document

consent_required is not in the console: set it through the API, in the body of POST /realms/{realm_name}/clients or PATCH /realms/{realm_name}/clients/{client_id}. A client that registered itself requires consent whatever this flag says. See consent.

Authenticating at the token endpoint

A confidential client proves who it is with client_secret_basic (an HTTP Basic header) or client_secret_post (client_id and client_secret in the form body). A public client sends only its client_id, which discovery lists as none. Those three are the whole list: private_key_jwt and the other assertion-based methods are not supported.

The username and password of a Basic header are form-decoded, as RFC 6749 §2.3.1 requires. A secret that contains + or % must therefore be percent-encoded before it is base64-encoded, or FerrisKey reads a different secret than the one you meant to send.

The client_credentials grant needs a confidential client with service_account_enabled, and it returns an access token with no refresh token. The token endpoint section lists the errors and the throttles that apply.

Redirect URIs

FerrisKey compares the redirect_uri of an authorization request with the enabled redirect URIs of the client. An entry is a literal address, and it must match character for character. An entry written ^...$, anchored at both ends, is read as a regular expression that must match the whole value. An expression that also matches the empty string never matches anything.

Prefer literal addresses. A loose pattern lets an authorization code leave for an address you did not intend. Post-logout redirect URIs are compared exactly, and the pattern form does not apply to them.

PUT .../redirects/{uri_id} only toggles enabled. To change an address, create the new entry and delete the old one.

Token lifetime overrides

Clients inherit token lifetimes from their realm. Override them per client when one application needs a different window:

OverrideDescription
access_token_lifetimeAccess token TTL in seconds
refresh_token_lifetimeRefresh token TTL in seconds
id_token_lifetimeID token TTL in seconds
temporary_token_lifetimeTemporary token TTL in seconds

A value set on the client wins over the realm default. Leave it null and the realm default applies. The refresh token lifetime also sets how long the user’s SSO session lives for a login through this client.

Direct access grants

With this enabled, a client can use the resource owner password credentials grant, sending a username and password straight to the token endpoint. It is handy for trusted first-party applications and for testing. Do not enable it for third-party clients. The grant refuses any account that still owes MFA or a required action, so it cannot stand in for a browser login.

Sub-resources

A client is not one object: several of its properties are collections with their own endpoints.

CollectionEndpointsWhat it controls
Redirect URIsGET POST …/{client_id}/redirects, PUT DELETE …/redirects/{uri_id}Where FerrisKey may send the browser back after login
Post-logout redirectsGET POST …/{client_id}/post-logout-redirects, PUT DELETE …/post-logout-redirects/{uri_id}Where it may send the browser after logout
Web originsGET POST …/{client_id}/web-origins, DELETE …/web-origins/{web_origin_id}Browser origins allowed to call realm-scoped routes
Client rolesGET POST …/{client_id}/rolesRoles scoped to this client
Client secretGET …/{client_id}/client-secretReading it raises client_secret_viewed in the audit log
Scope assignmentPUT DELETE …/default-client-scopes/{scope_id} and …/optional-client-scopes/{scope_id}See Client scopes
Token exchange policiesGET POST …/{client_id}/token-exchange-policies, DELETE …/token-exchange-policies/{policy_id}Which other clients this client may obtain tokens for. See token exchange

Web origins are per realm-scoped route only

A client’s web origins cover the realm-scoped routes. They do not cover /config, the health probes or the API documentation, which carry no realm, so those need the server-wide ALLOWED_ORIGINS. A console served from a different origin than the API needs both.

Previewing the token a client would get

POST /realms/{realm_name}/clients/{client_id}/evaluate-scopes

Takes a user_id and an optional scope, and returns the claims this client’s scopes would produce for that user, without a login or a signed token. It is the fastest way to check a protocol mapper before wiring an application to it.

Service accounts

A client with service_account_enabled can authenticate through the client credentials grant, with no user involved. FerrisKey creates a linked service account user for it, and that user takes roles and permissions like any other.

This is the usual pattern for machine to machine calls.