SenseCrypt Docs
Concepts

Security profiles

Standard vs the FAPI 2.0/CIBA security profile — where the profile is set, what discovery advertises, the registration rules a FAPI application must satisfy and their 422 codes, and the runtime rules that follow.

Every SenseCrypt tenant runs one of two security profiles:

  • Standard — the general-purpose OIDC/OAuth 2.0 issuer described throughout these docs. Authorization Code with PKCE, client secrets or asymmetric client authentication, bearer tokens.
  • FAPI 2.0/CIBA (plain_fapi) — the hardened profile: pushed authorization requests only, asymmetric client authentication only, sender-constrained tokens, no RS256 signatures, short-lived authorization codes, and — for CIBA — signed backchannel requests.

plain_fapi is the only profile value this issuer implements. It appears in two places, and the two mean different things.

Where the profile is set

LevelFieldSet whereMutable?
Tenantfapi_profile on the tenantPOST /v1/admin/tenantsNo — fixed at creation, like the slug. A PATCH that names it is refused 422 with code: "immutable_field".
Applicationfapi_profile on the OIDC applicationPOST /v1/admin/oidc-apps, or PATCH laterYes, subject to the rules below

A FAPI 2.0/CIBA tenant holds every application to the profile. A new application in such a tenant is created with the tenant's profile whether or not the request asks for it, and a request that tries to set another value — including an explicit null — is refused 422 with code: "fapi.inherited_from_tenant". The same rule applies on PATCH, so there is no per-application opt-out.

A Standard tenant's applications adopt the profile one at a time. Set fapi_profile: "plain_fapi" on the application you want hardened; other applications in the tenant are unaffected.

A FAPI application's own rules hold whatever the tenant's discovery document says. The document describes what the issuer accepts from every client; the profile on your application is what your client is held to. Creating an unrelated Standard application in the tenant never relaxes a FAPI application's requirements.

What discovery advertises

The shape of the discovery document is derived, not a separate switch:

  • A FAPI 2.0/CIBA tenant advertises the FAPI shape always — every application in it is held to the profile by construction.
  • A Standard tenant advertises the FAPI shape while it has at least one live OIDC application and every one of them adopts the profile. The next Standard application takes the shape away again. A tenant with no live OIDC applications advertises the standard shape.

The difference on the wire:

MetadataStandardFAPI 2.0/CIBA
require_pushed_authorization_requestsfalsetrue
response_types_supportedcode plus the implicit and hybrid types["code"]
response_modes_supportedquery, fragment, form_post, their .jwt twins and the generic jwt["query", "jwt", "query.jwt"]
grant_types_supportedincludes implicitno implicit
token_endpoint_auth_methods_supportedclient_secret_post, client_secret_basic, client_secret_jwt, private_key_jwtprivate_key_jwt — plus the two mutual TLS methods on either shape, where they are available
id_token_signing_alg_values_supportedES256, RS256["ES256"]
authorization_signing_alg_values_supportedES256, RS256["ES256"]
token_endpoint_auth_signing_alg_values_supportedHS256, RS256, ES256, PS256ES256, PS256
request_object_signing_alg_values_supportedES256, PS256, RS256ES256, PS256
backchannel_authentication_request_signing_alg_values_supportedES256, PS256, RS256ES256, PS256
backchannel_token_delivery_modes_supportedpoll, ping, pushpoll, ping

Losing or gaining the earned shape is recorded in the audit log as issuer.advertised_profile_changed, with the application that caused it.

Management API service accounts (/v1/admin/m2m-apps) authenticate with a client secret at the same token endpoint and stay outside the profile by design: such a credential can only ever mint for the tenant's own Management API audience, never for a resource a relying party integrates against. They are not counted when the shape is derived.

Registration rules

Setting fapi_profile on an application is refused unless the row that would result satisfies every rule below. Each refusal is a 422 in the management envelope with a stable code in details[] — see Error codes. The rules are identical on create and on PATCH: the merged state is what is checked.

codeThe rule
fapi.requires_confidential_clientclient_type must be confidential. A SPA (public) client cannot adopt the profile.
fapi.requires_pkcerequire_pkce must be true.
fapi.requires_key_or_certificate_authtoken_endpoint_auth_method must be private_key_jwt, tls_client_auth or self_signed_tls_client_auth. Shared-secret methods are refused.
fapi.requires_sender_constrained_tokensAt least one of dpop_bound_access_tokens / tls_client_certificate_bound_access_tokens must be on. See Sender-constrained tokens.
fapi.forbids_rs256id_token_signed_response_alg must not be RS256. ES256 and RS256 are the two registrable values, so a FAPI application signs its id_tokens with ES256.
fapi.requires_code_onlyresponse_types must be exactly ["code"] — no implicit or hybrid type.
fapi.requires_https_redirect_urisEvery entry in allowed_redirect_uris must be https, a native-app custom scheme, or a loopback http address (localhost, 127.0.0.1, ::1).
fapi.forbids_ciba_pushbackchannel_token_delivery_mode must not be push — FAPI-CIBA is poll or ping.
fapi.inherited_from_tenantThe tenant is a FAPI 2.0/CIBA tenant and the request tried to set another profile (or clear it) on one of its applications.
fapi_ciba.requires_signing_keysThe application has a CIBA delivery mode, so it must have registered request-signing keys (jwks or jwks_uri) — every one of its backchannel requests is signed and has to be verifiable.
fapi_ciba.requires_certificate_bindingThe application has a CIBA delivery mode, so tls_client_certificate_bound_access_tokens must be on.

Runtime rules

Once the profile is on the application, these apply to its requests whatever the discovery document advertises:

  • Pushed authorization requests only. A direct /authorize from a FAPI application is refused invalid_request (as an error redirect when the plain redirect_uri parameter is registered, otherwise on SenseCrypt's refusal page). Push to the pushed_authorization_request_endpoint and redirect with the returned request_uri.
  • Authorization codes live at most 60 seconds, whatever the deployment's default code lifetime is.
  • No RS256 anywhere. The id_token (and a signed authorization response, which uses the same signer — see JARM) is ES256; a signed request object and a private_key_jwt client assertion must be ES256 or PS256. An RS256 client assertion or request object from a FAPI application is refused.
  • A client assertion's aud must be a string, and at most endpoints it must be the issuer identifier. An array aud is refused even when it contains the issuer. At the token endpoint (on the authorization_code and client_credentials grants) and at /par, only the issuer identifier is accepted; a non-FAPI client may name either the issuer identifier or the token endpoint URL at both of those endpoints — there is no PAR-URL audience for any client. Two relaxations: the CIBA backchannel endpoint accepts its own URL and the token endpoint URL from every client (CIBA's own rule), and a FAPI application registered for CIBA may name the token endpoint URL at the token endpoint on the CIBA and refresh_token grants (the FAPI-CIBA lineage).
  • A DPoP proof must carry a server nonce. A FAPI application's proof without one is answered use_dpop_nonce with a fresh DPoP-Nonce header, at the token endpoint and at the UserInfo endpoint. Retry with the value from the header.
  • Signed backchannel requests for CIBA. See FAPI 2.0/CIBA clients.

Availability of the mutual TLS half

tls_client_auth, self_signed_tls_client_auth and tls_client_certificate_bound_access_tokens need a deployment that terminates client certificates on a dedicated mutual TLS listener — the deployments where discovery publishes mtls_endpoint_aliases (and tls_client_certificate_bound_access_tokens: true). Check the tenant's discovery document before you plan around them.

Where that listener is not configured, registering either mutual TLS authentication method or certificate-bound tokens is refused 422 with code: "mtls.listener_unavailable" — a client that could never authenticate is refused up front rather than at its first token request. A FAPI-CIBA application is affected by this directly: fapi_ciba.requires_certificate_binding makes certificate binding mandatory, so a FAPI application with a CIBA delivery mode can only be registered where the aliases are published.

DPoP has no such dependency: it is available on every deployment and to every application, FAPI or not.

On this page