M7 OAuth 2.0 and OpenID Connect Provider Profile

Status: Release-candidate public contract
Profile version: M7-OIDC-1.0-RC4
Last reviewed: 2026-09-16

This profile defines the interoperable OAuth 2.0 and OpenID Connect behavior that an external M7 client can rely on. It consolidates the choices M7 makes where the underlying standards permit several behaviors. The endpoint guides linked from this page remain the detailed request and response references.

This is a release-candidate profile, not an OpenID certification claim. The independent-library, RFC 8705 mTLS, and scoped JAR interoperability campaigns have passed; the official OpenID conformance-suite run remains pending.

The server supports all 15 asymmetric signing profiles below. Shared issuance and personal Device Authorization/strict-refresh flows passed live algorithm tests; normal browser sign-in and refresh were confirmed by the operator. After deploying the updated Identity SDK, the operator also confirmed Ed25519 browser sign-in and refresh. This does not claim a separate authorization-code campaign for every algorithm or ML-DSA browser-cookie compatibility.

HS256/384/512 client-secret signing additionally passed client credentials, device/strict-refresh and browser login/profile/refresh. See HMAC setup and acceptance boundaries. Both live discovery documents advertise these three algorithms alongside the 15 asymmetric profiles, for 18 total.

HMAC ID tokens are restricted to confidential clients. The User.M7/SSO update rejecting token_endpoint_auth_method=none with HMAC is implemented and locally tested; production deployment verification remains pending. The earlier public-device HMAC test is historical signing evidence, not an eligible configuration under this policy. See the setup guide above for errors, existing-client repair and the retained shared-storage boundary.

At 2026-09-15 21:49:24 UTC, both live discovery documents returned identical metadata with all 18 ID-token algorithms. Provider JWKS covered all 15 asymmetric profiles and contained no symmetric material. HMAC discovery is deployed and advertised. Consumers MUST use the live advertisement when selecting automatically; see HMAC and discovery.

All ten authorization response modes and JARM RS256/RS512 signing are deployed and advertised, verified from both discovery documents at 2026-09-15 06:24 UTC. Direct authorization, PAR and cancellation passed live for each mode, including both flexible Web Message presentations. These checks do not extend the certification, encryption or iframe account-reuse claims below.

The key words MUST, MUST NOT, SHOULD, and MAY describe the M7 integration contract. They do not broaden an application's registered grants, scopes, audiences, or ownership.

Profile identity and discovery

Item Contract
Authorization-server issuer https://sso.user.m7.org
OpenID discovery https://sso.user.m7.org/.well-known/openid-configuration
OAuth authorization-server metadata https://sso.user.m7.org/.well-known/oauth-authorization-server
Subject type public
Authorization response code; query, form POST, fragment, signed JARM and Web Message modes
Profile version M7-OIDC-1.0-RC4

Clients MUST use discovery for endpoint locations and advertised capability sets. This profile defines behavior and constraints that cannot be represented completely in discovery metadata. Automatic negotiation MUST treat an absent advertisement as unavailable. Explicitly configured HMAC clients use the separate shared-secret contract linked above. Account-level two-factor settings have no discovery flag; their hosted behavior is described separately below.

M7 publishes asymmetric signing keys at the discovered jwks_uri. For those profiles, clients MUST select a key by the signed JWT's exact kid and supported alg, verify the signature, and then validate the token-class claims. They MUST NOT trust a decoded JWT without verification.

Issuer rules by token class

Token or response Issuer rule
New ID token Exact discovery issuer, https://sso.user.m7.org
Migrating ID token The documented transitional allowlist may also contain legacy id.m7.org
Signed UserInfo response Exact discovery issuer
Signed JARM authorization response Exact discovery issuer; audience is the public OAuth client ID
Web Message response iss equals the exact discovery issuer; browser sender origin and window must also match
Current M7 access token Current token-profile issuer id.m7.org; do not substitute the discovery issuer

The legacy ID-token issuer is a migration compatibility rule, not a suffix or host-matching rule. New integrations SHOULD use the canonical discovery issuer and MUST reject unrelated issuer values.

Supported integration shapes

Need Supported profile
Browser, web, SPA, or native user sign-in Authorization code with PKCE S256
CLI, TV, or input-constrained user sign-in OAuth Device Authorization Grant
Service acting as itself client_credentials with a confidential client
Long-lived user authorization Rotating refresh-token lineage when refresh_token is registered and granted
Back-channel authorization request Pushed Authorization Requests (PAR)
Signed authorization request Per-client JAR by value or registered HTTPS URI at /authorize

M7 does not support implicit or hybrid response types. A client MUST use response_type=code. PKCE is required for authorization-code flow, and the only accepted method is exact S256; plain is not supported.

The application MUST create high-entropy, single-use state and SHOULD create a single-use OpenID Connect nonce. It MUST validate callback state and the requested nonce in the ID token before establishing an application session.

Authorization response delivery

The supported response modes are query, form_post, fragment, query.jwt, form_post.jwt, fragment.jwt, jwt, web_message.opener, web_message.parent, and web_message. A client MAY register several through the M7 response_modes extension, but MUST select one permitted mode per request. Omission selects query; it does not select the first allowed mode. An unsupported or disallowed value fails without a fallback transport. PAR and JAR MUST retain the selected mode in their bound transaction.

Signed JARM MUST use the client's authorization_signed_response_alg, exactly RS256 or RS512 with RS256 as the default. The client MUST validate signature, provider issuer, client-ID audience, expiry and single-use state before acting on the enclosed code or error. JARM is signed, readable and not encrypted. Its signing policy is independent of ID tokens, UserInfo, JAR and client authentication. jwt uses query delivery for the supported code response.

Web Message clients MUST validate the provider origin, exact popup/frame window, message issuer and state, and accept only one result. Explicit modes use only the named opener or parent. Generic mode prefers the opener, then uses the parent only when an opener is absent. Invalid or closed receivers MUST NOT trigger a fallback transport.

Iframe use additionally requires an explicit provider framing allowance for the embedding origin and compatible client CSP/browser policies. Client registration alone does not authorize framing. Current live acceptance covers fresh password sign-in and cancellation from approved iframe origins; it does not establish remembered-session reuse or iframe MFA, email, federation or consent-denial coverage. See response modes for the complete delivery table, registration, SDK and browser requirements.

Consumer email sign-in

M7 supports a magic link and a one-time code in one email for an existing consumer account with a unique active verified address. Both share one-use proof and require the browser that requested the email. Link landing displays the requested address and requires explicit confirmation. Applications MUST use the normal hosted authorization-code flow and callback contract. The account's independent Email sign-in two-factor setting applies to both methods. See email sign-in.

Consumer two-factor authentication

Consumer accounts MAY enable independent TOTP checks for regular local password login, email sign-in, local-password Device Code approval, and remembered-account or existing-session authorization. Account/session intervals are Every time, 15 minutes, one hour, or eight hours. A completed proof is reusable only within the appropriate account, browser, authenticator, and authorization context.

Fresh federated authentication follows the provider's policy; later reuse of the remembered M7 account follows the account/session setting. Refresh-token requests and machine credentials do not prompt for these codes. Applications MUST continue using M7's hosted authorization pages, preserve the normal OAuth transaction, and handle a due prompt=none check as login_required with error_reason=account_two_factor_required.

Recovery codes are single-use. M7's hosted recovery option offers full authenticator unlink by default and always unlinks on use of the final code. The settings page also supports TOTP, recovery-code, and eligible linked-account unlink. These account features do not add a discovery flag or a new amr/acr assurance guarantee. See two-factor authentication and recovery.

Client authentication

Each client has one registered token-endpoint authentication method. The same method applies wherever that method is advertised for the invoked endpoint.

Method Token PAR Device authorization Introspection Revocation
none Yes Yes Yes No Yes
client_secret_basic Yes Yes Yes Yes Yes
client_secret_post Yes Yes Yes Yes Yes
client_secret_jwt Yes Yes Yes Yes Yes
private_key_jwt Yes Yes Yes Yes Yes
tls_client_auth Yes Yes Yes Yes Yes
self_signed_tls_client_auth Yes Yes Yes Yes Yes

none identifies a public client with its exact client_id; it is not anonymous access. A public client cannot use client_credentials or introspection.

For client_secret_basic, the client identifier and secret are application/x-www-form-urlencoded before Basic construction, as required by the OAuth client-password contract. Clients SHOULD use a standards-compliant library rather than constructing the header ad hoc.

For client_secret_jwt and private_key_jwt:

  • iss and sub MUST both equal the exact client_id;
  • aud MUST contain either the canonical issuer https://sso.user.m7.org or the exact invoked endpoint URL;
  • iat and unexpired exp MUST be current;
  • jti MUST be nonempty and unique; and
  • a replayed, stale, incorrectly signed, or otherwise invalid assertion is rejected as invalid_client.
Assertion method Algorithms
client_secret_jwt HS256, HS384, HS512
private_key_jwt RS256, RS384, RS512, ES256

Mutual-TLS client authentication

M7 supports both RFC 8705 authentication methods. A client using tls_client_auth presents a certificate that chains to an M7-trusted client CA and matches exactly one registered subject-DN or SAN selector. A client using self_signed_tls_client_auth presents the exact self-signed certificate represented by one key in its registered inline jwks or HTTPS jwks_uri.

The canonical issuer remains https://sso.user.m7.org. Clients discover and use the dedicated mtls_endpoint_aliases when presenting a client certificate:

Operation mTLS endpoint alias
Token https://mtls-sso.user.m7.org/token
PAR https://mtls-sso.user.m7.org/par
Device authorization https://mtls-sso.user.m7.org/device_authorization
Introspection https://mtls-sso.user.m7.org/introspect
Revocation https://mtls-sso.user.m7.org/revoke
UserInfo https://mtls-sso.user.m7.org/userinfo

The ordinary authorization endpoint, discovery documents, registration endpoint, and provider JWKS remain on the canonical SSO origin.

For either mTLS authentication method, the registration value tls_client_certificate_bound_access_tokens=true requires certificate-bound access and refresh tokens. M7 records the presented leaf certificate's SHA-256 thumbprint as cnf."x5t#S256". Token refresh MUST present the same certificate, and a protected-resource request MUST present the exact bound certificate over mutual TLS; the token cannot be downgraded to bearer use. When a token carries both DPoP and certificate confirmation members, both sender constraints apply.

Provider-signed responses

Response Formats and algorithms
ID token Signed JWT using a configured asymmetric profile or, for confidential clients, HS256/384/512 with a recoverable client secret; registration default is RS256
UserInfo JSON by default, or signed JWT using registered RS256 or RS512
Provider JWKS RSA, EC, OKP, and AKP signing keys selected by exact kid, matching alg, and algorithm-appropriate key parameters

For a signed UserInfo response, the client MUST validate the JWS, canonical issuer, its own client_id as audience, time claims, and exact sub equality with the already validated ID token.

Signing algorithm selection

Family ID-token signing algorithms Public JWK type
RSA PKCS#1 v1.5 RS256, RS384, RS512 RSA
RSA-PSS PS256, PS384, PS512 RSA
ECDSA ES256, ES384, ES512, ES256K EC
Edwards Ed25519, Ed448 OKP
ML-DSA ML-DSA-44, ML-DSA-65, ML-DSA-87 AKP
HMAC (confidential clients) HS256, HS384, HS512 None: shared client secret, never published

Clients select id_token_signed_response_alg during registration; omission continues to default to RS256. The protected alg MUST match the configured algorithm and the selected verification key. HMAC requires the original client secret and authoritative issued-token/state checks for API credentials. There is no automatic downgrade or fallback to a different signing profile. UserInfo, client-authentication assertions, JAR and DPoP retain their separate algorithm lists.

A third-party OIDC/JOSE client MAY use a supported profile when its verifier supports that exact algorithm and key type. The generic EdDSA name is not an alias for M7's Ed25519 or Ed448 values. Support for those explicit names and for AKP/ML-DSA must be checked in the consumer's library.

Current-source M7 Identity SDK asymmetric verification supports the 15 public-key profiles through its existing PHP paths and optional m7crypto backend, with an explicit allowlist and compatible public-key resolution. Missing native support MUST fail rather than bypass signature verification. Published SDK ZIPs remain at their existing release capabilities until a new artifact includes the change. Local HMAC verification additionally uses explicit hmac_secret and m7crypto; the Web SDK session path uses authenticated introspection.

ML-DSA works in the tested device/native paths, but the current Web SDK's full-token cookies cannot generally accommodate it. Clients using that browser session integration MUST select a compact supported profile until storage and transport are revised and tested. See post-quantum storage limits and signing profiles and key formats.

Authorization requests, PAR, and JAR

An authorization request MUST use an exact registered HTTPS redirect URI, response_type=code, PKCE S256, and a nonempty allowed resource-audience set. M7 performs exact redirect matching; it does not support wildcard redirect URIs, HTTP loopback exceptions, or custom-scheme exceptions in the current profile.

PAR uses POST with an application/x-www-form-urlencoded body and the client's registered authentication method. Successful PAR returns exactly the standard top-level values below with HTTP 201 Created:

{
  "request_uri": "urn:ietf:params:oauth:request_uri:OPAQUE_VALUE",
  "expires_in": 600
}

The browser request then carries the issued request_uri; it MUST NOT override the stored authorization parameters.

Signed Request Objects (JAR)

A client with an enabled JAR Request Policy MAY send a signed Request Object to /authorize, either by value in request or through an exactly registered HTTPS request_uri. The outer request MUST include client_id and exactly one delivery parameter. JAR uses a dedicated client key source: exactly one inline public JWKS or HTTPS JWKS URI, with one selected signing algorithm from RS256, RS384, RS512, and ES256. It does not change the client's registered token-endpoint authentication method or require every authorization to use JAR.

The signed object MUST contain matching iss and client_id, an aud naming the canonical authorization server, an integer exp, a fresh nonempty jti, and the authorization parameters required for code flow. The signed inner parameters are authoritative; outer authorization fields cannot override them. PKCE, exact redirect matching, scopes, consent, authentication freshness, and sender-constraint policies continue to apply.

The JWT aud identifies M7 as the recipient of the Request Object. It MUST NOT be used to select a resource audience. This JAR profile uses the client's complete registered resource-audience set; a separately signed resource subset is not supported.

A customer-hosted object is fetched only from its exact registered public HTTPS URI, without redirects. After verification, M7 preserves the accepted parameters through login and consent; later content changes cannot alter that transaction. Replay, invalid signatures, invalid claims, unregistered URIs, and failed retrieval are rejected.

Discovery describes these capabilities through request_parameter_supported, request_uri_parameter_supported, request_object_signing_alg_values_supported, and require_request_uri_registration. See the discovery metadata contract for their values and JAR integration for the request and hosting rules. JAR at /par, JAR metadata through Dynamic Registration and encrypted Request Objects remain outside this JAR profile. Signed JARM is supported separately for authorization responses.

Resource audiences and scopes

An audience identifies an intended resource. It is not proof that a client owns a domain or resource. M7 issues only audiences on the client's current administrative allowlist, and each resource server MUST still require its own exact audience and scopes.

Audience identifiers are exact, case-sensitive strings. They may be URLs or opaque identifiers. A grant may request a nonempty subset of its configured audiences. Where omission is permitted, omission selects the client's complete configured set. Refresh cannot add or replace a lineage's audience set.

The standard SSO scope catalogue is:

Scope Meaning
openid Enables OpenID Connect, ID-token issuance, and UserInfo sub; required for UserInfo
profile Standard profile claims and the compatible m7 profile projection
email Primary active email and verification state when available
groups Tenant-member organization and group projection
offline_access Requests durable offline use; does not add UserInfo claims by itself

Applications MUST request only an allowed subset. Consent and provider policy may narrow the result. Application-specific resource scopes may exist beyond the SSO catalogue.

Token issuance and refresh

The token endpoint accepts only POST with an application/x-www-form-urlencoded body. Successful token requests return HTTP 200 and application/json.

Authorization codes are opaque, confidential, and single use. Redemption MUST use the exact redirect URI and PKCE verifier bound to the authorization transaction. Device codes are also confidential and single use after approved pickup.

client_credentials represents the OAuth application itself: its sub and client_id identify the application. It does not impersonate a user and does not return a refresh token.

Refresh belongs to one authenticated client and one immutable resource- audience lineage. The normal profile rotates the refresh package. A client MUST persist the complete successful replacement before discarding its preceding package. A revoked, expired, malformed, foreign, or otherwise invalid refresh credential returns HTTP 400 with error=invalid_grant.

M7 refresh packages may contain binding_chain and binding_link. The default advisory binding policy permits a standard refresh request to omit these M7 extensions. Clients using a binding-required profile MUST retain and return both values; legacy personal-lineage signing also requires its binding package. The optional M7 acknowledgement refresh mode and /token/ack are extensions to the base profile; ordinary integrations SHOULD omit refresh_mode.

DPoP sender constraint

M7 advertises RFC 9449 DPoP with exactly ES256. Dynamic Registration's standard dpop_bound_access_tokens=true selects required DPoP. Omission or false leaves DPoP optional, allowing an otherwise eligible request to opt in with a valid proof. Internal M7 root-session proofing is a separate mechanism and is not altered by a customer's DPoP registration setting.

A DPoP proof MUST:

  • be a signed dpop+jwt using ES256 and a public JWK;
  • bind the exact HTTP method and normalized target URI;
  • contain a current iat and a fresh, single-use jti; and
  • contain the exact access-token ath for protected-resource requests.

Authorization-code binding may use dpop_jkt in the authorization request or PAR. PAR also accepts a DPoP proof; when both are present, their key thumbprints MUST match. The token request MUST use the same key when the code is bound.

A bound token response uses token_type=DPoP and carries cnf.jkt in the access and refresh tokens. A protected-resource request then uses Authorization: DPoP ACCESS_TOKEN plus a distinct fresh DPoP proof. An unbound token uses token_type=Bearer and MUST NOT be upgraded to the DPoP authorization scheme.

At the token endpoint, a missing or invalid proof required by client or grant policy returns HTTP 400 with error=invalid_dpop_proof before a code or device credential is consumed. At a protected resource, a missing, malformed, stale, replayed, wrong-method, wrong-URI, or wrong-ath proof is rejected with HTTP 401, error=invalid_dpop_proof, and WWW-Authenticate: DPoP advertising algs="ES256". Scheme or key-binding confusion is rejected as invalid_token with the same DPoP challenge.

M7 does not currently issue DPoP-Nonce.

UserInfo and protected resources

UserInfo accepts GET or form-encoded POST. A standard request authenticates only with the access token; M7 derives the owning client from the validated token. An optional legacy client_id request value is accepted only as an additional exact-match assertion and never overrides the token.

UserInfo requires an active access token with openid. It does not require a dedicated UserInfo audience, but it rechecks that the token's full signed audience and scope sets remain allowed by current client policy. A resource API still MUST enforce its own exact audience, client, scope, principal, active state, and sender constraint.

JSON UserInfo success uses HTTP 200 and application/json; registered signed UserInfo uses HTTP 200 and application/jwt. Clients MUST compare UserInfo sub exactly with the validated ID-token sub.

Introspection, revocation, and end session

Introspection is restricted to confidential clients. An authenticated client may inspect only a token issued to that exact client_id. Success returns HTTP 200 with active and, only when active and authorized, the available token metadata. Clients and resource servers MUST treat active=false as unusable.

Public and confidential clients may revoke their own credentials. A valid revocation request returns HTTP 200, application/json, and exactly {} whether the submitted token was active, malformed, expired, already revoked, or owned by another client. This opaque response prevents token-existence and ownership probing; a foreign token is not revoked.

The end-session endpoint accepts GET or POST. A validated post_logout_redirect_uri must exactly match the client's registered list. M7 returns to an approved URI with HTTP 303; otherwise it completes only the generic M7 sign-out experience.

Dynamic Client Registration

Dynamic Client Registration uses authenticated POST /register with a JSON object and returns HTTP 201 on success. Defaults are:

Metadata Default
application_type web
response_types ["code"]
grant_types ["authorization_code"]
token_endpoint_auth_method client_secret_basic
id_token_signed_response_alg RS256
UserInfo response JSON
dpop_bound_access_tokens false

Public clients MUST explicitly request token_endpoint_auth_method=none. Secret-based registration returns the plaintext secret once; clients MUST store it securely because it is not later retrievable.

An mTLS registration selects either tls_client_auth or self_signed_tls_client_auth. CA-issued authentication requires exactly one of tls_client_auth_subject_dn, tls_client_auth_san_dns, tls_client_auth_san_uri, tls_client_auth_san_ip, or tls_client_auth_san_email. Self-signed authentication requires exactly one of inline jwks or HTTPS jwks_uri, and the selected JWK must represent the presented self-signed certificate. The optional tls_client_certificate_bound_access_tokens boolean defaults to false and is valid only with an mTLS authentication method.

Registration creates a protocol-valid client but deliberately does not grant a resource audience. An administrator must configure at least one exact allowed_audiences value before token issuance. M7 does not currently expose public resource-authority packs or permit a client to grant itself an arbitrary audience.

Error and rate-limit contract

Protocol errors use an OAuth JSON object with error and, where safe, error_description. Clients MUST branch on the stable error value rather than prose and MUST safely tolerate unknown error values. Browser authorization errors return only to an already validated redirect URI and preserve the transaction's exact state.

Rate-limited protocol requests return HTTP 429, error=too_many_requests, and a Retry-After header describing the current cooldown. A client MUST stop and wait; it MUST NOT rapidly retry. A request during an active rolling penalty may restart one configured quiet period from the latest attempt, but it does not add a second penalty duration to the existing expiry. Defensive thresholds are intentionally not part of the public profile.

Deliberately unsupported or pending features

Feature Status in this profile
OpenID certification Not claimed; official conformance-suite execution remains pending
JAR at /par, JAR Dynamic Registration metadata, and encrypted Request Objects Unsupported; signed JAR is enabled per client at /authorize
Encrypted JARM authorization responses Deferred; the four signed JARM modes are supported
Signed resource-audience subsets in JAR Deferred; JAR uses the complete registered resource set
JWE-encrypted ID tokens or UserInfo Unsupported
OAuth Token Exchange Unsupported; possible future profile
DPoP nonce Not issued
Implicit or hybrid flow Unsupported
PKCE plain Unsupported
Public-client introspection Unsupported
Automatic DCR audience or authority packs Deferred; administrative audience assignment remains required

Executable profile assertions

These stable assertion IDs let conformance tests and release evidence point to the public rule without exposing private fixtures or operations.

Assertion Required result
M7P-DISC-001 Both discovery URLs return the canonical issuer and identical advertised capability sets
M7P-AUTH-001 Authorization code with exact redirect, state, nonce, and PKCE S256 succeeds; wrong state, nonce, audience, or verifier fails closed
M7P-RESP-001 All ten registered response modes deliver success and safe errors; omitted request mode selects query; unknown/disallowed modes fail without fallback; PAR/JAR retain the selected mode
M7P-JARM-001 Four JARM modes carry one signed response with exact RS256/RS512 policy, issuer, client audience, expiry and state; tampered, unsigned, mixed or mismatched responses fail before code exchange
M7P-WMSG-001 Explicit opener/parent and generic receiver selection enforce exact window, origin, issuer and state, one-use receipt, and receiver cleanup; missing/closed receivers fail without fallback
M7P-PAR-001 Successful PAR returns HTTP 201 with top-level string request_uri and numeric expires_in
M7P-JAR-001 An enabled client can complete signed direct and registered-URI authorization; signatures, client/issuer/audience/time binding, replay rejection, URI restrictions, immutable continuation, and ordinary authorization/PAR regressions enforce this profile
M7P-CLIENT-001 Every advertised client-authentication method and algorithm reaches grant processing; invalid or replayed credentials return invalid_client
M7P-TOKEN-001 Successful grants return the registered signing profile and correct principal, audience, scope, and client binding
M7P-REFRESH-001 Valid refresh rotates successfully; revoked or invalid refresh returns HTTP 400 invalid_grant
M7P-DPOP-001 Bound issuance, refresh, and resource use preserve one exact cnf.jkt; downgrade and proof negatives fail with the documented 401 challenge
M7P-USERINFO-001 JSON and signed UserInfo validate and bind sub exactly without requiring a request client_id
M7P-LIFE-001 Introspection, opaque revocation, post-revocation inactive state, and end-session redirect controls match this profile
M7P-DCR-001 Registration defaults are authoritative, unsupported metadata fails without a client, and no resource audience is self-granted
M7P-RATE-001 Limiting returns 429 plus accurate Retry-After; cooldown recovery requires no protocol-bound administrative token
M7P-MTLS-001 Both advertised RFC 8705 authentication methods work through the discovered aliases; bound issuance and refresh preserve one exact cnf."x5t#S256"; UserInfo and resource use require that certificate; introspection and revocation accept the registered mTLS method; and missing, mismatched, untrusted, expired, or revoked certificates fail closed

Detailed references