Skip to main content

Scopes

Scopes are the space-separated permission strings that 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 that 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 determine whether a refresh_token is issued. That depends on the client's refresh_token grant (see Refresh tokens)

These scopes control whether the matching claims appear 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 that 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 scope catalogue lists the resource names (users, roles, groups, clients, webhooks, audit, social, agents, permissions, settings, analytics, sessions, abac) and what each one unlocks. 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 that 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 can never be granted to organization API keys (lmk_): they act as a person, not as an integration.

Some routes need no user scope: the OAuth/OIDC protocol endpoints under /oauth/, dynamic client registration, /.well-known/* discovery, SCIM (its own token), AAuth, the challenge endpoint for MCP, workload attestation and the public agent card. The same applies to 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 capability model for agents 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 page for the scope catalogue; there you list the scopes that an application may request under Applications → your app → Configuration → Allowed scopes.) The endpoints are:

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 one returns 409 conflict with "message": "A scope with this name already exists" when the name already exists in your organization or matches a system scope (openid, profile, admin, and so on). 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.