OIDC claims
Auth Service injects custom claims into the id_token, the userinfo endpoint response and, when the access token is a JWT, the access token, for every OAuth 2.1 authorization. Claims are scoped to the requesting application — two applications that share the same user pool get independent claim sets.
Available scopes
Request these scopes in the authorization URL to receive the corresponding claims. The application's allowedScopes must include them:
| Scope | Claim added | Description |
|---|---|---|
openid | sub | Required for OIDC. The ID token also carries iss, aud, iat and exp. |
profile | name, picture, updated_at, company | Basic user profile. Each claim is present only when the user has a value. |
email | email, email_verified | User email address. |
phone | phone_number | User phone number (when the user has one). |
roles | roles | Array of role names assigned to the user in this app. |
permissions | permissions | Flat array of permission strings. |
features | features, plan | JSON object of subscription plan feature flags, and the plan name. |
wallet:self:read | — | Authorizes bearer access to the signed-in user's wallet read API when resource=<BETTER_AUTH_URL>. |
org | org_id | Organization ID (multi-tenant). Added to the access token only. |
offline_access | — | Issues a refresh_token. |
Claim formats
roles
An array of role name strings:
{ "roles": ["editor", "beta-tester"] }
Roles are defined per-application via the Roles & permissions admin API.
permissions
A flat array of permission strings derived from the roles assigned to the user. The format is:
"resource"— read permission (action isread)"resource.write"— write permission (action iswrite)
{ "permissions": ["articles", "comments.write", "media"] }
Permissions are derived in getUserClaims() (src/services/claims.ts): for each role the user holds in the application, all linked permissions are resolved and aggregated per resource. When the user holds both read and write on the same resource, only resource.write is emitted.
features
A free-form JSON object from the user's active subscription plan. The shape is whatever you define when creating the plan:
{
"features": {
"maxProjects": 5,
"exportEnabled": false,
"storageGb": 10
},
"plan": "pro"
}
plan is the name of the plan. If the user has no active subscription for the application, or the subscription has expired, features is an empty object {} and plan is absent.
org_id
With the org scope, the access token carries org_id. A user who belongs to exactly one organization is bound to it implicitly. A user who belongs to several is redirected to /select-org during authorization to pick one. A user in no organization gets no org_id. The claim is not added to the ID token or to userinfo. See Organizations.
client_attrs
When the application has metadata, its string entries are emitted under the client_attrs claim of its tokens (ID token, access token, userinfo; also client_credentials tokens).
Access gate
Token issuance is guarded by the same checks for the ID token and for a JWT access token (so omitting the openid scope does not bypass them). It fails with a FORBIDDEN error if:
- The user is banned.
- The application does not exist or is inactive.
- The user has no active
userApplicationsrecord for the requesting application. If the record exists but is inactive, access is refused as revoked. If there is no record at all, a record is created automatically when the application is public or hasallowRegisterturned on; otherwise the token is refused. - The application has
isMfaRequired: true(or the user is individually flagged as MFA-required) and the user has not enabled two-factor authentication (TOTP). - The application restricts its social providers and none of the user's linked accounts uses an allowed one.
This means you do not need a separate "check if user has access" API call — the token will simply not be issued.
Discovery endpoints
Auth Service exposes the OIDC / OAuth 2.1 discovery documents at the issuer root (i.e. BETTER_AUTH_URL/…). The other endpoints are served by BetterAuth under /api/auth/…:
| Path | Description |
|---|---|
/.well-known/openid-configuration | OIDC discovery document (issuer root) |
/.well-known/oauth-authorization-server | OAuth 2.0 Authorization Server Metadata, RFC 8414 (issuer root) |
/api/auth/jwks | JSON Web Key Set (public keys for token verification) |
/api/auth/oauth2/authorize | Authorization endpoint |
/api/auth/oauth2/token | Token endpoint |
/api/auth/oauth2/userinfo | UserInfo endpoint |