Files
the-order/packages/shared/src/auth.js
T
defiQUG 2633de4d33 feat(eresidency): Complete eResidency service implementation
- Implement credential revocation endpoint with proper database integration
- Fix database row mapping (snake_case to camelCase) for eResidency applications
- Add missing imports (getRiskAssessmentEngine, VeriffKYCProvider, ComplyAdvantageSanctionsProvider)
- Fix environment variable type checking for Veriff and ComplyAdvantage providers
- Add required 'message' field to notification service calls
- Fix risk assessment type mismatches
- Update audit logging to use 'verified' action type (supported by schema)
- Resolve all TypeScript errors and unused variable warnings
- Add TypeScript ignore comments for placeholder implementations
- Temporarily disable security/detect-non-literal-regexp rule due to ESLint 9 compatibility
- Service now builds successfully with no linter errors

All core functionality implemented:
- Application submission and management
- KYC integration (Veriff placeholder)
- Sanctions screening (ComplyAdvantage placeholder)
- Risk assessment engine
- Credential issuance and revocation
- Reviewer console
- Status endpoints
- Auto-issuance service
2025-11-10 19:43:02 -08:00

137 lines
4.6 KiB
JavaScript

/**
* Authentication and authorization middleware
*/
import { verify } from 'jsonwebtoken';
import { DIDResolver } from '@the-order/auth';
import { getEnv } from './env';
import { AppError } from './error-handler';
import fetch from 'node-fetch';
/**
* JWT authentication middleware
*/
export async function authenticateJWT(request, _reply) {
const authHeader = request.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
throw new AppError(401, 'UNAUTHORIZED', 'Missing or invalid authorization header');
}
const token = authHeader.substring(7);
const env = getEnv();
if (!env.JWT_SECRET) {
throw new AppError(500, 'CONFIG_ERROR', 'JWT secret not configured');
}
try {
const decoded = verify(token, env.JWT_SECRET);
request.user = decoded;
}
catch (error) {
throw new AppError(401, 'INVALID_TOKEN', 'Invalid or expired token');
}
}
/**
* DID-based authentication middleware
*/
export async function authenticateDID(request, _reply) {
const didHeader = request.headers['x-did'];
const signatureHeader = request.headers['x-did-signature'];
const messageHeader = request.headers['x-did-message'];
if (!didHeader || !signatureHeader || !messageHeader) {
throw new AppError(401, 'UNAUTHORIZED', 'Missing DID authentication headers');
}
try {
const resolver = new DIDResolver();
const isValid = await resolver.verifySignature(didHeader, messageHeader, signatureHeader);
if (!isValid) {
throw new AppError(401, 'INVALID_SIGNATURE', 'Invalid DID signature');
}
request.user = {
id: didHeader,
did: didHeader,
};
}
catch (error) {
if (error instanceof AppError) {
throw error;
}
throw new AppError(401, 'AUTH_ERROR', 'DID authentication failed');
}
}
/**
* Role-based access control middleware
*/
export function requireRole(...allowedRoles) {
return async (request, _reply) => {
if (!request.user) {
throw new AppError(401, 'UNAUTHORIZED', 'Authentication required');
}
const userRoles = request.user.roles || [];
const hasRole = allowedRoles.some((role) => userRoles.includes(role));
if (!hasRole) {
throw new AppError(403, 'FORBIDDEN', `Required role: ${allowedRoles.join(' or ')}`);
}
};
}
/**
* OIDC token validation middleware
*/
export async function authenticateOIDC(request, _reply) {
const authHeader = request.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
throw new AppError(401, 'UNAUTHORIZED', 'Missing authorization header');
}
const token = authHeader.substring(7);
const env = getEnv();
// Validate token with OIDC issuer
if (!env.OIDC_ISSUER) {
throw new AppError(500, 'CONFIG_ERROR', 'OIDC issuer not configured');
}
try {
// Introspect token with issuer
const introspectionUrl = `${env.OIDC_ISSUER}/introspect`;
const response = await fetch(introspectionUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
Authorization: `Basic ${Buffer.from(`${env.OIDC_CLIENT_ID}:${env.OIDC_CLIENT_SECRET}`).toString('base64')}`,
},
body: new URLSearchParams({
token,
token_type_hint: 'access_token',
}),
});
if (!response.ok) {
throw new AppError(401, 'INVALID_TOKEN', 'Token introspection failed');
}
const tokenInfo = (await response.json());
if (!tokenInfo.active) {
throw new AppError(401, 'INVALID_TOKEN', 'Token is not active');
}
// Get user info from userinfo endpoint
const userInfoUrl = `${env.OIDC_ISSUER}/userinfo`;
const userInfoResponse = await fetch(userInfoUrl, {
headers: {
Authorization: `Bearer ${token}`,
},
});
if (userInfoResponse.ok) {
const userInfo = (await userInfoResponse.json());
request.user = {
id: userInfo.sub,
email: userInfo.email,
};
}
else {
// Fallback to token info
request.user = {
id: tokenInfo.sub || 'oidc-user',
email: tokenInfo.email,
};
}
}
catch (error) {
if (error instanceof AppError) {
throw error;
}
throw new AppError(401, 'AUTH_ERROR', 'OIDC token validation failed');
}
}
//# sourceMappingURL=auth.js.map