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:
issuerauthorization_endpointpushed_authorization_request_endpointregistration_endpointtoken_endpointend_session_endpointuserinfo_endpointrevocation_endpointintrospection_endpointdevice_authorization_endpointjwks_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:
- the JWS signature using the key selected by
kid; - the token type and intended audience for your client or resource;
- expiry and other time-based claims; and
noncefor 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.