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). The full RFC 7592 client management protocol (PUT/DELETE on a registration access token) is not supported: registration is one-shot.
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
| Aspect | Behavior |
|---|---|
| Endpoint | POST /oauth2/dcr/register |
| Authentication | Optional, configurable. When enabled, the caller must authenticate as a privileged client. |
| RFC 7592 management | Not supported, no PUT/DELETE on registered clients |
| Localized metadata | Supported via <field>#<bcp47-tag> syntax (see below) |
| Returned credentials | client_id, client_secret (for confidential clients), and the full echoed metadata |
| Discovery field | registration_endpoint in Server Metadata |
Register a Client
Send a JSON body with at minimum a redirect URI and grant type:
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.
{
"client_id": "abc123",
"client_secret": "s3cr3t",
"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
| Field | Required | Description |
|---|---|---|
redirect_uris | Yes | One or more URIs ThunderID redirects to after authentication. |
grant_types | No | OAuth 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_id | No | The organization unit ID to associate the registered client with. If omitted, the client is registered under the root organization unit. |
response_types | No | OAuth 2.1 response types. Specify explicitly. No default is applied. Use code. |
token_endpoint_auth_method | No | How the client authenticates at the token endpoint. Supported values: client_secret_basic (default), client_secret_post, private_key_jwt, none. |
client_name | No | Human-readable name of the client. |
client_uri | No | URL of the client's home page. |
logo_uri | No | URL of the client logo image. |
tos_uri | No | URL of the client's Terms of Service. |
policy_uri | No | URL of the client's Privacy Policy. |
contacts | No | Array of administrator email addresses for this client. |
scope | No | Space-separated list of scopes the client is allowed to request. |
jwks_uri | No | URL 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. |
jwks | No | Inline 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_requests | No | When true, the client must use the /oauth2/par endpoint before starting an authorization flow (RFC 9126). Defaults to false. |
userinfo_signed_response_alg | No | Algorithm used to sign the userinfo response. When set, the userinfo endpoint returns a signed JWT. Supported values: RS256, RS512, PS256, ES256, ES384, ES512, EdDSA. |
userinfo_encrypted_response_alg | No | Key-management algorithm for userinfo response encryption. Supported values: RSA-OAEP, RSA-OAEP-256. |
userinfo_encrypted_response_enc | No | Content-encryption algorithm for userinfo response encryption. Required when userinfo_encrypted_response_alg is set. Supported values: A128CBC-HS256, A256GCM. |
id_token_encrypted_response_alg | No | Key-management algorithm for ID token encryption. Supported values: RSA-OAEP, RSA-OAEP-256. |
id_token_encrypted_response_enc | No | Content-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:
| Field | Description |
|---|---|
client_id | The OAuth 2.1 client identifier assigned by ThunderID. |
client_secret | The client secret. Present only for confidential clients. Store this value securely. ThunderID does not return it again. |
client_secret_expires_at | Expiry time of the client secret as a Unix timestamp. 0 means the secret does not expire. |
app_id | The internal ThunderID application identifier linked to this registered client. |
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>
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
| Rule | Details |
|---|---|
| Valid BCP 47 tag | The tag must be a well-formed BCP 47 language tag (e.g. fr, en-US, zh-Hant). Invalid tags return 400. |
| Automatic normalization | Non-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 field | You may provide at most 20 language variants for each field. Exceeding this limit returns 400. |
Error Responses
| HTTP Status | Error Code | Cause |
|---|---|---|
400 | invalid_client_metadata | A language tag is not a valid BCP 47 tag (e.g. client_name#not-valid). |
400 | invalid_client_metadata | More than 20 language variants provided for a single field. |
400 | invalid_client_metadata | A localized logo_uri, tos_uri, or policy_uri value is not a valid URI. |
Related Guides
- Manage Applications: Register and manage applications from the ThunderID Console
- Application Settings: Configure flows, tokens, and OAuth 2.1 settings
- Client Authentication Methods, pick
token_endpoint_auth_methodat registration time - Server Metadata, advertises the
registration_endpoint