Dynamic Client Registration
Authagonal implements OAuth 2.0 Dynamic Client Registration (RFC 7591), allowing client applications to register themselves at runtime without administrator involvement.
Enabling the Endpoint
Dynamic registration is disabled by default. Opt in via configuration:
{
"Auth": {
"DynamicClientRegistrationEnabled": true
}
}
Or set Auth__DynamicClientRegistrationEnabled=true as an environment variable.
When enabled, the discovery document advertises the endpoint:
GET /.well-known/openid-configuration
{
"registration_endpoint": "https://auth.example.com/connect/register"
}
Registering a Client
POST /connect/register
Content-Type: application/json
{
"client_name": "My App",
"redirect_uris": ["https://myapp.example.com/callback"],
"post_logout_redirect_uris": ["https://myapp.example.com/"],
"grant_types": ["authorization_code", "refresh_token"],
"token_endpoint_auth_method": "client_secret_basic",
"scope": "openid profile email offline_access",
"audiences": ["https://api.myapp.example.com"],
"allowed_cors_origins": ["https://myapp.example.com"],
"backchannel_logout_uri": "https://myapp.example.com/oidc/backchannel",
"frontchannel_logout_uri": "https://myapp.example.com/oidc/frontchannel",
"frontchannel_logout_session_required": true
}
Response
HTTP/1.1 201 Created
Content-Type: application/json
{
"client_id": "a1b2c3d4e5f6...",
"client_secret": "xkCd2_base64url...",
"client_id_issued_at": 1745000000,
"client_secret_expires_at": 0,
"client_name": "My App",
"redirect_uris": ["https://myapp.example.com/callback"],
"post_logout_redirect_uris": ["https://myapp.example.com/"],
"grant_types": ["authorization_code", "refresh_token"],
"response_types": ["code"],
"scope": "openid profile email offline_access",
"token_endpoint_auth_method": "client_secret_basic"
}
The client_secret is returned once and cannot be retrieved later. Store it securely.
Request Parameters
| Parameter | Required | Notes |
|---|---|---|
client_name |
no | Defaults to the generated client_id if omitted |
redirect_uris |
conditional | Required when grant_types contains authorization_code. Must be absolute URIs; javascript:/data:/vbscript:/file: schemes are rejected (native custom schemes for mobile deep links are fine). |
post_logout_redirect_uris |
no | Valid redirect targets after logout |
grant_types |
no | Defaults to ["authorization_code"]. Only authorization_code and refresh_token are registrable: client_credentials, implicit, device and any other grant type are rejected with invalid_client_metadata, so open registration can never mint a machine-to-machine client. refresh_token is added automatically if offline_access is requested. |
token_endpoint_auth_method |
no | client_secret_basic (default), client_secret_post, or none for public clients |
scope |
no | Space-separated scopes, must all be built-in or previously registered (see Scopes). The administrative scope (AdminApi:Scope, default authagonal-admin) can never be registered. |
audiences |
no | JWT aud values added to access tokens |
allowed_cors_origins |
no | Origins permitted to call the token endpoint from a browser |
backchannel_logout_uri |
no | Enables Back-Channel Logout |
frontchannel_logout_uri |
no | Enables Front-Channel Logout |
frontchannel_logout_session_required |
no | Defaults to true; when true, the logout URL carries iss and sid parameters |
Defaults & Invariants
- PKCE required:
RequirePkceis alwaystruefor dynamically registered clients. - Public clients:
token_endpoint_auth_method: "none"produces a client without a secret. PKCE is still required. - Offline access: requesting scope
offline_accessimplicitly addsrefresh_tokentogrant_types.
Error Responses
| HTTP | error |
Cause |
|---|---|---|
400 |
invalid_redirect_uri |
One of redirect_uris is not a valid absolute URI, or uses a script/data/file pseudo-scheme |
400 |
invalid_client_metadata |
A non-registrable grant type was requested, or redirect_uris is missing for a grant type that requires it |
400 |
invalid_scope |
A requested scope is neither built-in nor registered |
403 |
invalid_scope |
The administrative scope was requested, it can never be granted through registration |
403 |
not_supported |
Dynamic client registration is not enabled |
429 |
rate_limited |
Too many registrations from this IP (10 per hour) |
Security Considerations
The registration endpoint is unauthenticated, but constrained by design:
- Rate limited: 10 registrations per IP per rolling hour (
429 rate_limited), so the client store can’t be flooded. - Grant types restricted: only
authorization_code+refresh_token; a registered client always requires a user-mediated flow and can never act as a machine-to-machine client. - Admin scope reserved: the
authagonal-adminscope (or whateverAdminApi:Scopeis set to) is refused, so registration can never produce a client that reaches the admin API. - PKCE always required on registered clients.
For stronger gating (initial access tokens, mTLS, software statements), front the endpoint with your own middleware or an IAuthHook. Consider disabling dynamic registration entirely and managing clients via the admin API in environments where self-service registration is not a requirement.