| Scope | Description | Claims Included |
|---|---|---|
openid | Required for OIDC authentication | sub |
profile | Access to user's profile information | name, picture, updated_at |
email | Access to user's email address | email, email_verified |
offline_access | Request a refresh token | (enables refresh token issuance) |
read:documents - Read access to documents
write:documents - Write access to documents
admin:users - Administrative access to user management
api:reports - Access to reporting APIscurl "https://identities.authsafe.in/auth/authorize?client_id=your_client_id&redirect_uri=https://yourapp.com/callback&response_type=code&scope=openid%20profile%20email%20read:documents&state=random_state&code_challenge=CODE_CHALLENGE&code_challenge_method=S256"// Decode the JWT access token
const decoded = jwt.decode(accessToken);
// Check for required scopes
if (!decoded.scope.includes('read:documents')) {
throw new Error('Insufficient permissions');
}
// Or check multiple scopes
const requiredScopes = ['read:documents', 'write:documents'];
const hasPermission = requiredScopes.every(scope =>
decoded.scope.includes(scope)
);
// Proceed with authorized actionAdmin Role:
- Full system access
- User management
- Configuration access
Editor Role:
- Create and edit content
- View analytics
Viewer Role:
- Read-only access
- View content// Example: Middleware to check user roles
function requireRole(requiredRole: string) {
return (req, res, next) => {
const token = req.headers.authorization?.split(' ')[1];
const decoded = jwt.verify(token, publicKey);
const userRoles = decoded.roles || [];
if (!userRoles.includes(requiredRole)) {
return res.status(403).json({
error: 'Forbidden',
message: 'Insufficient permissions'
});
}
next();
};
}
// Usage
app.get('/admin/users', requireRole('admin'), getUsersHandler);{
"sub": "user_123",
"iss": "https://identities.authsafe.in",
"aud": "your_client_id",
"exp": 1234567890,
"iat": 1234564290,
"scope": "openid profile email read:documents",
"roles": ["editor", "analyst"],
"org_id": "org_456",
"permissions": ["documents:read", "documents:write"]
}import jwt from 'jsonwebtoken';
import jwksClient from 'jwks-rsa';
// Configure JWKS client to fetch public keys
const client = jwksClient({
jwksUri: 'https://identities.authsafe.in/.well-known/jwks.json'
});
function getKey(header, callback) {
client.getSigningKey(header.kid, (err, key) => {
const signingKey = key.publicKey || key.rsaPublicKey;
callback(null, signingKey);
});
}
// Verify token
function verifyToken(token: string): Promise<any> {
return new Promise((resolve, reject) => {
jwt.verify(token, getKey, {
audience: 'your_client_id',
issuer: 'https://identities.authsafe.in',
algorithms: ['RS256']
}, (err, decoded) => {
if (err) reject(err);
else resolve(decoded);
});
});
}app.get('/api/documents', async (req, res) => {
const token = await verifyToken(req.headers.authorization);
if (!token.scope.includes('read:documents')) {
return res.status(403).json({ error: 'Forbidden' });
}
// Fetch and return documents
const documents = await getDocuments();
res.json(documents);
});app.delete('/api/users/:id', async (req, res) => {
const token = await verifyToken(req.headers.authorization);
if (!token.roles.includes('admin')) {
return res.status(403).json({ error: 'Admin access required' });
}
// Delete user
await deleteUser(req.params.id);
res.json({ success: true });
});app.put('/api/documents/:id', async (req, res) => {
const token = await verifyToken(req.headers.authorization);
const document = await getDocument(req.params.id);
// Check if user owns the document or is an admin
if (document.owner_id !== token.sub && !token.roles.includes('admin')) {
return res.status(403).json({ error: 'Not authorized' });
}
// Update document
const updated = await updateDocument(req.params.id, req.body);
res.json(updated);
});Basic UI visibility controls
Scope and rate limit validation
Full token verification and business logic
Row-level security policies
// Good: Centralized authorization service
class AuthorizationService {
async checkPermission(token: string, resource: string, action: string): Promise<boolean> {
const decoded = await verifyToken(token);
// Check scopes
if (!decoded.scope.includes(`${action}:${resource}`)) {
return false;
}
// Check custom business rules
if (resource === 'sensitive_data' && !decoded.roles.includes('admin')) {
return false;
}
return true;
}
}
// Usage
if (!await authService.checkPermission(token, 'documents', 'write')) {
throw new ForbiddenError();
}function logAuthorizationEvent(userId: string, resource: string, action: string, granted: boolean) {
logger.info('Authorization decision', {
user_id: userId,
resource,
action,
granted,
timestamp: new Date().toISOString()
});
}app.get('/api/reports', async (req, res) => {
const token = await verifyToken(req.headers.authorization);
// Filter by user's organization
const reports = await getReports({
org_id: token.org_id
});
// Additional check: user must have reporting permission
if (!token.scope.includes('read:reports')) {
return res.status(403).json({ error: 'Forbidden' });
}
res.json(reports);
});// Service A calls Service B
async function callServiceB() {
// Get service token
const tokenResponse = await fetch('https://identities.authsafe.in/auth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'client_credentials',
client_id: process.env.SERVICE_CLIENT_ID,
client_secret: process.env.SERVICE_CLIENT_SECRET,
scope: 'api:read'
})
});
const { access_token } = await tokenResponse.json();
// Call Service B with token
return fetch('https://service-b.example.com/api/data', {
headers: { Authorization: `Bearer ${access_token}` }
});
}describe('Document authorization', () => {
it('should allow access with valid scope', async () => {
const token = createMockToken({ scope: 'read:documents' });
const result = await checkDocumentAccess(token);
expect(result).toBe(true);
});
it('should deny access without required scope', async () => {
const token = createMockToken({ scope: 'read:reports' });
const result = await checkDocumentAccess(token);
expect(result).toBe(false);
});
it('should deny access with expired token', async () => {
const token = createMockToken({
scope: 'read:documents',
exp: Math.floor(Date.now() / 1000) - 3600
});
await expect(checkDocumentAccess(token)).rejects.toThrow('Token expired');
});
});