Skip to main content

Overview

OAuth scopes define what information and capabilities your app can access. Ave supports standard OpenID Connect scopes plus additional scopes for extended functionality.

Standard Scopes

These scopes are available to all OAuth apps:

openid

openid
scope
Required for OpenID Connect authentication. Grants access to the sub (subject) claim in the ID token.Grants access to:
  • Identity ID (sub claim)
  • Issuer information (iss claim)
ID Token Claims:
{
  "iss": "https://aveid.net",
  "sub": "identity-uuid",
  "aud": "your_client_id",
  "exp": 1234567890,
  "iat": 1234567890,
  "sid": "user-uuid"
}

profile

profile
scope
Grants access to the user’s basic profile information.Grants access to:
  • Display name
  • Handle (username)
  • Avatar URL
ID Token Claims:
{
  "name": "John Doe",
  "preferred_username": "[email protected]",
  "picture": "https://avatars.aveid.net/..."
}
UserInfo Response:
{
  "sub": "identity-uuid",
  "name": "John Doe",
  "preferred_username": "[email protected]",
  "picture": "https://avatars.aveid.net/..."
}

email

email
scope
Grants access to the user’s email address.Grants access to:
  • Email address
ID Token Claims:
{
  "email": "[email protected]"
}
UserInfo Response:
{
  "sub": "identity-uuid",
  "email": "[email protected]"
}

offline_access

offline_access
scope
Grants a refresh token for obtaining new access tokens without user interaction.Grants access to:
  • Refresh token in token response
Token Response:
{
  "access_token": "...",
  "refresh_token": "rt_a1b2c3d4e5f6...",
  "expires_in": 3600
}
Use cases:
  • Background data sync
  • Long-lived sessions
  • Mobile applications

Extended Scopes

These scopes require explicit app configuration:

user_id

user_id
scope
Grants access to the user’s permanent user ID (separate from identity ID).Requirements:
  • Your OAuth app must have allowUserIdScope: true
  • User must authorize this scope
Grants access to:
  • User UUID (permanent across all identities)
Token Response:
{
  "access_token": "...",
  "user_id": "user-uuid"
}
Access Token JWT Claims:
{
  "sub": "identity-uuid",
  "sid": "user-uuid",
  "uid": "user-uuid",
  "scope": "openid profile email user_id"
}
UserInfo Response:
{
  "sub": "identity-uuid",
  "user_id": "user-uuid"
}
The user_id is a permanent identifier that persists across identity changes. Use this scope only if you need to track users across multiple Ave identities.

Scope Validation

Ave validates scopes at multiple points:
1

Authorization request

When the user authorizes your app, Ave checks that all requested scopes are in your app’s allowedScopes configuration.
{
  "error": "invalid_scope",
  "error_description": "Invalid scopes: custom_scope"
}
2

Token exchange

When exchanging an authorization code, Ave verifies the scopes again to prevent tampering.
3

Token refresh

When refreshing tokens, the original scopes are maintained. You cannot request additional scopes during refresh.
4

Delegation

When exchanging tokens for delegation, Ave validates that requested scopes are available on the target resource and don’t exceed the delegation grant.

Requesting Scopes

Authorization Request

Request scopes when building the authorization URL:
import { buildAuthorizeUrl } from 'ave-sdk';

const authUrl = buildAuthorizeUrl(
  {
    clientId: 'your_client_id',
    redirectUri: 'https://yourapp.com/callback'
  },
  {
    scope: ['openid', 'profile', 'email', 'offline_access'],
    codeChallenge,
    codeChallengeMethod: 'S256'
  }
);

Default Scopes

If no scopes are specified:
  • Default scopes: ["openid", "profile", "email"]
  • Configured in your OAuth app’s allowedScopes

Space-Separated Format

Scopes can also be provided as a space-separated string:
const authUrl = buildAuthorizeUrl(config, {
  scope: 'openid profile email offline_access'
});

Checking Granted Scopes

From Token Response

const tokens = await exchangeCode(config, { code, codeVerifier });

console.log(tokens.scope);
// "openid profile email offline_access"

const scopes = tokens.scope.split(' ');
if (scopes.includes('email')) {
  console.log('Email access granted');
}

From Access Token JWT

import { decodeJwt } from 'jose';

const payload = decodeJwt(tokens.access_token_jwt);

const hasEmail = payload.scope.split(' ').includes('email');
const hasUserId = payload.uid !== undefined;

From UserInfo Endpoint

import { fetchUserInfo } from 'ave-sdk';

const userInfo = await fetchUserInfo(config, accessToken);

// If email is undefined, 'email' scope was not granted
if (userInfo.email) {
  console.log('Email:', userInfo.email);
} else {
  console.log('Email scope not granted');
}

Scope Combinations

Basic Profile

['openid', 'profile']
Minimal user information for display purposes.

Full Profile

['openid', 'profile', 'email']
Complete user profile with contact information.

Long-Lived Session

['openid', 'profile', 'email', 'offline_access']
Full profile with refresh token for background access.

User Tracking

['openid', 'profile', 'email', 'user_id', 'offline_access']
Full access with permanent user identifier.

Custom Resource Scopes

When creating delegation grants to other apps, you request custom scopes defined by the target resource:
import { buildConnectorUrl } from 'ave-sdk';

const connectorUrl = buildConnectorUrl(
  config,
  {
    resource: 'calendar-api',
    scope: 'read:events write:events delete:events',  // Custom scopes
    mode: 'user_present'
  }
);
Custom scope format:
  • Resource-specific scopes use the format: action:resource
  • Examples: read:events, write:data, admin:settings
  • Defined by each OAuth resource/app independently
  • Validated against the resource’s scopes configuration

Scope Best Practices

Only request scopes your app actually needs. Users are more likely to authorize apps that request fewer permissions.
// Good: Only what you need
scope: ['openid', 'profile']

// Bad: Requesting everything
scope: ['openid', 'profile', 'email', 'user_id', 'offline_access']
Users may deny certain scopes. Your app should work with reduced functionality:
const tokens = await exchangeCode(config, { code, codeVerifier });

if (!tokens.scope.includes('email')) {
  console.warn('Email not available - falling back to username');
  displayName = tokens.user.handle;
} else {
  displayName = tokens.user.email;
}
Clearly document which scopes your app requires for different features:
// Required scopes by feature:
const SCOPES = {
  basic: ['openid', 'profile'],
  email_notifications: ['openid', 'profile', 'email'],
  background_sync: ['openid', 'profile', 'offline_access'],
  user_analytics: ['openid', 'profile', 'user_id']
};
For OIDC compliance and ID token issuance, always include the openid scope:
// Good
scope: ['openid', 'profile', 'email']

// Bad: No ID token will be issued
scope: ['profile', 'email']

Scope Reference Table

ScopeTypeDescriptionGrants
openidStandardOIDC authenticationID token, identity ID
profileStandardUser profileDisplay name, handle, avatar
emailStandardEmail addressUser’s email
offline_accessStandardRefresh tokenLong-lived access
user_idExtendedPermanent user IDUser UUID across identities

Discovery

Query supported scopes via the OIDC discovery endpoint:
curl https://api.aveid.net/.well-known/openid-configuration
{
  "scopes_supported": [
    "openid",
    "profile",
    "email",
    "offline_access",
    "user_id"
  ]
}

Next Steps

Authorization Flow

Learn how to request scopes in the authorization flow

Token Exchange

Understand scope validation in token operations

Build docs developers (and LLMs) love