Skip to main content

Scopes Catalog

Scopes describe what a token is permitted to do. ZewstID has four kinds of scopes, distinguished by who defines them and who enforces them:

KindDefined byEnforced byExamples
OIDC standardOIDC specToken consumers
openid
,
profile
,
email
,
offline_access
PlatformZewstIDZewstID's own M2M endpoints
api-keys:issue
,
users:invite
,
authz:write
App-declaredEach app's ownerThe app's own resource server
cal:read
,
cal:write
,
mail:send
Custom RBACEach app's owner via the Roles UIApp via the FGA enginePer-app role-permissions (RBAC Guide)

The platform and app-declared distinction is the important one for service accounts — see the Service Account Grants Guide.


OIDC standard scopes

ScopeAlways grantedWhat it adds to the token
openid
required
sub
claim. Required to make any flow OIDC-compliant.
profile
granted by default
name
,
given_name
,
family_name
,
preferred_username
,
picture
,
locale
,
zoneinfo
,
updated_at
claims
email
granted by default
email
,
email_verified
claims
offline_access
requires consentIssues a refresh token alongside the access token

When you create an OAuth client in the developer portal,

profile
and
email
are checked by default.
offline_access
is opt-in — enable it if your app needs to refresh tokens silently while the user is offline.

Authorization request example

GET /oauth/authorize? client_id=app_xxx &response_type=code &scope=openid+profile+email+offline_access &redirect_uri=https://yourapp.com/api/auth/callback/zewstid &code_challenge=... &code_challenge_method=S256 &state=...

Platform scopes

These gate ZewstID's own M2M endpoints. Granted to service accounts at creation time and stored on the SA itself (not on any per-app grant). The full catalog is also available at

GET /api/v1/auth/platform-scopes
.

ScopeGrants
users:read
Read user profile data via
GET /api/v1/users/:id
users:write
Update user profile data
users:invite
Send invitation emails (
POST /api/v1/m2m/users/invite
)
users:delete
Delete user accounts (GDPR)
api-keys:issue
Issue user-scoped API keys (
POST /applications/:appId/users/:sub/api-keys
)
api-keys:read
List/inspect API keys you've issued
api-keys:revoke
Revoke API keys
api-keys:introspect
Validate API keys at request time. Implied by any other
api-keys:*
scope
roles:read
Read role definitions and assignments
roles:manage
Create / delete roles, assign / unassign users
authz:check
Call the FGA authorization check endpoint
authz:write
Write FGA tuples / grant SA access to an app

Wildcards are supported:

users:*
grants every
users:
scope. The
*
super-scope grants everything (use sparingly — typically only for first-party administrative tooling).

App-declared scopes

Scopes your app's resource server enforces —

cal:read
,
mail:send
, etc. Defined per-app in the developer portal under Application → Settings → Declared Scopes. Independent of platform scopes; the same SA can have both.

App-declared scopes are stored:

  • On the service-account → app grant (FGA tuple
    condition.scopes
    ) — visible in the per-app Service Accounts tab
  • Stamped onto user-API-keys the SA issues for the app — propagated through the issued token

ZewstID itself doesn't enforce app-declared scopes; it just transports them. Your app's API checks them when a token / API key is presented.

Requesting scopes (Client Credentials)

curl -X POST https://api.zewstid.com/oauth/token \ -d 'grant_type=client_credentials' \ -d 'client_id=sa_xxx' \ -d 'client_secret=xxx' \ -d 'scope=users:invite users:read'

The returned token's

scope
claim lists the granted scopes; if you ask for a scope your service account isn't authorised for, the request returns
invalid_scope
rather than silently dropping it.


How to add a custom scope

Scopes specific to your application (e.g.,

appointments:write
,
mailbox:read
) are defined on the Roles tab of your application in the developer portal. They're checked at runtime via the FGA engine, not encoded in the access token. See the RBAC Guide and FGA Guide for the full pattern.

If you have a use case that genuinely needs a new ZewstID-managed scope (cross-application, identity-related), open a request in the developer portal — these are reviewed individually because new scopes affect every existing service account.


Validating scopes server-side

Resource servers should always check scope on the access token in addition to verifying the signature and audience:

const { payload } = await jwtVerify(token, JWKS, { issuer: 'https://auth.zewstid.com/realms/zewstid', audience: process.env.ZEWSTID_AUDIENCE, }); const granted = (payload.scope as string || '').split(' '); if (!granted.includes('users:invite') && !granted.includes('users:*') && !granted.includes('*')) { return res.status(403).json({ error: 'forbidden', error_description: 'missing users:invite scope' }); }

Wildcard handling is a small but easy-to-forget bit of glue — the snippet above is the canonical pattern.

Was this page helpful?

Let us know how we can improve our documentation