Scopes and Claims
Scopes define what data your application can access, and claims are the actual pieces of user information returned. Understanding both is essential for requesting the right permissions.
What are Scopes?
Scopes are OAuth 2.0 permissions that your application requests from the user. They determine what data your app can access.
Example authorization request:
GET /auth?scope=openid profile email&...
Standard Scopes
openid (Required)
The openid scope is required for OIDC authentication. It indicates that you want an ID token.
Returns:
sub- Unique user identifier
{
"sub": "user_123abc"
}
profile
Access to the user's basic profile information.
Returns:
name- Full namepreferred_username- Username or handlepicture- Profile picture URL
{
"sub": "user_123abc",
"name": "John Doe",
"preferred_username": "johnd",
"picture": "https://cdn.consentkeys.com/avatars/..."
}
email
Access to the user's email address.
Returns:
email- Email addressemail_verified- Whether email is verified (always true for ConsentKeys)
{
"sub": "user_123abc",
"email": "john@example.com",
"email_verified": true
}
address
Access to the user's physical address (if provided).
Returns:
address- Formatted address object
{
"sub": "user_123abc",
"address": {
"formatted": "123 Main St, Springfield, IL 62701, USA",
"street_address": "123 Main St",
"locality": "Springfield",
"region": "IL",
"postal_code": "62701",
"country": "USA"
}
}
Scope Combinations
You can request multiple scopes by separating them with spaces:
scope=openid profile email
Common combinations:
| Combination | Use Case |
|---|---|
openid | Minimal authentication (just user ID) |
openid profile | Basic profile display |
openid email | Email-based identification |
openid profile email | Full user profile (recommended) |
openid profile email address | Complete user information |
Claims Explained
Claims are name-value pairs that contain information about the user. They appear in:
- ID tokens (JWT)
- UserInfo endpoint response
Standard Claims
| Claim | Type | Description | Scope Required |
|---|---|---|---|
sub | string | Unique user identifier | openid |
iss | string | Token issuer (ConsentKeys) | openid |
aud | string | Audience (your client_id) | openid |
exp | number | Expiration timestamp | openid |
iat | number | Issued at timestamp | openid |
name | string | Full name | profile |
preferred_username | string | Username | profile |
picture | string | Avatar URL | profile |
email | string | Email address | email |
email_verified | boolean | Email verified status | email |
address | object | Physical address | address |
ID Token Example
When you exchange an authorization code for tokens, the ID token contains these claims:
{
"sub": "user_123abc",
"iss": "https://pseudoidc.consentkeys.com",
"aud": "ck_your_client_id",
"exp": 1703980800,
"iat": 1703977200,
"nonce": "random_replay_protection",
"name": "John Doe",
"preferred_username": "johnd",
"picture": "https://cdn.consentkeys.com/avatars/...",
"email": "john@example.com",
"email_verified": true
}
UserInfo Response Example
The /userinfo endpoint returns the same claims (without token-specific ones):
{
"sub": "user_123abc",
"name": "John Doe",
"preferred_username": "johnd",
"picture": "https://cdn.consentkeys.com/avatars/...",
"email": "john@example.com",
"email_verified": true
}
Dynamic Consent Screen
The consent screen dynamically shows what data you're requesting based on scopes:
Requested scopes: openid profile email
┌────────────────────────────────────────┐
│ │
│ YourApp wants to access: │
│ │
│ ✓ Your email address │
│ ✓ Your profile information │
│ (name, username, profile picture) │
│ │
│ [Allow] [Deny] │
│ │
└────────────────────────────────────────┘
Requesting Minimal Scopes
Best practice: Only request the scopes you actually need.
❌ Over-requesting
// Don't request everything if you only need email
const scopes = 'openid profile email address';
✅ Minimal Request
// Only request what you need
const scopes = 'openid email';
Benefits:
- Higher consent approval rates
- Better user trust
- Compliance with data minimization principles
Conditional Claims
Some claims are only included when certain scopes are requested:
// Only openid scope
GET /userinfo
Authorization: Bearer <token>
// Response - minimal claims
{
"sub": "user_123abc"
}
// With profile and email scopes
// Response - additional claims
{
"sub": "user_123abc",
"name": "John Doe",
"email": "john@example.com",
"email_verified": true
}
Verifying ID Tokens
ID tokens are JWTs that must be verified before use:
1. Verify Signature
import { jwtVerify } from 'jose';
const JWKS_URL = 'https://pseudoidc.consentkeys.com/.well-known/jwks.json';
async function verifyIdToken(idToken: string) {
const JWKS = createRemoteJWKSet(new URL(JWKS_URL));
const { payload } = await jwtVerify(idToken, JWKS, {
issuer: 'https://pseudoidc.consentkeys.com',
audience: 'ck_your_client_id',
});
return payload;
}
2. Verify Claims
function validateClaims(claims: any) {
// Check expiration
if (Date.now() >= claims.exp * 1000) {
throw new Error('Token expired');
}
// Verify issuer
if (claims.iss !== 'https://pseudoidc.consentkeys.com') {
throw new Error('Invalid issuer');
}
// Verify audience
if (claims.aud !== 'ck_your_client_id') {
throw new Error('Invalid audience');
}
// Verify nonce (if used)
if (claims.nonce !== sessionStorage.getItem('nonce')) {
throw new Error('Invalid nonce');
}
return true;
}
Scope Changes
Adding Scopes Later
If you need to request additional scopes after initial authentication:
// Redirect to authorization with additional scopes
const authUrl = `https://pseudoidc.consentkeys.com/auth?
client_id=ck_your_client_id&
scope=openid profile email address& // Now requesting address
prompt=consent`; // Force consent screen
window.location.href = authUrl;
The prompt=consent parameter forces the consent screen even if the user previously approved.
Removing Scopes
To reduce requested scopes, simply omit them in future authorization requests. Previously granted permissions remain until explicitly revoked by the user.
User Consent Management
Users can view and revoke granted permissions through:
- The ConsentKeys user dashboard
- Your app's settings (if you implement revocation)
Checking Current Scopes
// Decode the access token to see granted scopes
import { decodeJwt } from 'jose';
const token = 'eyJhbGciOiJSUzI1NiIs...';
const decoded = decodeJwt(token);
console.log(decoded.scope); // "openid profile email"
Revoking Access
Users can revoke your app's access at any time. Handle this gracefully:
async function callAPI() {
try {
const response = await fetch('/api/data', {
headers: {
Authorization: `Bearer ${accessToken}`
}
});
if (response.status === 401) {
// Token expired or revoked - re-authenticate
redirectToLogin();
}
} catch (error) {
console.error('API call failed:', error);
}
}
Privacy Considerations
Data Minimization
Only request and store the data you need:
// ❌ Storing everything
const user = {
sub: claims.sub,
name: claims.name,
email: claims.email,
picture: claims.picture,
// ... everything else
};
// ✅ Only storing what's needed
const user = {
id: claims.sub,
email: claims.email,
};
Pseudonymous Identifiers
The sub claim is a pseudonymous identifier:
- Unique per user
- Consistent across sessions
- Cannot be reverse-engineered to reveal the email
// Example sub value
"sub": "user_7f8a9b2c1d3e4f5a6b7c8d9e0f1a2b3c"
Scope Descriptions
When requesting scopes, users see clear descriptions:
| Scope | User Sees |
|---|---|
profile | "Your profile information (name, username, profile picture)" |
email | "Your email address" |
address | "Your physical address" |
Testing Scopes
Development Mode
Test different scope combinations during development:
// Test minimal scopes
const minimalScopes = 'openid';
// Test common scopes
const commonScopes = 'openid profile email';
// Test all scopes
const allScopes = 'openid profile email address';
Consent Screen Preview
The consent screen adapts based on requested scopes. Test each combination to ensure good UX.
Next Steps
- Understand the Authentication Flow
- Learn about Magic Links
- Check Error Codes
- Explore the API Reference