feat: add member portal and auth hardening

This commit is contained in:
defiQUG
2026-04-18 12:05:17 -07:00
parent c80b2a543a
commit 468bc05b78
59 changed files with 4066 additions and 604 deletions

View File

@@ -10,10 +10,21 @@ import { getEnv } from '@/shared/config/env-validator';
export interface AuthenticatedRequest extends Request {
sovereignBankId?: string;
employeeId?: string;
email?: string;
name?: string;
roleName?: string;
permissions?: string[];
sessionType?: 'portal' | 'service';
portalSurface?: 'admin' | 'member' | 'core';
identityType?: string;
apiRole?: string;
}
function isPortalSession(payload: JwtPayload): boolean {
return payload.sessionType === 'portal' || payload.identityType === 'WEB_PORTAL';
}
/**
* Extract Sovereign Identity Token (SIT) from Authorization header
*/
@@ -144,23 +155,43 @@ export async function zeroTrustAuthMiddleware(
throw new DbisError(ErrorCode.UNAUTHORIZED, 'Invalid or expired token');
}
// Extract sovereign bank ID and identity type
// Extract claims shared by service and portal sessions
req.sovereignBankId = decoded.sovereignBankId;
req.employeeId = typeof decoded.employeeId === 'string' ? decoded.employeeId : undefined;
req.email = typeof decoded.email === 'string' ? decoded.email : undefined;
req.name = typeof decoded.name === 'string' ? decoded.name : undefined;
req.roleName = typeof decoded.roleName === 'string' ? decoded.roleName : undefined;
req.permissions = Array.isArray(decoded.permissions)
? decoded.permissions.filter((permission): permission is string => typeof permission === 'string')
: undefined;
req.sessionType = decoded.sessionType === 'portal' ? 'portal' : 'service';
req.portalSurface =
decoded.portalSurface === 'admin' ||
decoded.portalSurface === 'member' ||
decoded.portalSurface === 'core'
? decoded.portalSurface
: undefined;
req.identityType = decoded.identityType;
req.apiRole = decoded.apiRole;
if (!req.sovereignBankId) {
throw new DbisError(ErrorCode.UNAUTHORIZED, 'Invalid token payload');
}
if (isPortalSession(decoded)) {
if (!req.employeeId && !req.email) {
throw new DbisError(ErrorCode.UNAUTHORIZED, 'Invalid portal token payload');
}
} else {
if (!req.sovereignBankId) {
throw new DbisError(ErrorCode.UNAUTHORIZED, 'Invalid token payload');
}
// Verify request signature
const signatureValid = await verifyRequestSignature(
req,
req.sovereignBankId,
req.identityType || ''
);
if (!signatureValid) {
throw new DbisError(ErrorCode.UNAUTHORIZED, 'Invalid request signature');
// Verify request signature
const signatureValid = await verifyRequestSignature(
req,
req.sovereignBankId,
req.identityType || ''
);
if (!signatureValid) {
throw new DbisError(ErrorCode.UNAUTHORIZED, 'Invalid request signature');
}
}
// Check token expiration
@@ -207,6 +238,20 @@ export function optionalAuthMiddleware(
if (jwtSecret) {
const decoded = jwt.verify(token, jwtSecret) as JwtPayload;
req.sovereignBankId = decoded.sovereignBankId;
req.employeeId = typeof decoded.employeeId === 'string' ? decoded.employeeId : undefined;
req.email = typeof decoded.email === 'string' ? decoded.email : undefined;
req.name = typeof decoded.name === 'string' ? decoded.name : undefined;
req.roleName = typeof decoded.roleName === 'string' ? decoded.roleName : undefined;
req.permissions = Array.isArray(decoded.permissions)
? decoded.permissions.filter((permission): permission is string => typeof permission === 'string')
: undefined;
req.sessionType = decoded.sessionType === 'portal' ? 'portal' : 'service';
req.portalSurface =
decoded.portalSurface === 'admin' ||
decoded.portalSurface === 'member' ||
decoded.portalSurface === 'core'
? decoded.portalSurface
: undefined;
req.identityType = decoded.identityType;
req.apiRole = decoded.apiRole;
}
@@ -219,4 +264,3 @@ export function optionalAuthMiddleware(
}
next();
}