Token Endpoints

POST /token/refresh

Exchange a valid refresh token for a new access + refresh token pair. The old refresh token is revoked (rotation).

Request:

{
  "refresh_token": "a1b2c3d4...",
  "app_id": "uuid"
}

Success Response (200):

{
  "access_token": "eyJhbG...",
  "refresh_token": "new-token...",
  "token_type": "Bearer",
  "expires_in": 900
}

Errors: | Status | Detail | Meaning | |--------|--------|---------| | 401 | Invalid or expired refresh token | Token not found, expired, or already used | | 401 | Token does not belong to this app | app_id mismatch | | 403 | Account suspended | User suspended for this app |

Replay Detection: If a revoked token is reused, ALL refresh tokens for that user+app are revoked.


POST /token/revoke

Revoke a refresh token (logout).

Request:

{
  "refresh_token": "a1b2c3d4..."
}

Response (200):

{ "status": "ok" }

Always returns 200, even if the token doesn't exist (prevents information leakage).


POST /token/verify

Verify an access token and return its claims. For apps that can't do local JWT verification.

Request:

{
  "token": "eyJhbG...",
  "audience": "your-app-id"
}

Valid Response:

{
  "valid": true,
  "claims": {
    "sub": "user-uuid",
    "email": "user@example.com",
    "roles": ["user", "admin"],
    "aud": "app-uuid",
    "iss": "https://keymaster.cloud-monitor.com",
    "exp": 1710548100
  }
}

Invalid Response:

{
  "valid": false,
  "error": "Token has expired"
}

Note: Prefer local JWKS verification for production. This endpoint is for debugging and apps that can't handle JWTs locally.


POST /auth/token

OAuth2 token endpoint. Currently supports client_credentials grant.

Request (form-encoded):

grant_type=client_credentials
&client_id=app-uuid
&client_secret=your-secret
&scope=push:send

Success Response (200):

{
  "access_token": "eyJhbG...",
  "token_type": "bearer",
  "expires_in": 900,
  "scope": "push:send"
}

Errors: | Status | Detail | |--------|--------| | 400 | unsupported_grant_type | | 400 | invalid_scope: xyz | | 401 | invalid_client | | 429 | too_many_requests (10 attempts/15min per client_id) |


GET /.well-known/jwks.json

Public keys for JWT verification. Cache these and refresh hourly or when kid doesn't match.

Response:

{
  "keys": [
    {
      "kty": "RSA",
      "use": "sig",
      "alg": "RS256",
      "kid": "a1b2c3d4e5f6",
      "n": "base64url-encoded-modulus...",
      "e": "AQAB"
    }
  ]
}

GET /.well-known/openid-configuration

OIDC discovery document.

Response:

{
  "issuer": "https://keymaster.cloud-monitor.com",
  "authorization_endpoint": "https://keymaster.cloud-monitor.com/login",
  "token_endpoint": "https://keymaster.cloud-monitor.com/auth/token",
  "userinfo_endpoint": "https://keymaster.cloud-monitor.com/userinfo",
  "jwks_uri": "https://keymaster.cloud-monitor.com/.well-known/jwks.json",
  "revocation_endpoint": "https://keymaster.cloud-monitor.com/token/revoke",
  "response_types_supported": ["code"],
  "subject_types_supported": ["public"],
  "id_token_signing_alg_values_supported": ["RS256"],
  "scopes_supported": ["openid", "profile", "email", "roles", "push:send"],
  "token_endpoint_auth_methods_supported": ["client_secret_post"],
  "grant_types_supported": ["authorization_code", "refresh_token", "client_credentials"]
}