Skip to main content

Scopes

Scopes are the space-separated permission strings a client sends in the scope parameter when requesting authorization (each one names a kind of access, such as reading the user's e-mail address). LumoAuth displays the requested scopes on the consent screen so the user knows what access they are granting. A token is issued only for the intersection of requested scopes and scopes the client is allowed to request.

Standard OIDC Scopes​

ScopeClaims returned
openidsub — required for all OIDC flows and for UserInfo with a user token
profilename, given_name, family_name, picture, locale, updated_at
emailemail, email_verified
phonephone_number, phone_number_verified
addressaddress object
offline_accessAccepted and advertised; it does not decide whether a refresh_token is issued. That depends on the client's refresh_token grant (see Refresh tokens)

These scopes gate the matching claims in the ID token, UserInfo and JWT access tokens: an access token carries email only with email, name and other profile claims only with profile, and so on.

LumoAuth Extension Scopes​

ScopeDescription
rolesAccepted in requests, but does not add role names by itself: roles appears in access tokens and UserInfo only for clients that enable User roles in their JWT claims (see Claims and roles). The ID token never carries roles
groupsInclude the user's groups
permissionsInclude the user's granted permissions
note

The discovery document's scopes_supported lists only the standard OIDC scopes. These extension scopes (and any custom scopes you create) are valid in scope requests but are not advertised in discovery.

Admin API scopes​

An access token can be allowed to call the Admin API by carrying an admin scope. These scopes are only granted to OAuth clients created in the portal: a dynamically registered client never receives admin, admin:*, *:write, mcp:* or offline_access, even if it asks for them.

ScopeGrants
adminEvery Admin API resource, limited to what the signed-in user may do: each endpoint still requires its own permission
admin:<resource>:readRead endpoints of one resource, for example admin:users:read
admin:<resource>:writeRead and write endpoints of one resource, for example admin:users:write

The resource names (users, roles, groups, clients, webhooks, audit, social, agents, permissions, settings, analytics, sessions, abac) and what each unlocks are listed in the scope catalogue. Scopes are enforced per resource, and a :read scope never authorises a POST, PUT, PATCH or DELETE. A token without any admin scope gets 403 forbidden on the Admin API, even for an administrator.

User API scopes​

The organization APIs outside /admin act on the signed-in user's own account or on organization data the user may see. Since 2026-09-25 every such route requires an explicit scope when it is called with an access token; a token with only openid profile email gets 403 forbidden (This endpoint does not accept API keys or access tokens. when the route takes no credential at all). Request the scope in the authorization request and allow it on the client, like any other scope. The consent screen describes each one.

ScopeUnlocks
mfa:readGET /me/authenticators, GET /me/recovery-codes, GET /me/trusted-devices
mfa:manageEverything under /me/authenticators, /me/recovery-codes, /me/trusted-devices (enroll, verify, rename, remove, regenerate) and /mfa/challenges
mfa:challengeStart, poll and answer step-up challenges at /mfa/challenges
abac:readPOST /abac/check, POST /abac/check-bulk, GET /abac/my-attributes, GET /abac/attribute-definitions, GET /abac/resources/{type}/{id}/attributes
abac:writePUT /abac/users/{userId}/attributes/{slug} and PUT /abac/resources/{type}/{id}/attributes/{slug}; the user must also hold abac.manage
mcp:readGET /mcp/servers and GET /mcp/servers/{serverId}
jit:approveGET /jit/pending, POST /jit/approve/{requestId}, POST /jit/deny/{requestId}; the user must also hold jit.approve. See JIT: who can approve

Paths are relative to /orgs/{orgId}/api/v1. These scopes are never grantable to organization API keys (lmk_): they act as a person, not as an integration.

Routes that need no user scope: the OAuth/OIDC protocol endpoints under /oauth/, dynamic client registration, /.well-known/* discovery, SCIM (its own token), AAuth, the MCP challenge endpoint, workload attestation, the public agent card, GET /me, GET /agents/me, POST /agents/ask and the self-decision Zanzibar check/expand (asking about another subject still needs authz:check). Agent and JIT credentials are governed by the agent capability model instead of these scopes.

Agent registration (POST /agents/register) with an access token needs admin or admin:agents:write; see Agent Registry.

Fresh sign-in for sensitive calls​

POST /me/authenticators/{type}/enroll and POST /me/recovery-codes also require the token's auth_time to be at most 10 minutes old. Otherwise the answer is 401 insufficient_user_authentication with WWW-Authenticate: Bearer error="insufficient_user_authentication", …, max_age=600 (a step-up authentication challenge). Re-run the authorization request with max_age=600 and retry. See the MFA API.

Common mistakes​

  • 403 on /me/authenticators with a token that works on /oauth/userinfo. The token has no mfa:read/mfa:manage. Add the scope to the client and request it.
  • 403 on /jit/approve with a working token. The token lacks jit:approve, the user lacks the jit.approve permission, or the token is a JIT, agent or delegated token.
  • 401 insufficient_user_authentication on enroll. The user signed in more than 10 minutes ago. Send them through /authorize with max_age=600.

Custom Scopes​

Create organization-specific scopes via the admin API (the portal has no scope catalogue page; there you list the scopes an application may request under Applications → your app → Configuration → Allowed scopes):

GET|POST /orgs/{orgId}/api/v1/admin/scopes
DELETE /orgs/{orgId}/api/v1/admin/scopes/{scopeId}

GET lists scopes and POST creates one on /admin/scopes; deletion targets a specific scope by ID on /admin/scopes/{scopeId}.

Create a Custom Scope
curl -X POST https://app.lumoauth.dev/orgs/acme-corp/api/v1/admin/scopes \
-H "Authorization: ApiKey lmk_abc123" \
-H "Content-Type: application/json" \
-d '{
"name": "read:reports",
"description": "Read access to financial reports",
"isDefault": false
}'

Custom scopes must be explicitly allowed per OAuth client.

Scope names are unique within an organization. Creating a name that already exists in your organization, or that matches a system scope (openid, profile, admin, and so on), returns 409 conflict with "message": "A scope with this name already exists". Another organization may use the same custom name; scopes never collide across organizations.

The endpoint requires admin:permissions:write (listing requires admin:permissions:read).

Requesting Scopes​

Include scopes as a space-separated list in the scope parameter:

scope=openid profile email read:reports

LumoAuth grants only the intersection of requested scopes and scopes allowed for the client. The scope field in the token response reflects what was actually granted.

Standards used on this page​

Plain nameStandard
OpenID Connect (OIDC)OpenID Connect Core 1.0
Dynamic client registrationRFC 7591 and RFC 7592
Step-up authentication challengeRFC 9470

See the standards reference for what each one covers and how LumoAuth uses it.