Discovery and signing keys

Discovery

M7 publishes OpenID Connect and OAuth authorization-server metadata at both:

GET https://sso.user.m7.org/.well-known/openid-configuration
GET https://sso.user.m7.org/.well-known/oauth-authorization-server

Both endpoints return the same JSON metadata. Use discovery to obtain the issuer and endpoint URLs rather than duplicating them in application code.

The metadata advertises:

  • issuer
  • authorization_endpoint
  • pushed_authorization_request_endpoint
  • registration_endpoint
  • token_endpoint
  • end_session_endpoint
  • userinfo_endpoint
  • revocation_endpoint
  • introspection_endpoint
  • device_authorization_endpoint
  • jwks_uri
  • supported response types, grants, PKCE methods, client-authentication methods, claims, and scopes

Use GET or HEAD; other methods are rejected.

Supported protocol metadata

The supported metadata contract is:

Metadata item Values
response_types_supported code
response_modes_supported query, form_post, fragment, query.jwt, form_post.jwt, fragment.jwt, jwt, web_message.opener, web_message.parent, web_message
authorization_signing_alg_values_supported RS256, RS512 for signed JARM
subject_types_supported public
id_token_signing_alg_values_supported RS256, RS384, RS512, PS256, PS384, PS512, ES256, ES384, ES512, ES256K, Ed25519, Ed448, ML-DSA-44, ML-DSA-65, ML-DSA-87, HS256, HS384, HS512 (18-profile source update; rollout status below)
userinfo_signing_alg_values_supported RS256, RS512
request_parameter_supported true for the JAR-enabled discovery release
request_uri_parameter_supported true for the JAR-enabled discovery release
request_object_signing_alg_values_supported RS256, RS384, RS512, ES256
require_request_uri_registration true
grant_types_supported authorization_code, refresh_token, client_credentials, urn:ietf:params:oauth:grant-type:device_code
code_challenge_methods_supported S256
token_endpoint_auth_methods_supported none, client_secret_basic, client_secret_post, client_secret_jwt, private_key_jwt, tls_client_auth, self_signed_tls_client_auth
token_endpoint_auth_signing_alg_values_supported HS256, HS384, HS512, RS256, RS384, RS512, ES256
introspection_endpoint_auth_methods_supported client_secret_basic, client_secret_post, client_secret_jwt, private_key_jwt, tls_client_auth, self_signed_tls_client_auth
introspection_endpoint_auth_signing_alg_values_supported HS256, HS384, HS512, RS256, RS384, RS512, ES256
revocation_endpoint_auth_methods_supported none, client_secret_basic, client_secret_post, client_secret_jwt, private_key_jwt, tls_client_auth, self_signed_tls_client_auth
revocation_endpoint_auth_signing_alg_values_supported HS256, HS384, HS512, RS256, RS384, RS512, ES256
scopes_supported openid, profile, email, groups, offline_access

Discovery also publishes tls_client_certificate_bound_access_tokens: true and this RFC 8705 endpoint-alias object:

{
  "mtls_endpoint_aliases": {
    "token_endpoint": "https://mtls-sso.user.m7.org/token",
    "pushed_authorization_request_endpoint": "https://mtls-sso.user.m7.org/par",
    "device_authorization_endpoint": "https://mtls-sso.user.m7.org/device_authorization",
    "introspection_endpoint": "https://mtls-sso.user.m7.org/introspect",
    "revocation_endpoint": "https://mtls-sso.user.m7.org/revoke",
    "userinfo_endpoint": "https://mtls-sso.user.m7.org/userinfo"
  }
}

Use these aliases only for requests that present an mTLS client certificate. They do not change the issuer: ID tokens and signed UserInfo still require the canonical discovery issuer. Authorization, discovery, registration, and JWKS remain on https://sso.user.m7.org.

The discovery document is the source of truth for endpoint URLs and supported feature lists. A client still needs to be configured and authorized for every grant, scope, authentication method, audience, and redirect URI it uses.

The advertised standard ID-token claims are iss, sub, aud, exp, iat, nbf, jti, azp, nonce, and auth_time. Discovery also lists the supported profile, email, and m7 extension claims described in UserInfo and scopes. nonce is present when the authorization request supplied one, and auth_time is present when the request or registered client policy requires it.

JAR supports signed Request Objects by value in request and by an exactly registered customer-hosted HTTPS request_uri at /authorize. Each client must first enable its dedicated JAR Request Policy and configure an allowed signing algorithm and public key source. require_request_uri_registration requires prior exact URI registration in that policy; it does not imply that Dynamic Registration accepts request_uris.

Check the live document before enabling automatic feature selection: a server still returning false for the Request Object flags has not enabled the JAR discovery advertisement. This does not disable ordinary PAR. Its M7-issued urn:ietf:params:oauth:request_uri: remains distinct from a customer-hosted URI. JAR at /par, encrypted Request Objects, and JAR Dynamic Registration metadata are not supported. See Signed Request Objects (JAR).

Both live discovery documents returned the ten response modes and the two JARM signing algorithms at 2026-09-15 06:24 UTC. The default mode is query; form POST, fragment, signed JARM and Web Message are also supported. See response modes for delivery and prerequisites, and callback outcomes for result handling. Authorization-response encryption metadata is absent because encryption is not supported. JARM signing algorithms are independent of the ID-token list.

M7 accepts only the exact PKCE method S256. Authorization requests with a missing method, plain, different capitalization, or an unknown method are rejected before an authorization code can be issued.

HMAC and discovery

Both live discovery documents advertise HS256, HS384 and HS512 in id_token_signing_alg_values_supported, alongside the 15 asymmetric profiles. Both discovery URLs use this same handler. HMAC requires a recoverable client secret and has no public PEM/JWKS key; see HMAC setup. UserInfo and JARM remain RS256/RS512, and client-authentication, JAR and DPoP algorithm lists retain their separate contracts.

Discovery lists provider capabilities, not every permitted combination of client settings. Public clients using none must select an asymmetric algorithm. The User.M7/SSO eligibility guard is locally tested and awaits production verification; it does not remove HMAC from discovery or change the public-key inventory. See the setup guide for the exact boundary.

The source update passed local discovery, registration, JWKS, UserInfo, JAR and mTLS contracts. At 2026-09-15 21:49:24 UTC, both public discovery URLs returned HTTP 200 with identical metadata and the full 18-algorithm ID-token list. Public JWKS covered the 15 asymmetric profiles and contained no symmetric material. HMAC discovery is deployed and advertised. Automatic selection must follow the live list. Discovery responses may be cached for 300 seconds.

Signing profiles

The complete signing set contains 18 profiles: the 15 asymmetric profiles with public provider keys below, plus three HMAC profiles using the client's shared secret. Choose from the live advertisement and your client's supported algorithms; HMAC uses the shared-secret contract above.

Algorithms JWK type Public key parameters
RS256, RS384, RS512, PS256, PS384, PS512 RSA n, e; RSA-PSS uses the selected hash for MGF1 and a salt the size of that hash
ES256 EC crv=P-256, x, y
ES384 EC crv=P-384, x, y
ES512 EC crv=P-521, x, y
ES256K EC crv=secp256k1, x, y
Ed25519 OKP crv=Ed25519, x
Ed448 OKP crv=Ed448, x
ML-DSA-44, ML-DSA-65, ML-DSA-87 AKP alg selects the parameter set; pub is the base64url public key
HS256, HS384, HS512 No public JWK Original client secret; never published in JWKS

The exact algorithm identifiers and key types are registered in the IANA JOSE registry. Edwards uses the explicit Ed25519 or Ed448 algorithm name, not EdDSA. ML-DSA consumers need JOSE support for AKP and the selected ML-DSA parameter set; generic OAuth support alone does not establish that capability.

Use a JOSE-aware verifier for the selected profile: ECDSA JWS signatures use fixed-width R || S, rather than an ASN.1 DER signature. Configure a local algorithm allowlist, require the client's selected ID-token algorithm, and bind the matching key type and curve/parameter set. Do not widen the allowlist from an untrusted token header or fall back to another algorithm on failure.

The current-source M7 Identity SDK token verifier supports the 15 asymmetric profiles with an explicit local allowlist and a compatible key resolver. Existing RSA and ES256/P-256 JWK paths retain PHP OpenSSL; additional profiles use the optional m7crypto extension (M7\Crypto\Key) and its OpenSSL backend. They require a resolved SPKI public PEM through the existing provided-PEM or trusted exact-kid x5u path; RSA-PSS also accepts the existing RSA JWK key type. This change does not add EC/OKP/AKP JWKS decoders or expand signed UserInfo. An absent module or unavailable algorithm fails explicitly; installing it does not widen the configured allowlist. Existing published SDK ZIPs are unchanged; deploy the updated source or a later artifact that explicitly includes it.

For asymmetric profiles, provider keys sign ID tokens and shared-key access/refresh packages. A client configured for personal refresh certificates instead receives access/refresh tokens signed by its lineage's key, reused across refresh. Those personal keys are deliberately absent from provider JWKS and are resolved by the exact-kid PEM endpoint below. ID tokens continue to use published provider keys for asymmetric profiles; HMAC ID tokens use the client secret instead. Treat refresh tokens as opaque; this key description does not authorize applications to depend on their internal claims.

Token size depends on both the algorithm and claims. The live sample's ML-DSA-87 ID token was 7,069 bytes; consumers must account for the selected profile in token storage and transport limits.

Post-quantum tokens and browser storage

ML-DSA signing and native SDK verification are supported in the tested device token-pickup/refresh and machine-token paths. Those JSON token responses do not store the relying client's tokens in browser cookies. Use updated SDK source, the compatible m7crypto backend and an explicit algorithm allowlist; the grant alone does not establish verifier or transport compatibility.

The current M7 Web/PHP SDK stores each complete access and refresh JWT in a single HttpOnly cookie. Do not select ML-DSA for that browser session path until its token-storage integration is revised and tested. Chromium limits each cookie name plus value to 4,096 bytes. The encoded signatures alone occupy:

Algorithm Raw signature bytes Unpadded base64url characters
ML-DSA-44 2,420 3,227
ML-DSA-65 3,309 4,412
ML-DSA-87 4,627 6,170

ML-DSA-65/87 exceed that cookie limit before JWT headers and claims are added. ML-DSA-44 can exceed it with normal claim growth; a small sample fitting does not establish general compatibility. PHP accepting setcookie() or a larger application input limit does not prove the browser retained the cookie. Aggregate Cookie/Authorization headers and proxy, server and storage limits also need explicit budgets.

This is a restriction of the current Web SDK token-cookie design, not a ban on post-quantum web authentication. A different integration can keep tokens server-side behind a bounded session reference, subject to its full security, refresh and sender-constraint contract. M7's Web SDK redesign and native crypto distribution remain separate follow-ups. Ed25519 browser sign-in and refresh have operator-confirmed production acceptance with the updated SDK source.

Signing-key set

M7 exposes this discovery key endpoint:

GET https://sso.user.m7.org/jwks.json

This is a conventional RFC 7517 JSON Web Key Set. It returns a keys array containing the currently published M7 provider-signing keys:

{
  "keys": [
    {
      "kty": "RSA",
      "use": "sig",
      "alg": "RS512",
      "kid": "SIGNING_KEY_UUID",
      "n": "BASE64URL_RSA_MODULUS",
      "e": "AQAB",
      "x5u": "https://sso.user.m7.org/cert/kid/SIGNING_KEY_UUID?response=pem"
    }
  ]
}

Select the key whose exact kid equals the signed JWT's protected-header kid, require use=sig, and require alg to match both the protected header and the configured algorithm. Validate the algorithm's key type and public parameters from the table above before verifying the signature. The endpoint validates the upstream provider inventory and fails closed instead of publishing unknown fields, private material, duplicate IDs, unsupported algorithms, malformed key material, or untrusted certificate locations.

Use GET or HEAD for /jwks.json. A successful response uses Content-Type: application/jwk-set+json and is publicly cacheable for 300 seconds. An unavailable or invalid provider key set returns server_error with HTTP 502; do not use a stale unknown key as a fallback.

PEM lookup

Each published x5u points to the matching public certificate or public key:

GET https://sso.user.m7.org/cert/kid/SIGNING_KEY_UUID?response=pem

kid must be a dashed UUID. Provider keys are selected from the current JWKS; personal-lineage keys may be retrieved by exact kid without appearing there. The optional response parameter may be omitted or must equal pem; other formats are not supported. Use GET or HEAD. A successful response uses Content-Type: application/x-pem-file and is publicly cacheable for 300 seconds.

For provider keys, M7 verifies that the PEM key type and algorithm-specific public parameters match the selected JWKS member. Personal-lineage PEMs are validated as supported public signing keys and remain outside provider JWKS. An invalid key identifier or format returns HTTP 400, an unknown kid returns HTTP 404, and an unavailable or invalid PEM fails closed with HTTP 502. OIDC libraries supporting the selected algorithm can validate provider keys from their JWK parameters; fetch the fixed SSO PEM URL when the validator needs PEM input. Never follow an arbitrary token-supplied certificate URL.

Validate tokens before use

For an ID token or a resource-server JWT, validate at least:

  1. the JWS signature using the key selected by kid;
  2. the token type and intended audience for your client or resource;
  3. expiry and other time-based claims; and
  4. nonce for an ID token obtained from a browser authorization that supplied a nonce.

Issuer values by token class

Use an issuer policy for the token class being validated:

Purpose Value
Authorization-server issuer in discovery https://sso.user.m7.org
Current ID-token migration allowlist https://sso.user.m7.org or legacy id.m7.org
Current access-token iss value id.m7.org
Public signing-key origin https://sso.user.m7.org/jwks.json

ID-token issuers are an exact transitional allowlist, not an arbitrary host or suffix match. Reject every other issuer. New relying-party integrations should retain the canonical discovery issuer and the documented legacy value only while this migration contract remains active.

Do not configure an access-token validator to require the discovery issuer: current access-token profiles carry iss: "id.m7.org". After selecting the public key by kid, validate the token class's issuer, signature, audience, authorized party or client, type, time claims, principal profile, and OIDC nonce where applicable. Do not trust a decoded JWT merely because it has expected-looking claims.

Caching and key rotation

Respect HTTP caching headers on the JWKS and PEM responses. Cache keys by kid and tolerate a new kid at any time. For provider keys, refresh the JWKS once for an unknown kid, and fail validation if the key remains absent. Do not pin a single signing key indefinitely and do not continue using a removed key beyond an explicitly safe cache policy. Personal-lineage access tokens use the fixed exact-kid PEM lookup; their absence from provider JWKS is expected and is not a reason to select a different provider key.