feat: add member portal and auth hardening
This commit is contained in:
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user