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.

For app-owned tenant sign-in screens, register an organization-owned application then configure the explicit browser-direct allowances through API User/User M7. Follow browser-direct setup for callback origins and mandatory proofs; these policy switches are not additional Dynamic Registration metadata advertised by this page.

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, urn:ietf:params:oauth:grant-type:device_code, and urn:ietf:params:oauth:grant-type:token-exchange. See connection configuration for exchange prerequisites and deployment status.
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.
frontchannel_logout_uri No Optional logout notification endpoint. HTTPS URL, at most 2048 characters, without credentials or a fragment; scheme, host and port must match a registered redirect URI.
frontchannel_logout_session_required No JSON boolean; defaults to false. true requires a configured front-channel URL.
backchannel_logout_uri No Optional logout notification endpoint. HTTPS URL, at most 2048 characters, without credentials or a fragment. A separate host/port and query parameters are permitted.
backchannel_logout_session_required No JSON boolean; defaults to false. true requires a configured back-channel URL.
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 key-based authentication or the response-encryption policy below; mutually exclusive with jwks.
jwks Conditional Inline public JWK Set for key-based authentication or the response-encryption policy below; mutually exclusive 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:

  • 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 authentication 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.

Logout notification metadata configures logout delivery for participating applications. Delivery is deployed and advertised; saving a URL alone does not send notifications or invalidate application sessions. M7 always sends sid on both channels and iss on the front channel, even when the standard session-required booleans are false or absent. They express receiver requirements, not a send/omit option.

User.M7 shows the URL fields under Client → Logout only when editing an existing application, with a separate save action and no session-ID checkboxes. These receivers are independent of post_logout_redirect_uris, which remain under General. Dynamic Registration can supply the notification fields at creation.

Blank or null notification URLs remain unconfigured. False session requirements use sparse storage; supplied flags must be JSON booleans. For each configured channel, the registration response returns the stored URL and effective boolean. Unconfigured channels are omitted. Stored values are checked after policy setup and before/after activation; a mismatch fails registration and triggers cleanup.

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. JARM encryption metadata is configured through the response-encryption policy. Registered response encryption is deployed; see the scoped live evidence in response encryption. 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.

Response-encryption policy

Registration accepts and forwards the following standard metadata to API User. SSO enforces required encryption for ID tokens, JARM and signed UserInfo. The inline-key RS256 browser path passed live login, refresh, UserInfo and cancellation acceptance on 2026-09-16. Confirm receiving SDK support before enabling a required policy. See response encryption for key selection and rollout.

Response Algorithm field Content-encryption field
ID token id_token_encrypted_response_alg id_token_encrypted_response_enc
JARM authorization_encrypted_response_alg authorization_encrypted_response_enc
Signed UserInfo userinfo_encrypted_response_alg userinfo_encrypted_response_enc

For each selected response, set the algorithm to exactly RSA-OAEP-256 and the content-encryption value to exactly A256GCM. Omit both fields to leave that response's policy unset. Nulls, empty strings, partial pairs and other algorithms produce HTTP 400 invalid_client_metadata before client creation. OIDC and JARM default an omitted content-encryption value to A128CBC-HS256; this profile does not implement that default and requires explicit A256GCM. The inner signing algorithm remains independent. UserInfo encryption also requires userinfo_signed_response_alg to be RS256 or RS512.

Supply exactly one standard public source, jwks or jwks_uri. For example, merge this metadata with the usual registration request:

{
  "jwks_uri": "https://app.example.com/.well-known/jwks.json",
  "id_token_encrypted_response_alg": "RSA-OAEP-256",
  "id_token_encrypted_response_enc": "A256GCM"
}

Encryption may be registered with any supported authentication method, including none. Inline encryption keys must be public RSA keys with distinct nonempty kid values, 2048–8192-bit moduli and compatible use, alg and key_ops. Private/symmetric material and encryption-key URL/certificate members are rejected. The inline set is limited to 32 keys and 64 KiB. For private_key_jwt or self_signed_tls_client_auth, a combined inline set must identify every key with use: sig for authentication, enc for encryption. Each subset must satisfy its existing validator. Signing keys in an encryption registration currently require one of those key-based auth methods. A source without any encryption policy retains the existing authentication-only registration rules.

Encryption jwks_uri must use a public DNS HTTPS hostname on port 443 with no credentials, fragment, IP literal or local hostname. Registration checks URL syntax without fetching it. At issuance, SSO fetches through its pinned HTTPS boundary, validates the document and selects the encryption subset.

SSO forwards the public encryption source into API User's separate response_encryption_jwks or response_encryption_jwks_uri configuration and sends the six policy fields unchanged through oauth.clients.set_knobs. Authentication retains its own key configuration. Successful registration returns the standard six fields and jwks or jwks_uri; a mixed inline set is reconstructed from both stored purposes. Internal source aliases are not part of the public registration contract. Missing or changed backend settings produce HTTP 502 server_error and trigger cleanup, with checks before and after activation.

Configure the receiving application with the matching private key and required policy before enabling encryption. Registering a public key does not supply a private key or decryption implementation to your application. The field names follow OIDC Dynamic Client Registration and JARM client metadata.

Connection configuration (M7 extensions)

The following optional top-level fields store the same settings as User.M7's Knobs → Connections panel. Registration stores these settings; the token endpoint enforces them with the live connection and source authorization.

These settings control incoming connections and exchange policy for the registered provider. closed rejects new connections without revoking existing ones; unlisted permits requests by known client ID; discoverable also lists the app's public name and client ID. Approval can be manual, automatic, or automatic for the same registered owner. For organization apps, the owner is the organization itself; the managing users never define the same-site boundary. Personal apps match by their personal owner. Missing settings default to closed, manual approval and exchange disabled.

Field JSON type and accepted values
connection_policy_availability String: closed, unlisted, or discoverable.
connection_policy_approval String: manual, same_site_auto, or auto.
connection_policy_other_sites String: manual or reject; fallback for different owners under same_site_auto.
connection_policy_token_exchange_enabled Boolean. Explicit false is stored and returned.
connection_policy_token_exchange_approval String: manual or connection_policy; records separate approval or inclusion in connection approval.
connection_policy_token_exchange_scopes Array of up to 100 scope strings, at most 256 bytes each, using OAuth scope-token characters. Duplicates are removed. [] leaves the setting absent.
connection_policy_token_exchange_ttl_max Integer from 1 to 2147483647 seconds.

Omit a field to leave it unconfigured. Null, empty strings, string booleans, string TTLs and scope objects are rejected with invalid_client_metadata before client creation. Preferences may be stored while exchange is disabled. The scope ceiling is separate from the client's scope registration metadata; neither it nor an absent ceiling grants resource authority.

{
  "client_name": "Example connection configuration",
  "redirect_uris": ["https://app.example.com/oauth/callback"],
  "token_endpoint_auth_method": "client_secret_basic",
  "grant_types": [
    "authorization_code",
    "urn:ietf:params:oauth:grant-type:token-exchange"
  ],
  "connection_policy_availability": "unlisted",
  "connection_policy_approval": "manual",
  "connection_policy_other_sites": "reject",
  "connection_policy_token_exchange_enabled": true,
  "connection_policy_token_exchange_approval": "manual",
  "connection_policy_token_exchange_scopes": ["files:read"],
  "connection_policy_token_exchange_ttl_max": 600
}

The canonical Token Exchange URI is accepted in the standard grant_types field. It permits the requesting app to use the grant; the provider's exchange policy flag controls incoming exchange and does not add a grant. /token implements the local-user, single-target exchange profile, and both live discovery documents advertise the canonical grant. The consumer exchange and refresh path passed scoped live acceptance on 2026-09-20. In manual exchange-approval mode, the provider must also approve exchange on the particular connection. In connection_policy mode, the active connection supplies that approval. Neither mode expands the user's signed scope authority. An absent exchange scope ceiling permits nothing. The exchange TTL cap limits both access and refresh lifetimes; refresh still requires explicit authorized offline_access. Existing HTTPS redirect-URI and application-type requirements still apply, including registrations containing only the exchange grant.

Successful responses include these fields from the saved configuration and the saved grant list. SSO checks storage after saving policy, after client authentication setup and after activation. Missing or changed values cause HTTP 502 server_error and trigger deletion of the incomplete client, so a backend that drops the new fields cannot report successful registration.

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.

Register consumer organization access

Register orgs in the consumer client's permitted scope set, request it explicitly and obtain hosted consent before calling consumer org discovery. Registration alone does not add the scope to existing tokens. The same registered UserInfo signing/encryption policy governs /userinfo/orgs; there is no per-request format override or separate org response-policy flag. Tenant groups and primary-email email remain separate permissions. Device authorization requires hosted device consent before granting orgs.