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_algandid_token_encrypted_response_enc;userinfo_encrypted_response_alganduserinfo_encrypted_response_enc;request_object_signing_alg,request_object_encryption_alg, andrequest_object_encryption_enc; andrequest_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;
queryis 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_metadatabefore 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_errorand 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. |