Dynamic client registration

POST https://sso.user.m7.org/register
Content-Type: application/json
Authorization: Bearer ACCESS_TOKEN

Dynamic registration creates an active personal OAuth client for the M7 principal represented by the access token, unless the request explicitly selects an organization that principal may manage. It is suitable for developer tooling and self-service application setup. First complete a user sign-in to obtain that access token, then call /register. Use the M7 User API to manage a registered client's later configuration, status, or deletion.

The endpoint uses the JSON request/response format defined by OAuth Dynamic Client Registration (RFC 7591) and OpenID Connect Registration. M7-specific metadata is identified below; response_modes is an optional M7 extension, distinct from the standard response_types registration field.

The request must be JSON and must use an active M7 access token. The current registration contract uses Authorization: Bearer ACCESS_TOKEN; requests without a valid token and any binding material required by that credential fail with invalid_token. The standard dpop_bound_access_tokens client metadata controls the new client's DPoP requirement independently of the credential used to authorize registration. See the project-specific DPoP status.

Minimal public client

The minimal request creates a public web client enabled for authorization code flow:

curl --fail-with-body --silent --show-error \
  --request POST 'https://sso.user.m7.org/register' \
  --header 'Authorization: Bearer ACCESS_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{
    "client_name": "Example App",
    "redirect_uris": ["https://app.example.com/oauth/callback"],
    "token_endpoint_auth_method": "none"
  }'

M7 requires at least one HTTPS redirect_uris entry. Redirect URIs are exact values, not patterns. The registration endpoint does not permit HTTP loopback or custom-scheme exceptions; use a registered HTTPS redirect URI.

Request metadata

Field Required Rules
redirect_uris Yes Array of one or more unique HTTPS URLs.
client_name No Display name. Defaults from the first redirect URI host when omitted.
application_type No web (default), native, or spa.
response_types No Omit or set to ['code']; no other response type is supported.
response_modes No M7 extension: JSON array of authorization response-mode names. Omission or [] registers ["query"]. See Response modes.
authorization_signed_response_alg No Standard JARM metadata: exactly RS256 or RS512; omission defaults to RS256. The effective value is returned. It is independent of ID-token, UserInfo and client-authentication algorithms.
grant_types No Defaults to ['authorization_code']. Allowed values: authorization_code, refresh_token, client_credentials, and urn:ietf:params:oauth:grant-type:device_code.
scope No Space-delimited string of allowed scopes. Scope names are case-sensitive; malformed or non-string values are rejected.
tenant No Organization UUID. Omit it for personal ownership. When supplied, the authenticated principal must be allowed to manage that active, non-archived organization.
token_endpoint_auth_method No client_secret_basic (default), none, client_secret_post, client_secret_jwt, private_key_jwt, tls_client_auth, or self_signed_tls_client_auth. Request none explicitly for a public client.
client_secret No Optional caller-selected secret for a secret-based method or HS256/384/512 token signing. M7 generates one when omitted; HMAC needs at least 32/48/64 bytes respectively.
post_logout_redirect_uris No Array of exact HTTPS URLs allowed for end-session return.
initiate_login_uri No Application's HTTPS login-start URL. M7 can use it after an expired PAR request.
default_max_age No Non-negative integer default authentication age in seconds.
require_auth_time No Boolean; defaults to false. When true, every ID token for the client must include the standard auth_time claim. This does not itself force reauthentication.
id_token_signed_response_alg No Available for issuance: RS256, RS384, RS512, PS256, PS384, PS512, ES256, ES384, ES512, ES256K, Ed25519, Ed448, ML-DSA-44, ML-DSA-65, ML-DSA-87. Omission defaults to RS256; spelling is normalized and the effective value is returned. HS256, HS384, and HS512 are also supported with a recoverable client secret; see HMAC setup.
userinfo_signed_response_alg No RS256 or RS512. When present, /userinfo returns a signed JWT using that algorithm. Omission retains ordinary JSON.
dpop_bound_access_tokens No Boolean; defaults to false. true requires a valid DPoP proof on every token request. false or omission still permits a client to request an opportunistically DPoP-bound token by sending a valid proof.
token_endpoint_auth_signing_alg Conditional Signing algorithm for client_secret_jwt or private_key_jwt. Omission selects HS256 or RS256, respectively.
jwks_uri Conditional Public HTTPS JWKS URL for private_key_jwt or self_signed_tls_client_auth; cannot contain credentials or be combined with jwks.
jwks Conditional Inline JSON JWK Set for private_key_jwt, or certificate-bearing keys for self_signed_tls_client_auth; cannot be combined with jwks_uri.
tls_client_auth_subject_dn Conditional Exact certificate subject DN selector for tls_client_auth; use exactly one subject-DN or SAN selector.
tls_client_auth_san_dns Conditional Exact DNS SAN selector for tls_client_auth.
tls_client_auth_san_uri Conditional Exact URI SAN selector for tls_client_auth.
tls_client_auth_san_ip Conditional Exact IP-address SAN selector for tls_client_auth.
tls_client_auth_san_email Conditional Exact email SAN selector for tls_client_auth.
tls_client_certificate_bound_access_tokens Conditional Boolean valid only with an mTLS authentication method; defaults to false. When true, issued access and refresh tokens are bound to the presented leaf certificate.

Select an ID-token algorithm that is advertised by the live discovery document and supported by the relying party's verifier. Both discovery documents advertise all 18 profiles: 15 asymmetric plus HS256/384/512. This does not upgrade existing SDKs. Updated M7 token-verifier source can use the optional m7crypto backend, but current Web SDK full-token cookies are unsuitable for general ML-DSA use; check SDK/key and storage compatibility before changing an existing web app. The signing choice is independent of userinfo_signed_response_alg, client-authentication assertions, and JAR.

Registration does not grant a resource audience. Before the client can obtain tokens, an administrator must configure at least one exact allowed_audiences value for it. When several audiences are configured, each grant may request any nonempty subset; omission selects the complete configured set where the grant permits omission.

Organization ownership is authorized again during draft creation, authentication setup, and activation. Registration fails closed if the organization is missing, inactive, archived, or no longer manageable by the authenticated principal. The successful response includes tenant for an organization-owned client and omits it for a personal registration.

Dynamic registration supports only web, native, and spa application types. It cannot create an application_type=machine client; create and manage machine applications through the M7 User API instead.

The registration request also accepts these optional client metadata fields: client_uri, logo_uri, policy_uri, tos_uri, contacts, subject_type, sector_identifier_uri, default_acr_values, software_id, and software_version.

The following deferred protocol metadata is deliberately rejected with invalid_client_metadata before client creation or modification:

  • id_token_encrypted_response_alg and id_token_encrypted_response_enc;
  • userinfo_encrypted_response_alg and userinfo_encrypted_response_enc;
  • request_object_signing_alg, request_object_encryption_alg, and request_object_encryption_enc; and
  • request_uris.

Signed JAR is supported separately at /authorize for clients with an enabled JAR Request Policy, configured through the M7 account application's OAuth client settings. Dynamic Registration does not configure that policy: the standard Request Object metadata listed above remains rejected. JAR signing keys are distinct from this endpoint's token-authentication jwks or jwks_uri. See Signed Request Objects (JAR).

An M7-issued PAR request_uri remains valid at /authorize independently of JAR policy.

URL-bearing metadata must be valid URLs. jwks_uri and the URL configuration fields that represent security metadata must use HTTPS.

Authentication metadata is validated as one closed combination before client creation. client_secret_jwt accepts only HS256, HS384, or HS512 and requires a usable secret. private_key_jwt accepts only RS256, RS384, RS512, or ES256 and requires exactly one of jwks_uri or jwks. JWT-only fields on none, Basic, or POST registrations, incompatible algorithms and incomplete key sources are rejected with invalid_client_metadata without leaving an active client. A client_secret on private-key JWT or mTLS is permitted when HS token signing is selected; it serves HMAC and does not change the authentication method. SSO rejects HS token signing with none as invalid_client_metadata before creating a registration in the locally tested eligibility update described below; production verification of that update remains pending.

tls_client_auth requires exactly one of the five subject-DN/SAN selector fields. self_signed_tls_client_auth requires exactly one of jwks_uri or jwks, and each usable key must represent a self-signed certificate through its public x5c certificate chain. Private JWK members are rejected. The certificate-bound-token boolean is valid only with one of these two methods. After registration, use the mTLS operation aliases published by discovery; the issuer and browser authorization endpoint do not change.

Before registering client_secret_jwt, configure the same M7_OAUTH_CLIENT_SECRET_JWT_KEYS value on API User and SSO. HMAC token signing also requires the same ring on ID.M7. New secrets are encrypted with the first 64-character hexadecimal key; remaining keys permit rolling decryption. A legacy password-hash row is deliberately not accepted as an HMAC key and requires secret reissue.

HMAC token signing

Select HS256, HS384 or HS512 in id_token_signed_response_alg. In M7 this preference also controls application access and refresh token signing. When editing an existing app, first configure and save confidential-client authentication on the Auth page. Then save the HMAC algorithm and set/reset its secret on the Auth page or through API User's POST /api/v2/oauth/clients/set_auth. Dynamic Registration instead accepts a suitable supplied secret or generates one, returning it once in the successful response.

Use randomly generated original secrets of at least 32 bytes for HS256, 48 for HS384 or 64 for HS512. The same value serves HMAC and Basic, POST or secret-JWT authentication when both apply. With private-key JWT or mTLS, the stored HMAC secret does not change the authentication method. Public clients using none must choose an asymmetric signing algorithm. A browser or native application does not become confidential merely by embedding a static secret.

The 2026-09-16 eligibility restriction is implemented and locally tested; production deployment verification remains pending. User.M7 prevents the none/HMAC combination using saved settings, while keeping an existing invalid selection visible for repair. Dynamic Registration returns HTTP 400 invalid_client_metadata; supplying a secret does not make none eligible. SSO returns HTTP 400 with unauthorized_client at PAR, device authorization and /token, including code pickup and refresh, before consuming grant artifacts. Existing invalid records require an explicit configuration repair; there is no automatic algorithm change or credential revocation. To change an HMAC client to none, first choose and save an asymmetric algorithm. Shared secret storage, signing services and the advertised algorithm inventory remain available.

Direct API User management retains the underlying storage support and can still save this combination; it does not make the configuration eligible at SSO. For an existing public client, save an asymmetric algorithm and start a new sign-in where a new signing lineage is needed. Configure confidential authentication only when the actual client can protect its credentials; changing the method label alone does not establish confidentiality. This restriction does not revoke previously issued credentials or replace their normal expiry and revocation controls.

HMAC requires recoverable encrypted storage. Existing Basic/POST password hashes cannot be converted into signing keys. Missing, empty, hashed, unreadable or undersized secrets fail before issuance; save a suitable secret before retrying. M7 never downgrades to another algorithm. A pre-existing personal asymmetric refresh lineage cannot switch families in place; start a new sign-in after changing it to HMAC. Root credentials remain asymmetric.

HMAC has no public certificate or PEM/JWK/JWKS lookup. Never expose the secret to browser JavaScript. A secret holder can both verify and create MACs, so local MAC validation alone does not prove provider issuance or active state. M7 additionally checks the issued token's recorded hash; resource consumers must retain authoritative issued-token/state checks, such as applicable introspection, plus claims, scope, token-class and sender constraints. Retain an old secret while accepting old tokens after rotation. ID tokens are relying-party assertions, not API credentials; refresh tokens remain opaque client state.

HS256/384/512 passed client-credentials, consumer device-code/strict-refresh and browser authorization-code/PAR login, profile and strict-refresh campaigns in September 2026. Browser tests included PKCE and DPoP. Basic/Post introspection, revocation and temporary-client cleanup passed. The historical none device case verified signing mechanics; the eligibility restriction above now excludes that combination and preserves confidential-client HMAC. This does not claim every authentication method, refresh mode or live secret rotation, nor formal OpenID conformance.

The Web SDK uses authenticated SSO introspection and passed without m7crypto. Current Token/PHP source supports local HMAC through explicit hmac_secret and the native extension; existing immutable SDK ZIPs are unchanged. See SDK HMAC guidance. Both live discovery documents advertise these HMAC algorithms with the 15 asymmetric profiles, verified at 2026-09-15 21:49:24 UTC. See HMAC and discovery; clients must follow live metadata for automatic negotiation. UserInfo, JARM, JAR, DPoP and client-authentication assertions retain their separate algorithm lists.

Response modes (M7 extension)

Register one or more modes through the same JSON request:

{
  "client_name": "Example App",
  "redirect_uris": ["https://app.example.com/oauth/callback"],
  "response_types": ["code"],
  "response_modes": ["query", "form_post"],
  "token_endpoint_auth_method": "none"
}

The registration inventory is query, form_post, fragment, query.jwt, form_post.jwt, fragment.jwt, jwt, web_message, web_message.opener, and web_message.parent. Names are trimmed, lowercased and deduplicated.

  • Omission or an empty JSON array registers query.
  • A nonempty list replaces that default; query is not added automatically to a list such as ["form_post"].
  • The field must be a JSON array of strings. Null, strings (including newline-separated text), objects, nested arrays, non-string entries, blank names and unknown mode names produce invalid_client_metadata before creating a client.
  • The successful response includes the modes actually saved by API.USER, in stored lexical order. The endpoint verifies that this set matches the normalized request before activation and again after activation. Missing, malformed or changed backend metadata produces server_error and invokes registration cleanup.

All ten modes are implemented and advertised by live discovery. Register only the modes the application can securely receive; each authorization request selects one permitted mode. response_mode (singular) belongs to an individual authorization request; response_modes (plural) is this M7 registration extension. Other providers are not required to understand this extension.

For signed JARM, register a JWT mode and set authorization_signed_response_alg to RS256 or RS512. The default is RS256 even when the field is omitted. Unsupported algorithms, none, null and malformed values are rejected as invalid_client_metadata. authorization_encrypted_response_alg and authorization_encrypted_response_enc are also rejected; encrypted JARM is not supported. See response delivery and validation.

Web Message registration does not grant iframe permission. The embedding origin also needs the provider's explicit framing allowance, and the client must satisfy the browser requirements.

Confidential-client examples

Client secret

{
  "client_name": "Example server app",
  "redirect_uris": ["https://app.example.com/oauth/callback"],
  "grant_types": ["authorization_code", "refresh_token"],
  "token_endpoint_auth_method": "client_secret_basic"
}

M7 returns client_secret once in the successful registration response if the selected authentication method or HMAC token-signing choice needs one. Store it immediately in a secret manager; do not expect the plaintext secret to be retrievable later.

Private-key JWT

{
  "client_name": "Example private-key client",
  "redirect_uris": ["https://app.example.com/oauth/callback"],
  "token_endpoint_auth_method": "private_key_jwt",
  "token_endpoint_auth_signing_alg": "ES256",
  "jwks_uri": "https://app.example.com/.well-known/jwks.json"
}

For private_key_jwt, M7 selects the assertion key by kid from either the registered inline JWKS or the registered HTTPS JWKS URL. The assertion must use a configured supported signing algorithm and satisfy the token-endpoint client assertion checks described in Token endpoint.

Successful response

M7 responds with HTTP 201, standard registration metadata and the M7 response_modes extension:

{
  "client_id": "CLIENT_ID",
  "client_id_issued_at": 1786309200,
  "application_type": "web",
  "client_name": "Example App",
  "redirect_uris": ["https://app.example.com/oauth/callback"],
  "response_types": ["code"],
  "response_modes": ["query"],
  "grant_types": ["authorization_code"],
  "token_endpoint_auth_method": "none",
  "dpop_bound_access_tokens": false
}

The response includes the submitted optional metadata that M7 accepted. A secret-based or HMAC-signing registration additionally includes client_secret and client_secret_expires_at (currently 0 for no fixed expiry).

Common errors

Error Typical cause
invalid_token Missing, expired, revoked, or incorrectly bound bearer token.
invalid_client_metadata Body is not a JSON object, has unsupported or malformed metadata (including response_modes), contains a malformed tenant, or selects an organization the principal cannot currently manage.
invalid_redirect_uri Missing, malformed, non-HTTPS, or duplicate-invalid redirect URI data.
server_error Registration could not reach or complete its management backend.