Supermail
Authorization

Discovery & JWKS

The OpenID Connect discovery document and the JSON Web Key Set used to verify tokens.

Discovery document

GET /.well-known/openid-configuration

The OpenID Connect / RFC 8414 discovery document. Fetch it to discover every other endpoint rather than hard-coding paths. The issuer equals your deployment's base URL, and every endpoint URL is absolute against it.

Response (shape)
{
  "issuer": "https://supermail.example.com",
  "authorization_endpoint": "https://supermail.example.com/oauth/authorize",
  "token_endpoint": "https://supermail.example.com/oauth/token",
  "revocation_endpoint": "https://supermail.example.com/oauth/revoke",
  "end_session_endpoint": "https://supermail.example.com/oauth/end_session",
  "jwks_uri": "https://supermail.example.com/oauth/jwks",
  "response_types_supported": ["code"],
  "grant_types_supported": ["authorization_code", "refresh_token"],
  "code_challenge_methods_supported": ["S256"],
  "prompt_values_supported": ["select_account"],
  "scopes_supported": [
    "openid", "mail.read", "mail.modify", "mail.send",
    "accounts.read", "accounts.update", "accounts.delete", "accounts.create"
  ]
}

JWKS

GET /oauth/jwks

The JSON Web Key Set holding the public signing key. Use it to verify the signature of access_token and id_token JWTs offline. The key is an Ed25519 public key (OKP / Ed25519).

Response (shape)
{
  "keys": [
    {
      "kty": "OKP",
      "crv": "Ed25519",
      "use": "sig",
      "alg": "EdDSA",
      "kid": "...",
      "x": "..."
    }
  ]
}

Cache the key set and refresh it on an unrecognized kid. When the deployment is configured without a stable signing seed the key is regenerated on restart, which rotates kid — one more reason to key your cache on kid rather than pinning a single value.

Token verification

To validate a token yourself:

  1. Fetch the key from /oauth/jwks matching the token header's kid.
  2. Verify the EdDSA signature.
  3. Check iss equals your deployment's issuer and the token is unexpired.
  4. For access tokens, treat scope as informational only — the API enforces the live grant on every call.

On this page