Skip to main content

Dynamic Client Registration

Dynamic Client Registration (RFC 7591) lets you create an OAuth 2.1 client at ThunderID with an HTTP request instead of going through the Console. It is used by automated provisioning pipelines, SDK installers that bootstrap their own clients, and federation flows where a relying party registers itself on first contact.

The endpoint accepts the standard RFC 7591 metadata fields plus localized variants of human-readable strings (OIDC language tags). A registration can then be read, updated, and deleted through the client configuration endpoint, as defined by RFC 7592. Those operations are administrative: one caller holding system-level permissions manages every registration. See Manage a Registration.

note

DCR registers applications only. Agents are provisioned through the ThunderID Console or the agent management API, not through /oauth2/dcr/register. Applications that use the embedded sign-in approach are also registered through the Console rather than via DCR.

How It Works​

How ThunderID Implements It
AspectBehavior
EndpointPOST /oauth2/dcr/register
AuthenticationOptional, configurable. When enabled, the caller must authenticate as a privileged client.
RFC 7592 managementSupported. GET, PUT, and DELETE on /oauth2/dcr/register/{client_id}, authorized by a caller holding system-level permissions
Localized metadataSupported via <field>#<bcp47-tag> syntax (see below)
Returned credentialsclient_id, client_secret (for confidential clients), and the full echoed metadata
Discovery fieldregistration_endpoint in Server Metadata

Register a Client​

Send a JSON body with at minimum a redirect URI and grant type:

http
POST /oauth2/dcr/register
Content-Type: application/json

{
"client_name": "My App",
"redirect_uris": ["https://app.example.com/callback"],
"grant_types": ["authorization_code"],
"response_types": ["code"],
"token_endpoint_auth_method": "client_secret_basic"
}

A successful registration returns 201 Created with the assigned client_id, client_secret (when applicable), and the full set of registered metadata echoed back.

json
{
"client_id": "abc123",
"client_secret": "s3cr3t",
"client_secret_expires_at": 0,
"client_name": "My App",
"redirect_uris": ["https://app.example.com/callback"],
"grant_types": ["authorization_code"],
"response_types": ["code"],
"token_endpoint_auth_method": "client_secret_basic"
}

Request Fields​

FieldRequiredDescription
redirect_urisYesOne or more URIs ThunderID redirects to after authentication.
grant_typesNoOAuth 2.1 grant types the client may use. Specify explicitly. No default is applied. Supported values: authorization_code, refresh_token, client_credentials, urn:ietf:params:oauth:grant-type:token-exchange.
ou_idNoThe organization unit ID to associate the registered client with. If omitted, the client is registered under the root organization unit.
response_typesNoOAuth 2.1 response types. Specify explicitly. No default is applied. Use code.
token_endpoint_auth_methodNoHow the registered client authenticates at ThunderID's client-authenticated endpoints (token, introspection, revocation, PAR, and CIBA). Supported values: client_secret_basic (default), client_secret_post, private_key_jwt, none.
client_nameNoHuman-readable name of the client.
client_uriNoURL of the client's home page.
logo_uriNoURL of the client logo image.
tos_uriNoURL of the client's Terms of Service.
policy_uriNoURL of the client's Privacy Policy.
contactsNoArray of administrator email addresses for this client.
scopeNoSpace-separated list of scopes the client is allowed to request.
jwks_uriNoURL of the client's JWKS endpoint. ThunderID fetches public keys from this URL to verify signed requests. Required for private_key_jwt. Cannot be used together with jwks.
jwksNoInline JSON Web Key Set. Required for private_key_jwt when a hosted JWKS endpoint is not available. Cannot be used together with jwks_uri.
require_pushed_authorization_requestsNoWhen true, the client must use the /oauth2/par endpoint before starting an authorization flow (RFC 9126). Defaults to false.
userinfo_signed_response_algNoRequests a signed (JWS) userinfo response, signed with the algorithm you specify. Must be one of the values advertised in userinfo_signing_alg_values_supported (Server Metadata); any other value is rejected with invalid_client_metadata.
userinfo_encrypted_response_algNoKey-management algorithm for userinfo response encryption. Supported values: RSA-OAEP, RSA-OAEP-256.
userinfo_encrypted_response_encNoContent-encryption algorithm for userinfo response encryption. Required when userinfo_encrypted_response_alg is set. Supported values: A128CBC-HS256, A256GCM.
id_token_signed_response_algNoJWS algorithm used to sign the client's ID tokens. Must be one of the values advertised in id_token_signing_alg_values_supported (Server Metadata); any other value is rejected with invalid_client_metadata. When omitted, ID tokens are signed with the deployment's signing key.
id_token_encrypted_response_algNoKey-management algorithm for ID token encryption. Supported values: RSA-OAEP, RSA-OAEP-256.
id_token_encrypted_response_encNoContent-encryption algorithm for ID token encryption. Required when id_token_encrypted_response_alg is set. Supported values: A128CBC-HS256, A256GCM.

Response Fields​

A successful registration returns 201 Created. The response echoes all registered metadata and includes the following additional fields:

FieldDescription
client_idThe OAuth 2.1 client identifier assigned by ThunderID.
client_secretThe client secret. Present only for confidential clients. Store this value securely. ThunderID does not return it again.
client_secret_expires_atExpiry time of the client secret as a Unix timestamp. 0 means the secret does not expire.
app_idThe internal ThunderID application identifier linked to this registered client.

Manage a Registration​

A registration is managed at the client configuration endpoint, as defined by RFC 7592.

text
/oauth2/dcr/register/{client_id}

Every request to this endpoint must be made by a caller holding system-level permissions, presenting its access token as a Bearer token. A caller holding that permission can manage any registration, so a single administrative credential governs every dynamically registered client. This suits a portal that provisions and maintains many clients on their behalf.

note

Registration does not hand the registered client a credential for managing itself. A client cannot read, update, or delete its own registration; those operations belong to an administrative caller.

Read a Registration​

http
GET /oauth2/dcr/register/abc123
Authorization: Bearer <admin_access_token>

Returns 200 OK with the currently registered metadata, including any localized variants the client has.

json
{
"client_id": "abc123",
"client_name": "My App",
"client_name#fr": "Mon Application",
"redirect_uris": ["https://app.example.com/callback"],
"grant_types": ["authorization_code"],
"response_types": ["code"],
"token_endpoint_auth_method": "client_secret_basic"
}

Read a registration before updating it. An update replaces the localized variants of every field it supplies, so the response above is what you modify and send back to keep the variants you are not changing.

note

ThunderID stores the client secret write-only, so it cannot be read back after registration. The client_secret is therefore absent from this response, unlike the example in RFC 7592. Store the secret when you register the client. If you lose it, register a new client.

Update a Registration​

http
PUT /oauth2/dcr/register/abc123
Authorization: Bearer <admin_access_token>
Content-Type: application/json

{
"client_id": "abc123",
"client_name": "My Renamed App",
"redirect_uris": ["https://app.example.com/callback2"],
"grant_types": ["authorization_code"],
"response_types": ["code"],
"token_endpoint_auth_method": "client_secret_basic"
}

Returns 200 OK with the updated registration.

If your update changes token_endpoint_auth_method from none to a method that uses a secret, ThunderID issues a client secret for the client and returns it in the update response. That response is the only place the value appears, so store it when you receive it. The reverse is not true: an update to a client that already has a secret does not rotate it, and the response carries no client_secret. There is no way to recover or rotate a secret through this endpoint, so a client that loses one must be registered again.

An update replaces the registered metadata in full. Any field you omit is not carried over from the previous registration, so send the complete set of metadata you want the client to keep. The client_id, client_secret, and ou_id are preserved across the update, and the request must not attempt to change them: an ou_id in the body is ignored rather than applied, so a registration cannot be moved between organization units this way.

The client name is the one exception to the replacement. A registration must always have a name, so if you omit both client_name and every localized variant of it, ThunderID keeps the name the client already has rather than clearing it. Every other mutable field must be resubmitted to survive the update.

Localized variants are replaced too. When an update supplies a value for a localizable field, whether the base field or a language variant, ThunderID discards the variants that field already had and stores only the ones in the request, so a translation you omit is removed. A field your update leaves out entirely is cleared along with its variants, like any other omitted field, so resubmit the variants you want to keep. The unlocalized value of a field is not one of those variants: if you send only language variants, the existing base value is kept, and it changes only when you send the field itself. The client name is the exception here as well: an update that mentions neither client_name nor any variant of it keeps the stored name and its translations.

The body must include client_id, and it must match the client in the request path. A request that omits it, or that names a different client, is rejected with 400.

You may also include client_secret. It is optional, but when present it must match the client's currently issued secret, and a request carrying the wrong value is rejected with 400 without changing the registration. The value is only checked, never stored, so an update cannot be used to choose a new secret. ThunderID ignores the server-managed field client_secret_expires_at if you send it.

Delete a Registration​

http
DELETE /oauth2/dcr/register/abc123
Authorization: Bearer <admin_access_token>

Returns 204 No Content. The client is removed, and the endpoint no longer resolves a registration for its client_id.

Management Error Responses​

HTTP StatusCause
401 UnauthorizedThe caller does not hold the required system-level permission. The response carries a WWW-Authenticate: Bearer error="invalid_token" header.
404 Not FoundNo registration exists for the given client_id, including one already deleted.
400 Bad RequestThe update contains invalid client metadata, or omits client_id, or carries a client_id that does not match the request path, or carries a client_secret that does not match the issued secret.

Localized Metadata​

You can provide translations of client_name, logo_uri, tos_uri, and policy_uri for different languages by appending a # and a BCP 47 language tag to the field name.

Syntax: <field>#<language-tag>

http
POST /oauth2/dcr/register
Content-Type: application/json

{
"client_name": "My App",
"client_name#fr": "Mon Application",
"client_name#ja": "マイアプリ",
"logo_uri": "https://app.example.com/logo.png",
"logo_uri#fr": "https://app.example.com/logo-fr.png",
"tos_uri": "https://app.example.com/terms",
"tos_uri#fr": "https://app.example.com/terms-fr",
"policy_uri": "https://app.example.com/privacy",
"policy_uri#fr": "https://app.example.com/privacy-fr",
"redirect_uris": ["https://app.example.com/callback"],
"grant_types": ["authorization_code"],
"response_types": ["code"]
}

All localized fields are echoed back in the registration response.

Language Tag Rules​

RuleDetails
Valid BCP 47 tagThe tag must be a well-formed BCP 47 language tag (e.g. fr, en-US, zh-Hant). Invalid tags return 400.
Automatic normalizationNon-canonical but valid tags are accepted and normalized to canonical BCP 47 form (e.g. en-us is stored as en-US).
Maximum 20 variants per fieldYou may provide at most 20 language variants for each field. Exceeding this limit returns 400.

Error Responses​

HTTP StatusError CodeCause
400invalid_client_metadataA language tag is not a valid BCP 47 tag (e.g. client_name#not-valid).
400invalid_client_metadataMore than 20 language variants provided for a single field.
400invalid_client_metadataA localized logo_uri, tos_uri, or policy_uri value is not a valid URI.

Explore with AI

ThunderID LogoThunderID Logo

Product

DocsAPIsSDKs
© Copyright Linux Foundation Europe.For web site terms of use, trademark policy and other project policies please see https://linuxfoundation.eu/en/policies.