Authentication Architecture
The ID PASS Data Collect authentication system provides a flexible, secure, and extensible authentication framework that supports multiple authentication providers and secure token storage.
Overview
The authentication architecture consists of three main components:
- AuthManager: Central authentication coordinator
- Auth Adapters: Provider-specific implementations (Auth0, Keycloak, OTP, National ID)
- Storage Adapters: Token persistence layer
Core Components
AuthManager
The AuthManager class serves as the central coordinator for authentication operations:
- Manages multiple authentication providers
- Coordinates token storage and retrieval
- Handles authentication state
- Provides unified authentication interface
Auth Adapters
Provider-specific adapters implement the AuthAdapter interface:
- Auth0Adapter (
auth0): Auth0-specific OIDC implementation for field workers - KeycloakAdapter (
keycloak): Keycloak-specific OIDC implementation for field workers - OtpAuthAdapter (
otp): One-time password via SMS/email for citizen self-service - IdAuthAdapter (
id): National ID card verification for citizen self-service - New adapters can be added by implementing the
AuthAdapterinterface and registering the registry key inAuthManager
Storage Adapters
Storage adapters implement the AuthStorageAdapter interface:
- IndexedDbAuthStorageAdapter: Browser-based storage
- Extensible for different storage backends
- Multi-tenant support
Authentication Flow
Token Management
Storage
- Tokens are stored securely using the configured storage adapter
- Multi-tenant support through tenant-specific storage instances
- Automatic token cleanup on logout
Validation
- Provider-specific token validation
- Environment-aware validation (frontend/backend)
- Regular token validation checks
Security Features
-
Token Security
- Secure token storage
- Token validation on each use
- Automatic token cleanup
-
Provider Integration
- OAuth 2.0 and OpenID Connect support
- Provider-specific security features
- Organization/Realm-based access control
-
Multi-tenant Security
- Tenant isolation
- Separate storage per tenant
- Tenant-specific configurations
Configuration
Auth0 Configuration
const auth0Config = {
type: "auth0",
fields: {
authority: "https://example.auth0.com",
client_id: "CLIENT_ID",
organization: "ORG_ID"
}
};
Keycloak Configuration
const keycloakConfig = {
type: "keycloak",
fields: {
authority: "https://keycloak.example.com",
client_id: "CLIENT_ID",
realm: "REALM_NAME"
}
};
OTP Configuration
The otp adapter sends a 6-digit one-time password to a beneficiary's phone number or email address. It is designed for citizen self-service scenarios where beneficiaries do not hold traditional login credentials. The resulting JWT carries a self-service scope and is scoped to the matched beneficiary entity.
const otpConfig = {
type: "otp",
fields: {
serverUrl: "https://sync.example.com"
}
};
Authentication is a two-step process — call requestOtp() first, then verifyOtp():
// 1. Request OTP delivery
const { success, expiresIn } = await otpAdapter.requestOtp("+15551234567");
// 2. Verify the received code and retrieve a JWT
const { username, token } = await otpAdapter.verifyOtp("+15551234567", "123456");
The standard login() method is not supported for this adapter; use requestOtp() and verifyOtp() directly.
Backend endpoints used:
POST /api/auth/otp/request— sends the one-time codePOST /api/auth/otp/verify— exchanges the code for a JWTPOST /api/users/check-token— validates a stored token
National ID Configuration
The id adapter authenticates beneficiaries using a national ID document combined with their date of birth, or via a scanned QR code. It is designed for self-service kiosks or assisted-service workflows where identity documents replace usernames and passwords. The resulting JWT carries a self-service scope and is scoped to the matched beneficiary entity.
const idConfig = {
type: "id",
fields: {
serverUrl: "https://sync.example.com"
}
};
Two authentication flows are available:
// ID document + date of birth
const { username, token } = await idAdapter.authenticateWithId(
"A1234567", // national ID number
"1990-05-15", // date of birth (ISO 8601)
"tenant-id"
);
// QR code scan
const { username, token } = await idAdapter.authenticateWithQr(qrCodePayload);
The standard login() method is not supported for this adapter; use authenticateWithId() or authenticateWithQr() directly.
Backend endpoints used:
POST /api/auth/id/verify— verifies national ID + date of birthPOST /api/auth/qr/verify— verifies QR code payloadPOST /api/users/check-token— validates a stored token
Extension Points
The authentication system can be extended in several ways:
-
New Auth Providers
- Implement
AuthAdapterinterface - Add provider-specific configuration
- Register with
AuthManagerusing a new registry key alongside the existingauth0,keycloak,otp, andidentries
- Implement
-
Storage Backends
- Implement
AuthStorageAdapterinterface - Add storage-specific configuration
- Use with existing auth adapters
- Implement
-
Custom Validation
- Override validation methods
- Add custom validation rules
- Implement provider-specific checks
Best Practices
-
Security
- Use HTTPS in production
- Implement token rotation
- Regular token validation
- Secure storage configuration
-
Configuration
- Environment-specific settings
- Proper error handling
- Logging and monitoring
-
Integration
- Single authentication instance
- Proper initialization order
- Clean logout handling
Integration with EntityManager
The EntityDataManager integrates with AuthManager to provide authenticated data operations and synchronization capabilities:
Initialization
const authManager = new AuthManager(
[{
type: "mock-auth-adapter",
fields: { url: "http://localhost:3000" }
}],
"http://localhost:3000",
authStorage
);
const entityManager = new EntityDataManager(
eventStore,
entityStore,
eventApplierService,
externalSyncManager,
internalSyncManager,
authManager
);
Authentication Flow
// Login
await entityManager.login({
username: "admin@example.com",
password: "password123"
});
// Check for unsynced events
const hasUnsynced = await entityManager.hasUnsyncedEvents();
if (hasUnsynced) {
// Sync requires authentication
await entityManager.syncWithSyncServer();
}
// Logout
await entityManager.logout();