Calimatic OIDC/OAuth2 Provider — API Reference

Base URL: https://auth.calimatic.com

Issuer: https://auth.calimatic.com

Spec compliance: OAuth 2.0 (RFC 6749), OpenID Connect Core 1.0, PKCE (RFC 7636), Token Introspection (RFC 7662), Token Revocation (RFC 7009), Dynamic Client Registration (RFC 7591), RP-Initiated Logout 1.0


Table of Contents

  1. Overview
  2. Discovery
  3. Client Registration
  4. Authorization Endpoint
  5. Token Endpoint
  6. UserInfo Endpoint
  7. JWKS Endpoint
  8. Token Introspection
  9. Token Revocation
  10. End Session (Logout)
  11. Scopes & Claims
  12. Error Responses
  13. Rate Limits
  14. Token Lifetimes
  15. Security Considerations

1. Overview

The Calimatic Auth platform acts as a standards-compliant OpenID Connect / OAuth 2.0 Provider. External applications (both first-party Calimatic apps and third-party integrations) authenticate users by redirecting them to the authorization endpoint, which presents a branded login page. After authentication and consent, the platform issues its own RSA-signed JWTs.

Supported Flows

FlowGrant TypeUse Case
Authorization Code + PKCEauthorization_codeWeb apps, SPAs, mobile apps
Refresh Tokenrefresh_tokenRenewing expired access tokens
Client Credentialsclient_credentialsMachine-to-machine (no user)

Token Format

TokenFormatSigning
Access TokenJWTRS256
ID TokenJWTRS256
Refresh TokenOpaque (base64url)N/A (stored hashed in DB)

2. Discovery

GET /.well-known/openid-configuration

Returns the OpenID Provider Configuration Document. All endpoint URLs, supported features, and signing algorithms are advertised here.

Response headers:

Cache-Control: public, max-age=3600
Access-Control-Allow-Origin: *

Example response:

{
  "issuer": "https://auth.calimatic.com",
  "authorization_endpoint": "https://auth.calimatic.com/api/v1/oidc/authorize",
  "token_endpoint": "https://auth.calimatic.com/api/v1/oidc/token",
  "userinfo_endpoint": "https://auth.calimatic.com/api/v1/oidc/userinfo",
  "jwks_uri": "https://auth.calimatic.com/api/v1/oidc/jwks",
  "registration_endpoint": "https://auth.calimatic.com/api/v1/oidc/register",
  "revocation_endpoint": "https://auth.calimatic.com/api/v1/oidc/revoke",
  "introspection_endpoint": "https://auth.calimatic.com/api/v1/oidc/introspect",
  "end_session_endpoint": "https://auth.calimatic.com/api/v1/oidc/end-session",
  "scopes_supported": [
    "openid", "profile", "email", "phone",
    "organization", "permissions", "offline_access"
  ],
  "response_types_supported": ["code"],
  "response_modes_supported": ["query"],
  "grant_types_supported": [
    "authorization_code", "refresh_token", "client_credentials"
  ],
  "subject_types_supported": ["public"],
  "id_token_signing_alg_values_supported": ["RS256"],
  "token_endpoint_auth_methods_supported": [
    "client_secret_post", "client_secret_basic"
  ],
  "code_challenge_methods_supported": ["S256"],
  "claims_supported": [
    "sub", "iss", "aud", "exp", "iat", "nonce",
    "name", "given_name", "family_name",
    "email", "email_verified", "picture",
    "phone_number", "updated_at",
    "organization_id", "organization_memberships", "user_type",
    "permissions", "roles"
  ]
}

3. Client Registration

3.1 Dynamic Registration (RFC 7591)

POST /api/v1/oidc/register
Content-Type: application/json

Rate limit: 5 requests/minute per IP.

Request body:

FieldTypeRequiredDefaultDescription
client_namestringYesHuman-readable name shown on consent screen
redirect_urisstring[]YesAllowed callback URLs (HTTPS required, except localhost)
applicationstringNoAuto-generatedUnique application identifier
grant_typesstring[]No["authorization_code"]authorization_code, refresh_token, client_credentials
token_endpoint_auth_methodstringNo"client_secret_post"client_secret_post, client_secret_basic, none
scopestringNo"openid profile email"Space-separated default scopes
client_uristringNoApplication homepage URL
logo_uristringNoLogo URL for consent screen

Example request:

{
  "client_name": "Partners Portal",
  "redirect_uris": [
    "https://partners.calimatic.com/auth/callback",
    "http://localhost:3001/auth/callback"
  ],
  "grant_types": ["authorization_code", "refresh_token"],
  "scope": "openid profile email organization",
  "client_uri": "https://partners.calimatic.com",
  "logo_uri": "https://partners.calimatic.com/logo.png"
}

Response (201 Created):

{
  "client_id": "cca_aBcDeFgHiJkL...",
  "client_secret": "ccas_xYzAbCdEfGhI...",
  "client_secret_expires_at": 0,
  "client_name": "Partners Portal",
  "redirect_uris": [
    "https://partners.calimatic.com/auth/callback",
    "http://localhost:3001/auth/callback"
  ],
  "grant_types": ["authorization_code", "refresh_token"],
  "token_endpoint_auth_method": "client_secret_post",
  "scope": "openid profile email organization",
  "client_uri": "https://partners.calimatic.com",
  "logo_uri": "https://partners.calimatic.com/logo.png"
}

Important: The client_secret is returned exactly once. Store it securely. It cannot be retrieved again. If lost, rotate the secret via the admin API.

3.2 Public Clients (SPAs / Mobile)

Set token_endpoint_auth_method to "none" during registration. Public clients:

  • Do not receive a client_secret
  • Must use PKCE (code_challenge + code_verifier)
  • Cannot use client_credentials grant

4. Authorization Endpoint

GET /api/v1/oidc/authorize

Initiates the OAuth 2.0 Authorization Code flow. The user is presented with a branded login page, and after authentication + consent, redirected back to the client with an authorization code.

Parameters

ParameterTypeRequiredDescription
client_idstringYesThe registered client ID
redirect_uristringYesMust exactly match a registered redirect URI
response_typestringYesMust be code
scopestringNoSpace-separated scopes (default: openid)
statestringRecommendedOpaque value for CSRF protection
code_challengestringConditionalS256 PKCE challenge (required if client has require_pkce: true)
code_challenge_methodstringConditionalMust be S256
noncestringNoRandom value bound to the ID token

Flow

1. Client redirects user to /api/v1/oidc/authorize?...
2. Platform validates client_id, redirect_uri, scopes, PKCE
3. If user not logged in → redirect to /login (branded, preserving OAuth params)
4. User authenticates (email/password, SSO, or social login)
5. If consent required and not yet granted → redirect to /consent
6. User approves consent
7. Platform issues authorization code (60-second expiry, one-time use)
8. Redirect to redirect_uri?code=...&state=...

Example

GET /api/v1/oidc/authorize
  ?client_id=cca_aBcDeFgHiJkL...
  &redirect_uri=https://partners.calimatic.com/auth/callback
  &response_type=code
  &scope=openid+profile+email+organization
  &state=af0ifjsldkj
  &code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
  &code_challenge_method=S256
  &nonce=n-0S6_WzA2Mj

Success Redirect

HTTP/1.1 302 Found
Location: https://partners.calimatic.com/auth/callback
  ?code=dGhpcyBpcyBhIHRlc3QgY29kZQ...
  &state=af0ifjsldkj

Error Redirect

HTTP/1.1 302 Found
Location: https://partners.calimatic.com/auth/callback
  ?error=access_denied
  &error_description=The+user+denied+the+authorization+request
  &state=af0ifjsldkj

Errors before redirect_uri is validated (invalid client_id or unregistered redirect_uri) return a JSON response directly to prevent open redirector attacks.


5. Token Endpoint

POST /api/v1/oidc/token
Content-Type: application/x-www-form-urlencoded

Rate limit: 20 requests/minute per client.

Client Authentication

Confidential clients must authenticate at the token endpoint using one of:

client_secret_post (body parameters):

client_id=cca_aBcDeFgHiJkL...&client_secret=ccas_xYzAbCdEfGhI...

client_secret_basic (Authorization header):

Authorization: Basic base64(client_id:client_secret)

Public clients pass only client_id in the body (no secret).


5.1 Authorization Code Grant

Exchange an authorization code for tokens.

Parameters:

FieldTypeRequiredDescription
grant_typestringYesauthorization_code
codestringYesThe authorization code received from the authorize redirect
redirect_uristringYesMust match the URI used in the authorization request
client_idstringYesClient identifier
client_secretstringConditionalRequired for confidential clients
code_verifierstringConditionalPKCE verifier (required if code_challenge was sent)

Example request:

curl -X POST https://auth.calimatic.com/api/v1/oidc/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=authorization_code" \
  -d "code=dGhpcyBpcyBhIHRlc3QgY29kZQ..." \
  -d "redirect_uri=https://partners.calimatic.com/auth/callback" \
  -d "client_id=cca_aBcDeFgHiJkL..." \
  -d "client_secret=ccas_xYzAbCdEfGhI..." \
  -d "code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"

Response (200 OK):

{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "xYzAbCdEfGhIjKlMnOpQrStUv...",
  "id_token": "eyJhbGciOiJSUzI1NiIs...",
  "scope": "openid profile email organization"
}

5.2 Refresh Token Grant

Exchange a refresh token for a new access token.

Parameters:

FieldTypeRequiredDescription
grant_typestringYesrefresh_token
refresh_tokenstringYesThe refresh token
client_idstringYesClient identifier
client_secretstringConditionalRequired for confidential clients
scopestringNoSubset of originally granted scopes

Example request:

curl -X POST https://auth.calimatic.com/api/v1/oidc/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=refresh_token" \
  -d "refresh_token=xYzAbCdEfGhIjKlMnOpQrStUv..." \
  -d "client_id=cca_aBcDeFgHiJkL..." \
  -d "client_secret=ccas_xYzAbCdEfGhI..."

Response (200 OK):

{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "xYzAbCdEfGhIjKlMnOpQrStUv...",
  "id_token": "eyJhbGciOiJSUzI1NiIs...",
  "scope": "openid profile email organization"
}

Note: The same refresh token is returned. Refresh tokens are not rotated on each use.


5.3 Client Credentials Grant

Obtain a machine-to-machine access token without user context.

Parameters:

FieldTypeRequiredDescription
grant_typestringYesclient_credentials
client_idstringYesClient identifier
client_secretstringYesClient secret
scopestringNoSpace-separated scopes

Example request:

curl -X POST https://auth.calimatic.com/api/v1/oidc/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=cca_aBcDeFgHiJkL..." \
  -d "client_secret=ccas_xYzAbCdEfGhI..." \
  -d "scope=openid"

Response (200 OK):

{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "openid"
}

Note: No refresh_token or id_token is issued for client credentials. The sub claim is the client_id.


6. UserInfo Endpoint

GET /api/v1/oidc/userinfo
Authorization: Bearer <access_token>

Also accepts POST with the same Authorization header.

Returns claims about the authenticated user based on the scopes granted to the access token.

Example request:

curl https://auth.calimatic.com/api/v1/oidc/userinfo \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIs..."

Example response (scopes: openid profile email organization):

{
  "sub": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "name": "Jane Smith",
  "given_name": "Jane",
  "family_name": "Smith",
  "picture": "https://lh3.googleusercontent.com/photo.jpg",
  "email": "jane@example.com",
  "email_verified": true,
  "organization_id": "org-uuid-here",
  "organization_memberships": [
    {
      "organization_id": "org-uuid-here",
      "role": "admin"
    }
  ],
  "user_type": "customer_admin"
}

Response headers:

Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache

7. JWKS Endpoint

GET /api/v1/oidc/jwks

Returns the JSON Web Key Set containing the platform's public RSA keys. Use these to verify JWTs (access tokens and ID tokens) issued by the platform.

Response headers:

Cache-Control: public, max-age=900
Access-Control-Allow-Origin: *

Example response:

{
  "keys": [
    {
      "kty": "RSA",
      "n": "0vx7agoebGcQ...",
      "e": "AQAB",
      "kid": "a1b2c3d4e5f6...",
      "alg": "RS256",
      "use": "sig"
    }
  ]
}

The JWKS may contain up to two keys (current + previous) during key rotation. Clients should match the kid from the JWT header to find the correct verification key.


8. Token Introspection

POST /api/v1/oidc/introspect
Content-Type: application/x-www-form-urlencoded

RFC 7662 compliant. Returns metadata about a token. Requires client authentication.

Rate limit: 60 requests/minute per client.

Parameters:

FieldTypeRequiredDescription
tokenstringYesThe token to introspect
token_type_hintstringNoaccess_token or refresh_token
client_idstringYesClient identifier
client_secretstringYesClient secret

Example request:

curl -X POST https://auth.calimatic.com/api/v1/oidc/introspect \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "token=eyJhbGciOiJSUzI1NiIs..." \
  -d "token_type_hint=access_token" \
  -d "client_id=cca_aBcDeFgHiJkL..." \
  -d "client_secret=ccas_xYzAbCdEfGhI..."

Response (active access token):

{
  "active": true,
  "scope": "openid profile email",
  "client_id": "cca_aBcDeFgHiJkL...",
  "token_type": "Bearer",
  "exp": 1700000000,
  "iat": 1699996400,
  "sub": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "iss": "https://auth.calimatic.com",
  "aud": "cca_aBcDeFgHiJkL..."
}

Response (inactive/invalid token):

{
  "active": false
}

9. Token Revocation

POST /api/v1/oidc/revoke
Content-Type: application/x-www-form-urlencoded

RFC 7009 compliant. Revokes a refresh token. Requires client authentication.

Rate limit: 20 requests/minute per client.

Parameters:

FieldTypeRequiredDescription
tokenstringYesThe token to revoke
token_type_hintstringNorefresh_token (default) or access_token
client_idstringYesClient identifier
client_secretstringYesClient secret

Example request:

curl -X POST https://auth.calimatic.com/api/v1/oidc/revoke \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "token=xYzAbCdEfGhIjKlMnOpQrStUv..." \
  -d "token_type_hint=refresh_token" \
  -d "client_id=cca_aBcDeFgHiJkL..." \
  -d "client_secret=ccas_xYzAbCdEfGhI..."

Response: 200 OK (empty body)

Note: Access tokens are stateless JWTs and cannot be revoked. They expire naturally based on expires_in. Passing an access token returns 200 but has no effect. Always returns 200 per RFC 7009.


10. End Session (Logout)

GET /api/v1/oidc/end-session

OpenID Connect RP-Initiated Logout 1.0. Ends the user's session at the provider.

Parameters:

ParameterTypeRequiredDescription
id_token_hintstringRecommendedThe ID token received during authentication
post_logout_redirect_uristringNoURL to redirect to after logout. Must share origin with a registered redirect URI.
statestringNoOpaque value passed back to post_logout_redirect_uri
client_idstringNoClient identifier (used if id_token_hint is not provided)

Example:

GET /api/v1/oidc/end-session
  ?id_token_hint=eyJhbGciOiJSUzI1NiIs...
  &post_logout_redirect_uri=https://partners.calimatic.com/logged-out
  &state=abc123

Behavior:

  1. Validates id_token_hint (extracts user and client)
  2. Validates post_logout_redirect_uri against registered origins
  3. Revokes all refresh tokens for user + client
  4. Redirects to the platform's signout page (clears session)
  5. After signout, redirects to post_logout_redirect_uri?state=abc123

11. Scopes & Claims

Standard Scopes

ScopeDescriptionClaims Granted
openidRequired. Verify user identitysub
profileName and profile picturename, given_name, family_name, picture, updated_at
emailEmail addressemail, email_verified
phonePhone numberphone_number
offline_accessIssue a refresh token(none — enables refresh token in response)

Calimatic-Specific Scopes

ScopeDescriptionClaims Granted
organizationOrganization contextorganization_id, organization_memberships, user_type
permissionsPlatform permissionspermissions, roles

Claim Definitions

ClaimTypeDescription
substringKeycloak user ID (stable, unique per user)
namestringDisplay name
given_namestringFirst name
family_namestringLast name
emailstringEmail address
email_verifiedbooleanWhether email is verified
picturestringAvatar URL
phone_numberstringPhone number
updated_atnumberUnix timestamp of last profile update
organization_idstringPrimary organization UUID
organization_membershipsarray[{ organization_id, role }]
user_typestringcustomer_admin, customer_teacher, student, parent, etc.
permissionsstring[]All resolved platform permissions
rolesstring[]Assigned role names

Access Token JWT Claims

Access tokens always include these standard JWT claims plus scope-dependent user claims:

{
  "iss": "https://auth.calimatic.com",
  "sub": "keycloak-user-id",
  "aud": "cca_clientId...",
  "iat": 1699996400,
  "exp": 1700000000,
  "scope": "openid profile email organization",
  "token_type": "access_token",
  "email": "jane@example.com",
  "email_verified": true,
  "name": "Jane Smith",
  "given_name": "Jane",
  "family_name": "Smith",
  "picture": "https://...",
  "organization_id": "org-uuid",
  "organization_memberships": [...],
  "user_type": "customer_admin"
}

ID Token JWT Claims

{
  "iss": "https://auth.calimatic.com",
  "sub": "keycloak-user-id",
  "aud": "cca_clientId...",
  "iat": 1699996400,
  "exp": 1700000000,
  "nonce": "n-0S6_WzA2Mj",
  "token_type": "id_token",
  "email": "jane@example.com",
  "email_verified": true,
  "name": "Jane Smith",
  "given_name": "Jane",
  "family_name": "Smith"
}

12. Error Responses

All error responses follow the OAuth 2.0 error format.

Token / Introspect / Revoke Endpoint Errors (JSON)

{
  "error": "invalid_grant",
  "error_description": "Authorization code is invalid, expired, or already used"
}

HTTP status codes:

StatusWhen
400Invalid request parameters, invalid grant, invalid scope
401Invalid client credentials
405Wrong HTTP method
429Rate limit exceeded
500Server error

Authorization Endpoint Errors (Redirect)

Errors are returned as query parameters on the redirect_uri:

https://partners.calimatic.com/callback?error=access_denied&error_description=...&state=xyz

Errors before redirect_uri validation return JSON directly (400 status).

UserInfo Endpoint Errors (Bearer)

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer error="invalid_token", error_description="Token is invalid or expired"

Error Codes

CodeDescription
invalid_requestMissing or invalid parameter
invalid_clientClient authentication failed
invalid_grantCode/token invalid, expired, or already used
unauthorized_clientClient not authorized for this grant type
unsupported_grant_typeGrant type not supported
unsupported_response_typeResponse type not supported
invalid_scopeRequested scope not supported or not allowed
access_deniedUser denied the authorization request
server_errorInternal server error
temporarily_unavailableRate limited or service overloaded
invalid_tokenBearer token invalid or expired
login_requiredUser must authenticate
consent_requiredUser must grant consent

13. Rate Limits

EndpointLimitWindowKey
/api/v1/oidc/token20 requests1 minutePer client
/api/v1/oidc/authorize30 requests1 minutePer IP
/api/v1/oidc/introspect60 requests1 minutePer client
/api/v1/oidc/revoke20 requests1 minutePer client
/api/v1/oidc/userinfo60 requests1 minutePer token
/api/v1/oidc/register5 requests1 minutePer IP

Rate limit response (429):

{
  "error": "temporarily_unavailable",
  "error_description": "Rate limit exceeded"
}

Headers: Retry-After, X-RateLimit-Limit, X-RateLimit-Remaining.


14. Token Lifetimes

TokenDefault TTLConfigurable
Authorization Code60 secondsNo
Access Token3600 seconds (1 hour)Per client (access_token_ttl)
ID TokenSame as access tokenPer client
Refresh Token86400 seconds (24 hours)Per client (refresh_token_ttl)

15. Security Considerations

PKCE (Proof Key for Code Exchange)

PKCE is required by default for all clients. Use the S256 challenge method:

code_verifier  = random 43-128 character string (unreserved URI characters)
code_challenge = BASE64URL(SHA256(code_verifier))

State Parameter

Always include a cryptographically random state parameter in authorization requests to prevent CSRF attacks. Verify it matches when receiving the callback.

Token Storage

  • Confidential clients (server-side): Store client_secret and refresh tokens securely (environment variables, secrets manager). Never expose in client-side code.
  • Public clients (SPAs): Never store refresh tokens in localStorage. Use httpOnly cookies or in-memory storage. Always use PKCE.
  • Access tokens: Short-lived by design. Store in memory when possible.

Redirect URI Validation

Redirect URIs are validated with exact match against registered URIs. No wildcards are supported. Register all environments (production, staging, localhost for development).

CORS

All OIDC endpoints and /.well-known/* return CORS headers:

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: Authorization, Content-Type
Access-Control-Max-Age: 86400

JWT Verification

Always verify JWTs using the public keys from the JWKS endpoint:

  1. Fetch JWKS from /api/v1/oidc/jwks (cache for 15 minutes)
  2. Match the kid from the JWT header to a key in the JWKS
  3. Verify the signature using the matched RSA public key
  4. Validate iss equals https://auth.calimatic.com
  5. Validate aud matches your client_id
  6. Validate exp is in the future