import { IUser } from '../interfaces/entities.interface'; import { StorageAdapter } from '../interfaces/storage-adapter.interface'; import { InternalAuthAuditService as AuthAuditService } from './auth-audit.service'; import { RiskFactor } from '../enums/risk-factor.enum'; import { RiskDetectionService } from './risk-detection.service'; import { RiskScoringService } from './risk-scoring.service'; import { ClientInfoService } from './client-info.service'; import { NAuthConfig, AdaptiveMFARiskEventPayload } from '../interfaces/config.interface'; import { NAuthLogger } from '../utils/nauth-logger'; import { HookRegistryService } from './hook-registry.service'; /** * Adaptive MFA decision result */ export interface AdaptiveMFADecision { /** * Action to take */ action: 'allow' | 'require_mfa' | 'block_signin'; /** * Risk score (0-100) */ riskScore: number; /** * Risk level classification */ riskLevel: 'low' | 'medium' | 'high'; /** * Detected risk factors * Array of RiskFactor enum values (stored as strings at runtime) */ riskFactors: RiskFactor[]; /** * Whether user should be notified */ notifyUser: boolean; /** * Whether lifecycle hook overrode the decision */ hookOverride: boolean; /** * Risk event payload (included when action requires it or notifyUser is true) * Contains full client context for use in blockUserSignIn or audit logging */ payload?: AdaptiveMFARiskEventPayload; } /** * Adaptive MFA Decision Service * * Makes context-aware MFA requirement decisions based on risk analysis. * Supports multiple actions (allow, require_mfa, block_signin) based on risk level. * * **Decision Flow:** * 1. Detect risk factors (via RiskDetectionService) * 2. Calculate risk score (via RiskScoringService) * 3. Determine risk level and action from configuration * 4. Call lifecycle hooks if notifyUser is true * 5. Record audit event (non-blocking) * 6. Return decision object * * **Default Risk Levels:** * - Low (0-20): action 'allow', notifyUser false * - Medium (21-50): action 'require_mfa', notifyUser true * - High (51-100): action 'require_mfa', notifyUser true (conservative default) * * **User Blocking:** * When action is 'block_signin', user is blocked in storage adapter with optional TTL. * Block status is checked before evaluation to prevent blocked users from attempting sign-in. * * @example * ```typescript * const decision = await adaptiveMFADecisionService.evaluateAdaptiveMFA(user, 'password'); * if (decision.action === 'block_signin') { * throw new NAuthException(AuthErrorCode.SIGNIN_BLOCKED_HIGH_RISK, 'Sign-in blocked'); * } * return decision.action === 'require_mfa'; * ``` */ export declare class AdaptiveMFADecisionService { private readonly riskDetectionService; private readonly riskScoringService; private readonly storageAdapter; private readonly clientInfoService; private readonly config; private readonly logger; private readonly auditService?; private readonly hookRegistry?; /** * Default risk level configuration * * Conservative defaults that prioritize security: * - Low risk: Allow without MFA (normal flow) * - Medium risk: Require MFA * - High risk: Require MFA (conservative - don't block by default) */ private readonly defaultRiskLevels; constructor(riskDetectionService: RiskDetectionService, riskScoringService: RiskScoringService, storageAdapter: StorageAdapter, clientInfoService: ClientInfoService, config: NAuthConfig, logger: NAuthLogger, auditService?: AuthAuditService | undefined, // Optional - audit trail service (enabled via config.auditLogs.enabled) hookRegistry?: HookRegistryService | undefined); /** * Resolve the configured block scope for adaptive MFA sign-in blocking. * * @returns Block scope (defaults to `user`) * @private */ private getBlockedSignInScope; /** * Build the storage key for a sign-in block. * * Keys are scoped to reduce the blast radius of blocking: * - user: `adaptive_mfa_block:{userId}` * - device: `adaptive_mfa_block:{userId}:device:{deviceToken}` * - ip: `adaptive_mfa_block:{userId}:ip:{ipAddress}` * * @param userId - Internal user ID * @param clientInfo - Current client context (used for device/ip scoped keys) * @returns Storage key * @private */ private buildBlockKey; /** * Evaluate adaptive MFA requirement with risk-based actions * * Main entry point for adaptive MFA evaluation. Analyzes current login context, * calculates risk score, determines action, and calls lifecycle hooks. * * @param user - User being authenticated * @param authMethod - Authentication method ('password', 'google', 'apple', etc.) * @returns Decision object with action, risk details, and hook override status * * @example * ```typescript * const decision = await adaptiveMFADecisionService.evaluateAdaptiveMFA(user, 'password'); * if (decision.action === 'block_signin') { * // Handle blocking * } * ``` */ evaluateAdaptiveMFA(user: IUser, authMethod: string): Promise; /** * Determine risk level and action based on score and configured thresholds * * Evaluates risk score against configured thresholds in order: low → medium → high. * Returns the first level that the score falls within. * * @param riskScore - Calculated risk score (0-100) * @param riskLevels - Configured risk level thresholds * @returns Risk level, action, and notifyUser flag * @private */ private determineRiskLevelAndAction; /** * Check if user is currently blocked due to high-risk sign-in * * Uses storage adapter to check for existing block. Block is stored with * key format: `adaptive_mfa_block:{userId}`. * * @param userId - Internal user ID (integer) * @returns Block status with expiration and message if blocked * * @example * ```typescript * const blockStatus = await adaptiveMFADecisionService.isUserBlocked(user.id); * if (blockStatus.blocked) { * throw new NAuthException(AuthErrorCode.SIGNIN_BLOCKED_HIGH_RISK, blockStatus.message); * } * ``` */ isUserBlocked(userId: number): Promise<{ blocked: boolean; expiresAt?: Date; message?: string; }>; /** * Block user sign-in due to high risk * * Stores block in storage adapter with optional TTL. Block data includes: * - userId, userSub (for reference) * - message (shown to user) * - riskScore, riskFactors (for audit) * - blockedAt, expiresAt (timestamps) * * Also calls onSignInBlocked lifecycle hook if configured. * * @param user - User to block * @param payload - Risk event payload with all context * * @example * ```typescript * await adaptiveMFADecisionService.blockUserSignIn(user, payload); * ``` */ blockUserSignIn(user: IUser, payload: AdaptiveMFARiskEventPayload): Promise; /** * Clear user block (manual unblock) * * Removes the block from storage adapter, allowing user to sign in again. * Useful for admin actions or when risk situation has improved. * * @param userId - Internal user ID (integer) * * @example * ```typescript * await adaptiveMFADecisionService.clearUserBlock(user.id); * ``` */ clearUserBlock(userId: number): Promise; } //# sourceMappingURL=adaptive-mfa-decision.service.d.ts.map