Backend / Service Track
Use this track when you are protecting a REST or GraphQL API, a background worker, or a service-to-service call. By the end you will be issuing and validating tokens and making authorization decisions from your service.
Prerequisites
- A LumoAuth organization — sign up
- A confidential OAuth client (client ID + client secret). "Confidential" means the client can safely hold a secret — unlike a browser or mobile app — so it is allowed to use client-credential based grants.
1. Learn the model
- Core Concepts — organizations, users, OAuth clients, tokens
- Authorization Overview — when to pick RBAC, ABAC, or Zanzibar
2. Get an access token (Client Credentials)
The client_credentials grant is part of core OAuth 2.0 (the sign-in and token protocol every flow on this site builds on). It issues a token to the service itself, with no end user involved — useful for cron jobs, server-to-server calls, and background workers.
curl -X POST https://app.lumoauth.dev/orgs/YOUR_ORG/api/v1/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET" \
-d "scope=read:users"
The response is a JWT access_token scoped to your app. See the Token Endpoint reference for all supported grants (authorization_code, refresh_token, token-exchange, CIBA, device).
Pick the narrowest credential. An API key with one resource scope (for example admin:users:read) is the simplest. If you use an OAuth token instead, it must carry admin or admin:<resource>:<read|write>; a plain openid profile email token gets 403. Neither credential ever creates a browser session. See Credentials and access model and Admin API authentication.
3. Validate tokens in your service
Pick one of two strategies:
| Strategy | When to use | Endpoint |
|---|---|---|
| Local JWT validation | High-throughput services; no network hop per request | .well-known/jwks.json (JWKS, the list of public keys used to verify tokens) |
| Token introspection | You need instant revocation to take effect | /oauth/introspect |
Local validation means your service fetches the public keys from jwks.json (cached), then verifies the JWT (signed token) signature offline. Introspection means asking LumoAuth on every request whether the token is still valid — slower, but LumoAuth can deny a revoked token immediately.
Whichever you pick, check iss against the issuer in the discovery document (byte-compare), aud against your resource, and exp. The issuer is https://app.lumoauth.dev/orgs/YOUR_ORG/api/v1, or your bare custom domain, and is identical everywhere a token or introspection response is produced.
The issuer identifier in the response prevents mix-up attacks, where a code or token from one authorization server is presented to a client expecting another. The one thing to do: compare iss with the discovery issuer before trusting anything else in the token. See the standards reference for the specification behind it.
4. Make authorization decisions
Your service now has an authenticated caller. Decide what they are allowed to do.
- Simple role checks: Permissions API
- Contextual rules (time of day, IP, resource attributes): ABAC Policies
- Relationship-based (owner / team / folder hierarchy): Zanzibar / ReBAC
- Natural-language rules: AI Policy Authoring
5. Production checklist
- Credentials and access model — least-privilege keys, admin scopes, stateless credentials
- Security best practices — the integrator checklist
- Rate Limiting
- Webhooks — receive user-lifecycle events
- Audit Logs — record of who did what
- Observability — Datadog and Axiom integration
- SDKs & Libraries
Common mistakes
403 forbiddenon/api/v1/admin/...with a valid OAuth token. The token lacks an admin scope. Addadmin:<resource>:<read|write>to a first-party client's allowed scopes and request it, or use an API key.403 insufficient scopewith an API key on aPOST. The key only has a:readscope for that resource.- Expecting a session cookie after an API call. API keys and Bearer tokens are stateless by design; send the credential on every request.
- Pinning the issuer to an old value. Read
issuerfrom discovery instead of hard-coding it.
Next track
- Agents or LLM tool calls? → AI Agents Track
- Enterprise SSO and SCIM? → Admin / IT Track
Standards used on this page
| Plain name | Standard |
|---|---|
| OAuth 2.0 (the core sign-in and token protocol) | RFC 6749 |
| Token introspection | RFC 7662 |
| Issuer identifier in the response | RFC 9207 |
See the standards reference for what each one covers and how LumoAuth uses it.