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:
| Kind | Defined by | Enforced by | Examples |
|---|---|---|---|
| OIDC standard | OIDC spec | Token consumers | openidprofileemailoffline_access |
| Platform | ZewstID | ZewstID's own M2M endpoints | api-keys:issueusers:inviteauthz:write |
| App-declared | Each app's owner | The app's own resource server | cal:readcal:writemail:send |
| Custom RBAC | Each app's owner via the Roles UI | App via the FGA engine | Per-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
| Scope | Always granted | What it adds to the token |
|---|---|---|
openid | required | sub |
profile | granted by default | namegiven_namefamily_namepreferred_usernamepicturelocalezoneinfoupdated_at |
email | granted by default | emailemail_verified |
offline_access | requires consent | Issues a refresh token alongside the access token |
When you create an OAuth client in the developer portal,
profileemailoffline_accessAuthorization 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| Scope | Grants |
|---|---|
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:* |
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:*users:*App-declared scopes
Scopes your app's resource server enforces —
cal:readmail:sendApp-declared scopes are stored:
- On the service-account → app grant (FGA tuple ) — visible in the per-app Service Accounts tab
condition.scopes - 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
scopeinvalid_scopeHow to add a custom scope
Scopes specific to your application (e.g.,
appointments:writemailbox:readIf 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