Quick Start Guide
Get your app authenticating with Keymaster in 5 minutes.
Prerequisites
- An app registered in the Keymaster Console (you'll need the
app_idandclient_secret) - At least one redirect URI configured
- At least one auth provider enabled (password, Google, etc.)
Step 1: Redirect to Keymaster Login
When a user clicks "Sign In" in your app, redirect them to:
https://keymaster.cloud-monitor.com/login
?app_id=YOUR_APP_ID
&redirect_uri=https://yourapp.com/auth/callback
Keymaster shows a branded login page with your app's logo, name, and enabled auth methods.
Recommended for web apps: use PKCE. Generate a random code_verifier, derive code_challenge = base64url(sha256(verifier)), stash the verifier in the user's server-side session, and add these params to the login URL:
&code_challenge=BASE64URL_SHA256_OF_VERIFIER
&code_challenge_method=S256
Step 2: Handle the Callback and Exchange the Code
Because your redirect_uri is a web (https) URL, Keymaster redirects back with a short-lived, single-use code — not tokens:
https://yourapp.com/auth/callback
?code=Xy7k... # single-use, expires in 60 seconds
Exchange the code for tokens server-side in your callback handler:
# Server-side callback handler (Python / Flask-style)
import httpx
@app.route("/auth/callback")
def auth_callback():
if error := request.args.get("error"):
return f"Login failed: {error}", 400
code = request.args["code"]
resp = httpx.post("https://keymaster.cloud-monitor.com/token/exchange", json={
"code": code,
"app_id": YOUR_APP_ID,
# Include code_verifier ONLY if you started login with PKCE:
"code_verifier": session.pop("code_verifier", None),
})
resp.raise_for_status()
tokens = resp.json()
# {
# "access_token": "eyJhbGciOiJSUzI1NiIs...",
# "refresh_token": "a1b2c3d4e5f6...",
# "token_type": "Bearer",
# "expires_in": 900
# }
# Store tokens in your durable session store (see Step 4), then redirect.
save_session(tokens)
return redirect("/")
Do NOT read
access_tokenfrom the callback URL. That was the old behavior and it no longer works for web apps — it causes an infinite login redirect loop. Web apps always exchange the?code=at/token/exchange. (Native apps using a custom URL scheme, e.g.myapp://callback, still receiveaccess_tokenandrefresh_tokendirectly on the deep link and skip this exchange.)
Step 3: Verify the Access Token
The access token is an RS256-signed JWT. Verify it using Keymaster's public keys:
# Python (using PyJWT)
import jwt
from jwt import PyJWKClient
jwks_client = PyJWKClient("https://keymaster.cloud-monitor.com/.well-known/jwks.json")
signing_key = jwks_client.get_signing_key_from_jwt(access_token)
payload = jwt.decode(
access_token,
signing_key.key,
algorithms=["RS256"],
issuer="https://keymaster.cloud-monitor.com",
audience=YOUR_APP_ID,
)
# Reject anything that isn't an access token:
assert payload["token_type"] == "access"
# payload contains:
# {
# "sub": "user-uuid",
# "email": "user@example.com",
# "name": "Jane Doe",
# "roles": ["user", "admin"],
# "aud": "your-app-id",
# "iss": "https://keymaster.cloud-monitor.com",
# "iat": 1710547200,
# "exp": 1710548100,
# "token_type": "access"
# }
// Node.js (using jose)
import { createRemoteJWKSet, jwtVerify } from 'jose';
const JWKS = createRemoteJWKSet(
new URL('https://keymaster.cloud-monitor.com/.well-known/jwks.json')
);
const { payload } = await jwtVerify(accessToken, JWKS, {
issuer: 'https://keymaster.cloud-monitor.com',
audience: YOUR_APP_ID,
});
// Reject anything that isn't an access token:
if (payload.token_type !== 'access') throw new Error('not an access token');
Step 4: Store Tokens and Refresh
Store both tokens in a durable session store (database, not memory). The access token expires in 15 minutes. Before it expires, refresh it:
import httpx
resp = httpx.post("https://keymaster.cloud-monitor.com/token/refresh", json={
"refresh_token": stored_refresh_token,
"app_id": YOUR_APP_ID,
})
data = resp.json()
# {
# "access_token": "new-jwt...",
# "refresh_token": "new-refresh-token...",
# "expires_in": 900
# }
# IMPORTANT: Update BOTH tokens in your session store.
# The old refresh token is now revoked (rotation).
The refresh token is valid for 30 days and rotates on every use. As long as the user is active within the 30-day window, their session never expires.
Step 5: Logout
When the user logs out:
# 1. Revoke the refresh token (best-effort)
httpx.post("https://keymaster.cloud-monitor.com/token/revoke", json={
"refresh_token": stored_refresh_token,
})
# 2. Destroy local session
session.delete()
# 3. Redirect to Keymaster's branded logout page
redirect("https://keymaster.cloud-monitor.com/sso/logout"
"?app_id=YOUR_APP_ID"
"&post_logout_redirect_uri=https://yourapp.com")
What's Next?
- Authentication Guide — Detailed OAuth2 flows, SSO behavior, provider setup
- Token Lifecycle — Refresh rotation, replay detection, session management
- Server-to-Server — Client credentials for backend API calls
- API Reference — Complete endpoint documentation