-
-
Notifications
You must be signed in to change notification settings - Fork 0
Dynamic Client Registration
Dynamic Client Registration (RFC 7591) allows OAuth clients to register themselves programmatically without manual intervention. This is useful for automated deployments and third-party integrations.
---
title: Dynamic Client Registration
---
sequenceDiagram
autonumber
actor Client
participant Authorization Service
participant Data Layer
Client-->>Authorization Service: Calls the registration endpoint with client metadata
Note right of Client: POST /oauth/register
Authorization Service-->Authorization Service: Validates initial access token (if required)
Authorization Service-->Authorization Service: Validates client metadata
Authorization Service-->Authorization Service: Validates software statement (if provided)
Authorization Service-->>Data Layer: Creates new Client record
Authorization Service-->>Client: Returns client credentials and metadata
The Client entity represents a client application that wishes to register with the OAuth provider.
The Authorization Service entity is responsible for validating the registration request and creating the client record. This is provided by TokenAuthority.
The Data Layer entity is a relational database used for storing client registration data.
The client calls the POST /oauth/register endpoint to register a new OAuth client. The endpoint responds with either an error or a JSON object containing the client credentials and echoed metadata.
HTTP Method: POST
URL: /oauth/register
Content-Type: application/json
Authentication: If configured, an initial access token may be required via the Authorization: Bearer <token> header.
Request Body:
| Field | Required? | Description |
|---|---|---|
| redirect_uris | yes | Array of redirect URIs for the client. Must be valid HTTP/HTTPS URLs. |
| token_endpoint_auth_method | no | Authentication method for the token endpoint. Default: client_secret_basic. |
| grant_types | no | Array of grant types the client will use. Default: ["authorization_code"]. |
| response_types | no | Array of response types. Default: ["code"]. |
| 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's 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 email addresses for responsible parties. |
| scope | no | Space-separated list of scopes the client may request. |
| jwks_uri | no | URL of the client's JSON Web Key Set (for private_key_jwt auth). |
| jwks | no | JSON Web Key Set document (for private_key_jwt auth). Mutually exclusive with jwks_uri. |
| software_id | no | Unique identifier for the client software. |
| software_version | no | Version of the client software. |
| software_statement | no | Signed JWT containing client metadata claims. |
Supported Authentication Methods:
| Method | Client Type | Description |
|---|---|---|
none |
Public | No authentication (for public clients). |
client_secret_basic |
Confidential | HTTP Basic authentication with client_id and client_secret. |
client_secret_post |
Confidential | Client credentials in POST body. |
private_key_jwt |
Confidential | JWT assertion signed with client's private key. |
Example Request:
POST /oauth/register
Content-Type: application/json
{
"redirect_uris": ["https://client.example.com/callback"],
"token_endpoint_auth_method": "client_secret_basic",
"grant_types": ["authorization_code", "refresh_token"],
"response_types": ["code"],
"client_name": "My Application",
"client_uri": "https://client.example.com"
}
Example Response:
{
"client_id": "550e8400-e29b-41d4-a716-446655440000",
"client_secret": "dGhpcyBpcyBhIHNlY3JldA==",
"client_id_issued_at": 1695140656,
"client_secret_expires_at": 0,
"redirect_uris": ["https://client.example.com/callback"],
"token_endpoint_auth_method": "client_secret_basic",
"grant_types": ["authorization_code", "refresh_token"],
"response_types": ["code"],
"client_name": "My Application",
"client_uri": "https://client.example.com"
}Response Fields:
| Field | Description |
|---|---|
| client_id | The unique client identifier assigned by the server. |
| client_secret | The client secret (only for confidential clients). |
| client_id_issued_at | Unix timestamp when the client_id was issued. |
| client_secret_expires_at | Unix timestamp when the client_secret expires. 0 means it does not expire. |
All submitted metadata fields are echoed back in the response.
Error Response:
{
"error": "invalid_client_metadata",
"error_description": "redirect_uris contains invalid URI: not-a-url"
}Error Codes:
| Error | Description |
|---|---|
invalid_client_metadata |
The request contains invalid or malformed metadata. |
invalid_software_statement |
The software statement JWT is invalid or cannot be verified. |
unapproved_software_statement |
The software statement is not signed by an approved issuer. |
invalid_token |
The initial access token is invalid or missing (HTTP 401). |
Software statements are signed JWTs that contain pre-approved client metadata. When a software statement is provided, its claims take precedence over request body parameters. This allows software publishers to pre-register their application metadata.
The server can be configured to:
- Accept any software statement (decode only)
- Require software statements signed by specific keys (verify signature)
- Require software statements for all registrations
Dynamic client registration must be enabled in the TokenAuthority configuration:
TokenAuthority.configure do |config|
config.dcr_enabled = true
config.dcr_allowed_token_endpoint_auth_methods = %w[none client_secret_basic client_secret_post private_key_jwt]
config.dcr_allowed_grant_types = %w[authorization_code refresh_token]
endWhen scopes are configured, you can restrict which scopes dynamically registered clients may request:
TokenAuthority.configure do |config|
# Define all available scopes
config.scopes = {
"read" => "Read your data",
"write" => "Create and modify your data",
"admin" => "Administrative access"
}
# Only allow dynamically registered clients to request read and write
config.dcr_enabled = true
config.dcr_allowed_scopes = %w[read write]
endIf dcr_allowed_scopes is not set (nil), dynamically registered clients can request any scope from config.scopes. If set to an empty array, clients cannot register with any scopes.
When a client attempts to register with scopes not in the allowlist, the server returns an invalid_client_metadata error.
Getting Started
- Installation Guide
- MCP Quickstart
- Configuration Reference
- User Authentication
- Protecting API Endpoints
- Customizing Views
- Event Logging
- Instrumentation
Process Flows
- Authorization Code Grant
- Authorization Code Redemption
- Token Refresh
- Token Revocation
- Authorization Server Metadata
- Protected Resource Metadata
- Dynamic Client Registration
- Client Metadata Documents
Development