Auth0 Implementation Plan for REC Verifiable Credentialing Platform¶
Overview¶
This document outlines the implementation plan for integrating Auth0 as the identity management solution for the REC Verifiable Credentialing Platform. Auth0 is a flexible, drop-in solution to add authentication and authorization services to applications. It provides a universal authentication & authorization platform for web, mobile, and legacy applications.
Alignment with Current Requirements¶
Auth0 directly addresses several key authentication and security requirements from the REC Platform specifications:
| Requirement | How Auth0 Addresses It |
|---|---|
| OAuth 2.0 / OpenID Connect | Auth0 is built on OAuth 2.0 and OpenID Connect standards |
| Multi-factor Authentication | Auth0 provides robust MFA options including SMS, email, authenticator apps, and WebAuthn |
| Passkey Implementation | Auth0 supports WebAuthn/FIDO2 which enables passkey authentication |
| Security | Auth0 provides enterprise-grade security with features like anomaly detection and brute force protection |
| Audit Logs | Auth0 maintains detailed, immutable audit logs of all authentication events |
Architecture Integration¶
System Architecture with Auth0¶
flowchart TB
subgraph "Client Layer"
WebBrowser["Web Browser"]
MobileApp["Mobile App"]
AdminDashboard["Admin Dashboard"]
end
subgraph "Auth0 Tenant"
Auth0["Auth0 Authentication"]
Auth0Rules["Auth0 Rules & Actions"]
Auth0MFA["Multi-factor Authentication"]
Auth0Logs["Audit Logs"]
end
subgraph "NestJS Application"
subgraph "Static File Serving"
ReactApp["React Frontend (Built)"]
end
subgraph "API Layer"
APIController["API Controllers"]
AuthGuard["Auth Guards"]
end
subgraph "Application Services Layer"
AuthService["Authentication Service"]
IssuerService["Credential Issuer Service"]
VerificationService["Credential Verification Service"]
UserService["User Management Service"]
AdminService["Admin Service"]
PaymentService["Payment Processing Service"]
end
end
subgraph "Data Storage Layer"
PostgreSQL[(PostgreSQL Database)]
end
%% Client to Auth0 connections
WebBrowser --> Auth0
MobileApp --> Auth0
AdminDashboard --> Auth0
%% Auth0 to Application connections
Auth0 --> ReactApp
Auth0 --> AuthGuard
%% API Guards to Services
AuthGuard --> APIController
APIController --> AuthService
APIController --> IssuerService
APIController --> VerificationService
APIController --> UserService
APIController --> AdminService
APIController --> PaymentService
%% Services to Database
AuthService --> PostgreSQL
IssuerService --> PostgreSQL
VerificationService --> PostgreSQL
UserService --> PostgreSQL
AdminService --> PostgreSQL
PaymentService --> PostgreSQL
Authentication Flow¶
sequenceDiagram
participant User
participant Frontend as React Frontend
participant Auth0
participant Backend as NestJS Backend
participant DB as Database
User->>Frontend: Access Application
Frontend->>Auth0: Redirect to Auth0 Login
Auth0->>User: Present Login UI
User->>Auth0: Enter Credentials
alt MFA Required
Auth0->>User: Request MFA
User->>Auth0: Provide MFA
end
Auth0->>Auth0: Validate Credentials
Auth0->>Frontend: Return Access & ID Tokens
Frontend->>Frontend: Store Tokens
User->>Frontend: Request Protected Resource
Frontend->>Backend: API Request with Access Token
Backend->>Backend: Validate Token
Backend->>DB: Query Data
DB->>Backend: Return Data
Backend->>Frontend: Return Protected Resource
Frontend->>User: Display Protected Resource
Implementation Details¶
1. Auth0 Tenant Setup¶
- Create an Auth0 tenant for the REC Platform
- Configure domain settings and branding
- Set up appropriate connections (database, social, enterprise)
- Configure MFA settings, requiring MFA for admin accounts
- Set up email templates for verification and password reset
2. Backend Integration (NestJS)¶
Install Required Packages¶
cd backend
npm install @nestjs/passport passport passport-jwt jwks-rsa express-jwt
Configure Auth0 Authentication Module¶
// backend/src/auth/auth.module.ts
import { Module } from '@nestjs/common';
import { PassportModule } from '@nestjs/passport';
import { JwtStrategy } from './jwt.strategy';
import { ConfigModule, ConfigService } from '@nestjs/config';
@Module({
imports: [
PassportModule.register({ defaultStrategy: 'jwt' }),
ConfigModule,
],
providers: [JwtStrategy],
exports: [PassportModule],
})
export class AuthModule {}
Create JWT Strategy¶
// backend/src/auth/jwt.strategy.ts
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { passportJwtSecret } from 'jwks-rsa';
import { ConfigService } from '@nestjs/config';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(configService: ConfigService) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKeyProvider: passportJwtSecret({
cache: true,
rateLimit: true,
jwksRequestsPerMinute: 5,
jwksUri: `https://${configService.get('auth.domain')}/.well-known/jwks.json`,
}),
audience: configService.get('auth.audience'),
issuer: `https://${configService.get('auth.domain')}/`,
algorithms: ['RS256'],
});
}
async validate(payload: any) {
// You can add custom validation logic here
return payload;
}
}
Create Auth Guard¶
// backend/src/auth/jwt-auth.guard.ts
import { Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {}
Create Role Guard for Admin Access¶
// backend/src/auth/roles.guard.ts
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const requiredRoles = this.reflector.getAllAndOverride<string[]>('roles', [
context.getHandler(),
context.getClass(),
]);
if (!requiredRoles) {
return true;
}
const { user } = context.switchToHttp().getRequest();
return requiredRoles.some((role) =>
user.permissions?.includes(role) ||
user['https://rec-platform.com/roles']?.includes(role)
);
}
}
Update Configuration¶
// backend/src/config/configuration.ts
export default () => ({
// ... existing config
auth: {
domain: process.env.AUTH0_DOMAIN,
audience: process.env.AUTH0_AUDIENCE,
clientId: process.env.AUTH0_CLIENT_ID,
clientSecret: process.env.AUTH0_CLIENT_SECRET,
},
});
Apply Guards to Controllers¶
// backend/src/admin/admin.controller.ts
import { Controller, Get, UseGuards } from '@nestjs/common';
import { JwtAuthGuard } from '../auth/jwt-auth.guard';
import { RolesGuard } from '../auth/roles.guard';
import { Roles } from '../auth/roles.decorator';
@Controller('admin')
@UseGuards(JwtAuthGuard, RolesGuard)
export class AdminController {
@Get()
@Roles(['admin'])
findAll() {
return { message: 'This is protected admin data' };
}
}
3. Frontend Integration (React)¶
Install Required Packages¶
cd frontend
npm install @auth0/auth0-react
Configure Auth0 Provider¶
// frontend/src/index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import { Auth0Provider } from '@auth0/auth0-react';
import App from './App';
ReactDOM.render(
<Auth0Provider
domain={process.env.REACT_APP_AUTH0_DOMAIN || ''}
clientId={process.env.REACT_APP_AUTH0_CLIENT_ID || ''}
authorizationParams={{
redirect_uri: window.location.origin,
audience: process.env.REACT_APP_AUTH0_AUDIENCE,
}}
>
<App />
</Auth0Provider>,
document.getElementById('root')
);
Create Authentication Hook¶
// frontend/src/hooks/useAuth.ts
import { useAuth0 } from '@auth0/auth0-react';
import { useEffect, useState } from 'react';
export const useAuth = () => {
const {
isAuthenticated,
loginWithRedirect,
logout,
getAccessTokenSilently,
user,
isLoading,
} = useAuth0();
const [token, setToken] = useState<string | null>(null);
useEffect(() => {
const getToken = async () => {
if (isAuthenticated) {
try {
const accessToken = await getAccessTokenSilently();
setToken(accessToken);
} catch (error) {
console.error('Error getting token', error);
}
}
};
getToken();
}, [isAuthenticated, getAccessTokenSilently]);
return {
isAuthenticated,
isLoading,
user,
token,
login: loginWithRedirect,
logout: () => logout({ logoutParams: { returnTo: window.location.origin } }),
};
};
Create Protected Route Component¶
// frontend/src/components/ProtectedRoute.tsx
import React from 'react';
import { Navigate } from 'react-router-dom';
import { useAuth } from '../hooks/useAuth';
interface ProtectedRouteProps {
children: React.ReactNode;
requiredRole?: string;
}
export const ProtectedRoute: React.FC<ProtectedRouteProps> = ({
children,
requiredRole
}) => {
const { isAuthenticated, isLoading, user } = useAuth();
if (isLoading) {
return <div>Loading...</div>;
}
if (!isAuthenticated) {
return <Navigate to="/login" replace />;
}
if (requiredRole) {
const userRoles = user?.['https://rec-platform.com/roles'] || [];
if (!userRoles.includes(requiredRole)) {
return <Navigate to="/unauthorized" replace />;
}
}
return <>{children}</>;
};
Create API Service with Auth Token¶
// frontend/src/services/api.ts
import { useAuth } from '../hooks/useAuth';
export const useApi = () => {
const { token } = useAuth();
const apiUrl = process.env.REACT_APP_API_URL || '';
const fetchWithAuth = async (
endpoint: string,
options: RequestInit = {}
) => {
if (!token) {
throw new Error('No token available');
}
const headers = {
...options.headers,
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
};
const response = await fetch(`${apiUrl}${endpoint}`, {
...options,
headers,
});
if (!response.ok) {
throw new Error(`API error: ${response.statusText}`);
}
return response.json();
};
return {
get: (endpoint: string) => fetchWithAuth(endpoint),
post: (endpoint: string, data: any) =>
fetchWithAuth(endpoint, {
method: 'POST',
body: JSON.stringify(data),
}),
put: (endpoint: string, data: any) =>
fetchWithAuth(endpoint, {
method: 'PUT',
body: JSON.stringify(data),
}),
delete: (endpoint: string) =>
fetchWithAuth(endpoint, {
method: 'DELETE',
}),
};
};
4. Environment Configuration¶
Backend Environment Variables (.env)¶
AUTH0_DOMAIN=your-tenant.auth0.com
AUTH0_AUDIENCE=https://api.rec-platform.com
AUTH0_CLIENT_ID=your-backend-client-id
AUTH0_CLIENT_SECRET=your-backend-client-secret
Frontend Environment Variables (.env)¶
REACT_APP_AUTH0_DOMAIN=your-tenant.auth0.com
REACT_APP_AUTH0_CLIENT_ID=your-frontend-client-id
REACT_APP_AUTH0_AUDIENCE=https://api.rec-platform.com
REACT_APP_API_URL=https://api.rec-platform.com
Auth0 Rules and Actions¶
1. Add Role Information to Tokens¶
// Auth0 Rule: Add roles to tokens
function addRolesToTokens(user, context, callback) {
const namespace = 'https://rec-platform.com';
const assignedRoles = (context.authorization || {}).roles || [];
const idTokenClaims = context.idToken || {};
const accessTokenClaims = context.accessToken || {};
idTokenClaims[`${namespace}/roles`] = assignedRoles;
accessTokenClaims[`${namespace}/roles`] = assignedRoles;
context.idToken = idTokenClaims;
context.accessToken = accessTokenClaims;
callback(null, user, context);
}
2. Enforce MFA for Admin Users¶
// Auth0 Rule: Enforce MFA for admin users
function enforceMultifactorForAdmins(user, context, callback) {
const adminRoles = ['admin'];
const userRoles = (context.authorization || {}).roles || [];
const isAdmin = userRoles.some(role => adminRoles.includes(role));
if (isAdmin && !context.multifactor.provider) {
context.multifactor = {
provider: 'any',
allowRememberBrowser: false
};
}
callback(null, user, context);
}
Passkey Implementation¶
Auth0 supports WebAuthn/FIDO2 which enables passkey authentication. Here's how to implement it:
1. Enable WebAuthn in Auth0 Dashboard¶
- Navigate to Authentication > Multi-factor Auth
- Enable WebAuthn (Platform/Cross-Platform)
- Configure settings as needed
2. Frontend Implementation¶
// frontend/src/components/PasskeyButton.tsx
import React from 'react';
import { useAuth0 } from '@auth0/auth0-react';
export const PasskeyButton: React.FC = () => {
const { loginWithRedirect } = useAuth0();
const loginWithPasskey = () => {
loginWithRedirect({
authorizationParams: {
prompt: 'login',
acr_values: 'http://schemas.openid.net/pape/policies/2007/06/multi-factor http://schemas.openid.net/pape/methods/2007/01/webauthn'
}
});
};
return (
<button onClick={loginWithPasskey}>
Sign in with Passkey
</button>
);
};
Migration Strategy¶
If the platform already has existing users, a migration strategy will be needed:
- Custom Database Connection: Set up a custom database connection in Auth0 that points to the existing user database
- Migration Script: Implement a migration script that transfers users to Auth0 when they log in
- Bulk Import: For immediate migration, use Auth0's bulk user import feature
Security Considerations¶
- Token Storage: Store tokens securely in browser memory, not localStorage
- PKCE Flow: Use PKCE (Proof Key for Code Exchange) for added security
- Token Expiration: Set appropriate token expiration times
- Scope Management: Limit token scopes to only what's needed
- Regular Audits: Regularly review Auth0 logs for suspicious activity
Implementation Timeline¶
| Phase | Tasks | Timeline |
|---|---|---|
| Planning & Setup | Create Auth0 tenant, configure connections | Week 1 |
| Backend Integration | Implement JWT validation, guards, and roles | Week 2 |
| Frontend Integration | Implement Auth0 provider, hooks, and protected routes | Week 3 |
| Testing & Refinement | Test authentication flows, role-based access | Week 4 |
| Production Deployment | Deploy to production, monitor for issues | Week 5 |
Conclusion¶
Implementing Auth0 for the REC Verifiable Credentialing Platform provides a robust, standards-compliant authentication solution that meets all the specified requirements. The implementation plan outlined in this document provides a clear path forward for integrating Auth0 with the existing NestJS and React architecture.
Auth0's support for OAuth 2.0/OpenID Connect, multi-factor authentication, and WebAuthn (passkeys) makes it an ideal choice for the platform's authentication needs. The detailed implementation steps, code examples, and configuration guidelines provided in this document should enable a smooth integration process.