Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Registering an OAuth2 Client

Before a client application can request tokens from ahdapa it must be registered. ahdapa provides three registration paths:

  • Static clients file — a TOML file declared via [clients] file = ... in ahdapa.toml. Clients are seeded into the CRDT at startup, gossiped to peers, and protected from API modification. Suitable for pre-provisioned infrastructure clients (e.g. SSSD machine templates, CI pipelines). See Configuration §[clients] for the file format.
  • Admin API (POST /api/admin/clients) — operator-controlled registration with full access to every client field.
  • Dynamic Client Registration (POST /register, RFC 7591) — automated or self-service registration for applications that manage their own credentials.

All client state lives in the database and is replicated across cluster nodes via CRDT gossip regardless of how the client was registered.


Admin API registration

The admin API gives an operator full control over every client field, including fields that dynamic registration does not expose (Kerberos authentication, grant-type restrictions, mTLS certificate binding, and pairwise subject types).

The admin API requires an authenticated admin session. See Authentication Methods and the RBAC configuration in Configuration for how to grant the clients:write permission.

Create a client

curl -s -X POST https://idp.example.com/api/admin/clients \
  -b session.jar \
  -H "Content-Type: application/json" \
  -d '{
    "client_name": "My Web App",
    "redirect_uris": ["https://app.example.com/callback"],
    "scopes": ["openid", "profile", "email", "offline_access"],
    "token_endpoint_auth_method": "client_secret_basic",
    "client_secret": "change-me-to-a-random-secret"
  }'

Successful response — 201 Created:

{
  "client_id": "3f8a2c1e-7d4b-4e9f-a0c1-2b3d4e5f6a7b",
  "client_name": "My Web App",
  "redirect_uris": ["https://app.example.com/callback"],
  "scopes": ["openid", "profile", "email", "offline_access"],
  "token_endpoint_auth_method": "client_secret_basic",
  "source": "dynamic"
}

The client_id is a UUID generated by the server. Store it alongside the client_secret you supplied (the server does not echo secrets on subsequent GET calls).

Request body fields

FieldTypeRequiredDescription
client_namestringyesHuman-readable display name.
redirect_urisarray of stringsyes (for interactive flows)Allowed redirect URIs. Must be https:// except for loopback addresses (127.0.0.1, [::1]).
scopesarray of stringsnoScopes the client is permitted to request. Defaults to [].
token_endpoint_auth_methodstringnoAuthentication method at the token endpoint. Default: private_key_jwt. See table below.
client_secretstringif client_secret_basic or client_secret_postShared secret. Stored in the database; choose a high-entropy random value.
jwks_uristringif private_key_jwtURL of the client’s JWKS endpoint. The server fetches the public key from here to verify signed assertions.
tls_client_certificatestring (PEM)if tls_client_auth or self_signed_tls_client_authPEM-encoded client certificate. The server extracts and stores the SHA-256 thumbprint; the full PEM is not persisted.
tls_client_auth_subject_dnstringnoExpected Subject DN for tls_client_auth. Not enforced by the server today; stored for informational purposes.
grant_typesarray of stringsnoIf present, restricts which grant types the client may use. When absent, all grant types are permitted. Valid values: authorization_code, refresh_token, client_credentials, urn:ietf:params:oauth:grant-type:device_code, urn:ietf:params:oauth:grant-type:token-exchange, urn:ietf:params:oauth:grant-type:jwt-bearer.
subject_typestringno"public" (default) or "pairwise". Pairwise derives a per-client pseudonymous sub from the underlying identity.
kerberos_principalstringif kerberos_client_auth (single-machine)Exact Kerberos service principal. Format: service/host@REALM.
kerberos_principal_patternstringif kerberos_client_auth (template)Glob pattern matching multiple Kerberos principals. The * wildcard matches any sequence of non-@ characters. At most three * wildcards. Format: service/*@REALM.
kerberos_hbac_servicestringnoFreeIPA HBAC service name. When set, the server enforces HBAC rules before issuing a token to a kerberos_client_auth client.
id_token_signed_response_algstringnoJWS algorithm to use for signing both the ID token and the access token for this client (OIDC Dynamic Registration §3.1). Overrides the server-wide jwt_signing_algorithm. Allowed values: RS256, RS384, RS512, PS256, PS384, PS512, ES256, ES384, ES512, EdDSA, ML-DSA-44, ML-DSA-65, ML-DSA-87. When absent, the server default is used.

Supported token endpoint authentication methods

token_endpoint_auth_methodRequired fieldsNotes
private_key_jwtjwks_uriDefault. RFC 7523 §2.2 signed JWT assertion. Requires no shared secret.
client_secret_basicclient_secretHTTP Basic authentication header.
client_secret_postclient_secretclient_id + client_secret in the POST body.
client_secret_jwtclient_secretHMAC-signed JWT assertion (HS256/HS384/HS512).
tls_client_authtls_client_certificateRFC 8705 mutual TLS with a CA-issued certificate.
self_signed_tls_client_authtls_client_certificateRFC 8705 mutual TLS with a self-signed certificate.
kerberos_client_authkerberos_principal or kerberos_principal_patternAhdapa extension. Requires [ipa] gssapi = true. Admin API only — not available via DCR.
none(none)Public client. No credential check. PKCE is required for all flows.

Update a client

PUT /api/admin/clients/{client_id} replaces the registration with the supplied body. The same field constraints apply. Fields that are omitted revert to their defaults; the exception is client_secret and tls_client_certificate_thumbprint, which are preserved from the existing record when the update body omits them.

curl -s -X PUT https://idp.example.com/api/admin/clients/3f8a2c1e-7d4b-4e9f-a0c1-2b3d4e5f6a7b \
  -b session.jar \
  -H "Content-Type: application/json" \
  -d '{
    "client_name": "My Web App (v2)",
    "redirect_uris": [
      "https://app.example.com/callback",
      "https://app.example.com/callback2"
    ],
    "scopes": ["openid", "profile", "email", "offline_access"],
    "token_endpoint_auth_method": "client_secret_basic"
  }'

Response: 200 OK with the full updated client object.

Delete a client

curl -s -X DELETE https://idp.example.com/api/admin/clients/3f8a2c1e-7d4b-4e9f-a0c1-2b3d4e5f6a7b \
  -b session.jar

Response: 204 No Content. The client is tombstoned in the CRDT and propagated to all cluster nodes. Any existing tokens for this client remain valid until they expire (access tokens are self-contained JWTs); revoke outstanding refresh token families separately via DELETE /api/admin/refresh-families/{family_id} if needed.

List and inspect clients

# List all clients
curl -s https://idp.example.com/api/admin/clients -b session.jar

# Get a specific client
curl -s https://idp.example.com/api/admin/clients/3f8a2c1e-7d4b-4e9f-a0c1-2b3d4e5f6a7b \
  -b session.jar

Dynamic Client Registration (RFC 7591)

POST /register allows a client to register itself without operator involvement. It is available via two authorization paths:

Path 1 — Pre-shared initial access token

Set server.registration_token in ahdapa.toml:

[server]
registration_token = "a-random-high-entropy-token"

Then register:

curl -s -X POST https://idp.example.com/register \
  -H "Authorization: Bearer a-random-high-entropy-token" \
  -H "Content-Type: application/json" \
  -d '{
    "redirect_uris": ["https://app.example.com/callback"],
    "client_name": "Self-registered App",
    "token_endpoint_auth_method": "client_secret_basic",
    "scope": "openid profile email"
  }'

Path 2 — Kerberos service-principal session

A session cookie whose sub is a service principal in the server’s own Kerberos realm (format service/host@REALM) may register without a pre-shared token. See Authentication Methods for how to obtain such a session via SPNEGO at /authorize.

DCR request body

FieldTypeNotes
redirect_urisarray of stringsRequired.
client_namestringOptional. Defaults to the generated client_id.
token_endpoint_auth_methodstringclient_secret_basic (default), client_secret_post, private_key_jwt, or none. kerberos_client_auth is not available via DCR.
scopestringSpace-separated. Defaults to "openid".
jwks_uristringRequired when token_endpoint_auth_method = "private_key_jwt".
client_secretstringOptional. When omitted for client_secret_* methods, the server generates a random 32-byte secret.
subject_typestring"public" or "pairwise".

DCR response — 201 Created

{
  "client_id": "9b2e4f1a-3c7d-4a0e-b8f2-1d5a6c7e8f9g",
  "client_name": "Self-registered App",
  "redirect_uris": ["https://app.example.com/callback"],
  "token_endpoint_auth_method": "client_secret_basic",
  "scope": "openid profile email",
  "client_secret": "server-generated-secret-here",
  "registration_client_uri": "https://idp.example.com/api/admin/clients/9b2e4f1a-3c7d-4a0e-b8f2-1d5a6c7e8f9g"
}

The registration_client_uri points to the admin API resource for this client. Subsequent management (updates, deletion) requires admin RBAC permissions via the admin session — RFC 7592 management tokens are not issued.


Registration for Kerberos clients

kerberos_client_auth clients must be registered via the admin API. Dynamic registration rejects them with invalid_client_metadata. See Authentication Methods for the full workflow including HBAC enforcement and SSSD deployment patterns.

curl -s -X POST https://idp.example.com/api/admin/clients \
  -b session.jar \
  -H "Content-Type: application/json" \
  -d '{
    "client_name": "SSSD template client",
    "token_endpoint_auth_method": "kerberos_client_auth",
    "kerberos_principal_pattern": "host/*@EXAMPLE.COM",
    "kerberos_hbac_service": "sssd-idp",
    "scopes": ["openid"]
  }'

The * wildcard in kerberos_principal_pattern matches any hostname in the realm. Each matched machine presents its own Kerberos AP-REQ at the token endpoint; the server verifies it against the pattern and, if kerberos_hbac_service is set, against the replicated HBAC rule set.