RegisterLogin
DocsPricing
RegisterLogin
  • Getting Started
  • Introduction
  • Quick Start
  • SDKs
  • React
  • TypeScript
  • Next.js
  • Express
  • NestJS
  • Python
  • API Reference
  • Support and Resources
  • FAQ
  • Contact Support

AuthSafe

Product

HighlightFeatureIntegrationPricingFAQ

Company

AboutBlogContactSitemap

Developer

DashboardDocumentation

Legal

Terms & ConditionsPrivacyComplianceShippingCancellation

© 2026 AuthSafe. All rights reserved.

We value your privacy

This website uses cookies for anonymous analytics to help us improve your experience. No personal information is stored or shared. You can allow or reject analytics tracking at any time. See our Privacy Policy.

We use cookies for anonymous analytics. No personal info is stored. See our Privacy Policy.

Authorization & Access Control

Implement fine-grained access control in your applications using OAuth 2.0 scopes, roles, and custom permissions.
OAuth 2.0 Scopes
RBAC
Fine-Grained Control

Understanding Authorization

Authentication verifies who a user is, while Authorization determines what they can access. AuthSafe handles both:
  • Authentication: Confirms user identity through the OAuth 2.0/OIDC flow
  • Authorization: Controls resource access through scopes and permissions

OAuth 2.0 Scopes

Scopes define the level of access granted to an access token. They are requested during the authorization flow and included in the issued tokens.

Standard Scopes

AuthSafe supports the following OpenID Connect scopes:
ScopeDescriptionClaims Included
openidRequired for OIDC authenticationsub
profileAccess to user's profile informationname, picture, updated_at
emailAccess to user's email addressemail, email_verified
offline_accessRequest a refresh token(enables refresh token issuance)

Custom Scopes

You can define custom scopes for your application to represent specific permissions:
read:documents    - Read access to documents
write:documents   - Write access to documents
admin:users       - Administrative access to user management
api:reports       - Access to reporting APIs

Requesting Scopes

Request scopes during the authorization flow:
curl "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"

Validating Scopes

After receiving an access token, validate that it contains the required scopes:
// 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 action

Role-Based Access Control (RBAC)

AuthSafe supports role-based access control where users are assigned roles, and roles have associated permissions.

Common Role Patterns

Admin Role:
  - Full system access
  - User management
  - Configuration access

Editor Role:
  - Create and edit content
  - View analytics

Viewer Role:
  - Read-only access
  - View content

Implementing RBAC

  1. Define roles in your AuthSafe organization
  2. Assign roles to users
  3. Include role claims in access tokens
  4. Enforce role checks in your application
// 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);

Token-Based Authorization

Access tokens contain claims that can be used for authorization decisions.

Access Token Structure

{
  "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"]
}

Verifying Access Tokens

Always verify tokens on your backend before granting access:
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);
    });
  });
}

Authorization Patterns

Pattern 1: Scope-Based Authorization

Best for API access and general resource permissions.
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);
});

Pattern 2: Role-Based Authorization

Best for user interface features and workflow control.
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 });
});

Pattern 3: Ownership-Based Authorization

Best for user-specific resources.
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);
});

Best Practices

1. Principle of Least Privilege

Grant only the minimum permissions necessary:
  • Request only required scopes
  • Assign minimal roles
  • Review and revoke unused permissions regularly

2. Defense in Depth

Implement authorization at multiple layers:
Frontend

Basic UI visibility controls

API Gateway

Scope and rate limit validation

Backend API

Full token verification and business logic

Database

Row-level security policies

3. Secure Token Handling

Critical Security Practices
  • Never log tokens - Tokens in logs can be exploited
  • Use HTTPS only - Prevent token interception
  • Validate on every request - Don't cache authorization decisions
  • Check expiration - Reject expired tokens immediately

4. Centralize Authorization Logic

// 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();
}

5. Audit Authorization Events

Log authorization decisions for security monitoring:
function logAuthorizationEvent(userId: string, resource: string, action: string, granted: boolean) {
  logger.info('Authorization decision', {
    user_id: userId,
    resource,
    action,
    granted,
    timestamp: new Date().toISOString()
  });
}

Common Scenarios

Multi-Tenant Applications

Ensure users can only access their organization's data:
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-to-Service Authorization

Use client credentials for backend services:
// 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}` }
  });
}

Testing Authorization

Unit Tests

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');
  });
});

Next Steps

API Endpoints

Complete API reference for all authentication endpoints.

View API Reference →

Quick Start

Get started with AuthSafe in minutes with our integration guide.

Start Integrating →

React SDK

Frontend integration with React hooks and components.

View React SDK →