# Dynamic client registration (RFC 7591)

`POST /register`

Registers a new client and returns its credentials plus a
`registration_access_token` and `registration_client_uri` for managing
the registration via RFC 7592.

## Authorization

- [Initial access token](https://www.nauthera.io/docs/v1/api#auth-initialAccessToken) (`initialAccessToken`)

Authorization header: The access token, as `Bearer <token>` (RFC 6750 §2.1) or, for a sender-constrained token, `DPoP <token>` (RFC 9449 §7.1). A token minted with a `cnf.jkt` confirmation is only accepted together with a `DPoP` proof signed by that key, whichever of the two schemes carries it.

This endpoint is rate limited; it answers `429` when a limit is hit.

## Headers

- `DPoP` (string, optional): DPoP proof JWT (RFC 9449 §4). REQUIRED on a request presenting a DPoP-bound access token, and it must be signed by the key named in the token's `cnf.jkt` and cover this request's method and URI. The proof is read from the request itself rather than from this decoded parameter, so every protected operation is covered whether or not it declares it.

## Request body (`application/json`)

Client metadata for dynamic registration (RFC 7591 §2, with OpenID Connect Dynamic Client Registration extensions).

- `client_id` (string, optional): RFC 7592 §2.2 requires an UPDATE (PUT) body to carry the client's own `client_id`, and the server MUST reject a value that differs from the currently issued one. On registration (POST) it is ignored: a client never influences its own identifier (RFC 9700 §4.15.1). Constraints: at most 256 characters.
- `client_secret` (string, optional): RFC 7592 §2.2: if present on an UPDATE it MUST match the currently issued secret; a client may never overwrite its secret with a value of its own choosing. Ignored on registration. Constraints: at most 512 characters.
- `redirect_uris` (array of strings (uri), optional): Redirection URIs (REQUIRED for redirect-based grants). Constraints: 1–32 items; no duplicates; each item: at most 2048 characters. Example: `["https://app.example.com/callback"]`.
- `token_endpoint_auth_method` (enum, optional): One of: `"none"`, `"client_secret_basic"`, `"client_secret_post"`, `"private_key_jwt"`, `"tls_client_auth"`, `"self_signed_tls_client_auth"`.
- `token_endpoint_auth_signing_alg` (enum, optional): One of: `"ES256"`, `"RS256"`.
- `grant_types` (array of strings, optional): Constraints: at most 16 items; no duplicates; each item: at most 128 characters. Example: `["authorization_code","refresh_token"]`.
- `response_types` (array of strings, optional): Constraints: at most 8 items; no duplicates; each item: at most 64 characters. Example: `["code"]`.
- `client_name` (string, optional): Constraints: at most 256 characters. Example: `"Example App"`.
- `client_uri` (string (uri), optional): Constraints: at most 2048 characters.
- `logo_uri` (string (uri), optional): Constraints: at most 2048 characters.
- `scope` (string, optional): Constraints: at most 2048 characters; pattern `^\S+( \S+)*$`. Example: `"openid profile email"`.
- `contacts` (array of strings (email), optional): Constraints: at most 32 items; no duplicates; each item: at most 256 characters.
- `tos_uri` (string (uri), optional): Constraints: at most 2048 characters.
- `policy_uri` (string (uri), optional): Constraints: at most 2048 characters.
- `jwks_uri` (string (uri), optional): Constraints: at most 2048 characters.
- `jwks` (object, optional): The client's public JWK Set, by value (RFC 7591 §2): the alternative to jwks_uri, and refused alongside it. It is held to the rules a fetched set obeys: a key with private members refuses the whole set, a key this server cannot verify with is ignored, and a set with no usable key is refused.
- `software_id` (string (uuid), optional)
- `software_version` (string, optional): Constraints: at most 128 characters.
- `software_statement` (string, optional): Constraints: at most 16384 characters.
- `application_type` (enum, optional): One of: `"web"`, `"native"`.
- `subject_type` (enum, optional): One of: `"public"`, `"pairwise"`.
- `sector_identifier_uri` (string (uri), optional): Constraints: at most 2048 characters.
- `id_token_signed_response_alg` (string, optional): Constraints: at most 16 characters.
- `userinfo_signed_response_alg` (string, optional): Constraints: at most 16 characters.
- `request_object_signing_alg` (string, optional): Constraints: at most 16 characters.
- `default_max_age` (integer, optional): Constraints: between 0 and 315360000.
- `require_auth_time` (boolean, optional)
- `default_acr_values` (array of strings, optional): Constraints: at most 16 items; no duplicates; each item: at most 256 characters.
- `initiate_login_uri` (string (uri), optional): Constraints: at most 2048 characters.
- `request_uris` (array of strings (uri), optional): Constraints: at most 8 items; no duplicates; each item: at most 2048 characters.
- `post_logout_redirect_uris` (array of strings (uri), optional): Constraints: at most 32 items; no duplicates; each item: at most 2048 characters.
- `backchannel_logout_uri` (string (uri), optional): Constraints: at most 2048 characters.
- `backchannel_logout_session_required` (boolean, optional)
- `frontchannel_logout_uri` (string (uri), optional): Constraints: at most 2048 characters.
- `frontchannel_logout_session_required` (boolean, optional)

## Responses

### 201 Created

The client was registered.

Content type: `application/json`

Headers:
- `Cache-Control` = `no-store`
- `Pragma` = `no-cache`

Client information response (RFC 7591 §3.2.1 + RFC 7592). Echoes the registered metadata and adds the server-issued credentials and management fields.

- `client_id` (string, required): Example: `"nauthera_2Rk9"`.
- `client_secret` (string, optional): Present for confidential clients only.
- `client_id_issued_at` (integer, optional): Example: `1735686000`.
- `client_secret_expires_at` (integer, optional): Epoch seconds, or 0 if the secret never expires. Example: `0`.
- `registration_access_token` (string, optional): RFC 7592 token authorizing management of this registration.
- `registration_client_uri` (string (uri), optional): RFC 7592 management endpoint for this client. Example: `"https://auth.example.com/register/nauthera_2Rk9"`.
- `redirect_uris` (array of strings (uri), optional)
- `token_endpoint_auth_method` (string, optional)
- `token_endpoint_auth_signing_alg` (string, optional)
- `grant_types` (array of strings, optional)
- `response_types` (array of strings, optional)
- `client_name` (string, optional)
- `client_uri` (string (uri), optional)
- `logo_uri` (string (uri), optional)
- `scope` (string, optional)
- `contacts` (array of strings, optional)
- `tos_uri` (string (uri), optional)
- `policy_uri` (string (uri), optional)
- `jwks_uri` (string (uri), optional)
- `jwks` (object, optional): The client's public JWK Set, when it registered one by value.
- `software_id` (string, optional)
- `software_version` (string, optional)
- `application_type` (string, optional)
- `post_logout_redirect_uris` (array of strings (uri), optional)
- `request_uris` (array of strings (uri), optional)
- `id_token_signed_response_alg` (string, optional)
- `userinfo_signed_response_alg` (string, optional)
- `backchannel_logout_uri` (string (uri), optional)
- `backchannel_logout_session_required` (boolean, optional)
- `frontchannel_logout_uri` (string (uri), optional)
- `frontchannel_logout_session_required` (boolean, optional)

### 400 Bad Request

Registration error (RFC 7591 §3.2.2).

Content type: `application/json`

Headers:
- `Cache-Control` = `no-store`
- `WWW-Authenticate` (string)
- `DPoP-Nonce` (string)

A dynamic client registration error (RFC 7591 §3.2.2).

- `error` (enum, required): One of: `"invalid_redirect_uri"`, `"invalid_client_metadata"`, `"invalid_software_statement"`, `"unapproved_software_statement"`, `"invalid_token"`, `"access_denied"`, `"temporarily_unavailable"`.
- `error_description` (string, optional): Example: `"token_endpoint_auth_method is not supported"`.

### 401 Unauthorized

Registration error (RFC 7591 §3.2.2).

Content type: `application/json`

Headers:
- `Cache-Control` = `no-store`
- `WWW-Authenticate` (string)
- `DPoP-Nonce` (string)

A dynamic client registration error (RFC 7591 §3.2.2).

- `error` (enum, required): One of: `"invalid_redirect_uri"`, `"invalid_client_metadata"`, `"invalid_software_statement"`, `"unapproved_software_statement"`, `"invalid_token"`, `"access_denied"`, `"temporarily_unavailable"`.
- `error_description` (string, optional): Example: `"token_endpoint_auth_method is not supported"`.

### 429 Too Many Requests

Too many requests.

Content type: `application/problem+json`

RFC 9457 (formerly RFC 7807) problem details (application/problem+json). `code` is a stable machine-readable slug for the problem kind (it mirrors the last path segment of `type`), so consumers can branch on it without parsing prose. `type` is `https://nauthera.io/problems/<code>`, which resolves to the code's runbook: why it happened, who is affected and what to do (RFC 9457 §3.1.1). A problem without a code has `about:blank`. `errors` is present on validation failures and lists every offending field.

- `title` (string, required): Example: `"Request validation failed"`.
- `status` (integer, required): Example: `400`.
- `type` (string (uri), optional): Example: `"https://nauthera.io/problems/validation_error"`.
- `detail` (string, optional): Example: `"One or more fields did not satisfy the API schema."`.
- `code` (enum, optional): The kind of problem, as a stable machine-readable slug a client branches on and translates rather than parsing `title` or `detail`, which are English prose for logs. A problem whose status says everything the server knows carries no `code`. One of: `"invalid_request"`, `"invalid_credentials"`, `"forbidden"`, `"unauthorized"`, `"insufficient_user_authentication"`, `"protected_account"`, `"password_held_by_directory"`, `"last_role_administrator"`, `"invalid_code"`, `"too_many_requests"`, `"too_many_attempts"`, `"account_locked"`, `"account_unavailable"`, `"already_enrolled"`, `"registration_disabled"`, `"registration_conflict"`, `"captcha_failed"`, `"invalid_reset_link"`, `"password_reused"`, `"password_too_short"`, `"password_too_long"`, `"password_compromised"`, `"invalid_verification_link"`, `"invalid_invitation_link"`, `"mail_relay_missing"`, `"email_unverified"`, `"not_found"`, `"conflict"`, `"unavailable"`, `"busy"`, `"window_too_large"`, `"too_many_series"`, `"internal_error"`, `"rate_limited"`, `"malformed"`, `"body_too_large"`, `"required"`, `"validation_error"`, `"unknown_org"`, `"invalid_token"`, `"invalid_dpop_proof"`, `"use_dpop_nonce"`, `"server_error"`.

  `validation_error` is the one code whose message is not a sentence about
  the code itself: the request failed the schema, and the specifics are
  the `errors` list — one entry per offending field — which a form renders
  on those fields. `required` and `malformed` are spelled like two of the
  per-field violation codes (`FieldError.code`) but mean something else;
  the two vocabularies are told apart by position, never by spelling.

  **Compatibility:** this is a CLOSED enum, with the same cost as
  `AuditAction`: a client generated against an older version of this
  document rejects a problem carrying a code added after it was built.
  Regenerate on upgrade, or decode the field as a plain string. It is
  enumerated anyway so that what a client must translate is knowable
  from the contract alone.
- `instance` (string, optional): Example: `"/introspect"`.
- `request_id` (string, optional): Example: `"8d94ed49-1caf-4ab3-9594-557142f2d023"`.
- `trace_id` (string, optional): Distributed-trace id (hex) for correlating this response with traces and logs. Present only when tracing is enabled and the request was recorded. Opaque and safe to log/share. Example: `"4bf92f3577b34da6a3ce929d0e0e4736"`.
- `errors` (array of objects, optional): Field-level violations (present for validation errors).

  Each item: A single field-level validation violation.
  - `field` (string, required): Offending field, dotted for nested bodies (e.g. child.id). Example: `"token_type_hint"`.
  - `code` (string, required): Machine-readable violation code: required, too_short, too_long, pattern, invalid_format, not_allowed, minimum, maximum, multiple_of, too_few, too_many, not_unique, malformed. Example: `"not_allowed"`.
  - `message` (string, required): Example: `"field token_type_hint must be one of [access_token, refresh_token]"`.

### Error

Registration error (RFC 7591 §3.2.2).

Content type: `application/json`

Headers:
- `Cache-Control` = `no-store`
- `WWW-Authenticate` (string)
- `DPoP-Nonce` (string)

A dynamic client registration error (RFC 7591 §3.2.2).

- `error` (enum, required): One of: `"invalid_redirect_uri"`, `"invalid_client_metadata"`, `"invalid_software_statement"`, `"unapproved_software_statement"`, `"invalid_token"`, `"access_denied"`, `"temporarily_unavailable"`.
- `error_description` (string, optional): Example: `"token_endpoint_auth_method is not supported"`.

## Example request

```bash
curl -X POST 'https://auth.example.com/register' \
  -H "Authorization: Bearer $INITIAL_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "redirect_uris": [
      "https://app.example.com/callback"
    ],
    "token_endpoint_auth_method": "client_secret_basic",
    "grant_types": [
      "authorization_code",
      "refresh_token"
    ],
    "response_types": [
      "code"
    ],
    "client_name": "Example App",
    "scope": "openid profile email"
  }'
```

## Example response (201)

```json
{
  "client_id": "nauthera_2Rk9",
  "client_id_issued_at": 1735686000,
  "client_secret_expires_at": 0,
  "registration_client_uri": "https://auth.example.com/register/nauthera_2Rk9"
}
```
