@revealui/auth 0.3.5 → 0.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Security Event Bridge — Connects auth events to the audit trail.
3
+ *
4
+ * Each function wraps an auth operation with structured audit logging
5
+ * via the AuditSystem from @revealui/security. Uses lazy import to
6
+ * avoid circular dependency issues at module load time.
7
+ */
8
+ /**
9
+ * Record a successful login to the audit trail.
10
+ *
11
+ * @param userId - The authenticated user's ID
12
+ * @param ip - Client IP address
13
+ * @param userAgent - Client User-Agent header
14
+ */
15
+ export declare function auditLoginSuccess(userId: string, ip: string, userAgent: string): Promise<void>;
16
+ /**
17
+ * Record a failed login attempt to the audit trail.
18
+ *
19
+ * @param email - The email used in the failed attempt
20
+ * @param ip - Client IP address
21
+ * @param userAgent - Client User-Agent header
22
+ * @param reason - Why the login failed (e.g. 'invalid_password', 'account_locked')
23
+ */
24
+ export declare function auditLoginFailure(email: string, ip: string, userAgent: string, reason: string): Promise<void>;
25
+ /**
26
+ * Record a password change to the audit trail.
27
+ *
28
+ * @param userId - The user who changed their password
29
+ * @param ip - Client IP address
30
+ */
31
+ export declare function auditPasswordChange(userId: string, ip: string): Promise<void>;
32
+ /**
33
+ * Record a password reset request to the audit trail.
34
+ *
35
+ * @param email - The email for which a reset was requested
36
+ * @param ip - Client IP address
37
+ */
38
+ export declare function auditPasswordReset(email: string, ip: string): Promise<void>;
39
+ /**
40
+ * Record MFA being enabled on an account to the audit trail.
41
+ *
42
+ * @param userId - The user who enabled MFA
43
+ * @param ip - Client IP address
44
+ */
45
+ export declare function auditMfaEnabled(userId: string, ip: string): Promise<void>;
46
+ /**
47
+ * Record MFA being disabled on an account to the audit trail.
48
+ *
49
+ * @param userId - The user who disabled MFA
50
+ * @param ip - Client IP address
51
+ */
52
+ export declare function auditMfaDisabled(userId: string, ip: string): Promise<void>;
53
+ /**
54
+ * Record a session revocation to the audit trail.
55
+ *
56
+ * @param userId - The user whose session was revoked
57
+ * @param sessionId - The revoked session's ID
58
+ * @param ip - Client IP address
59
+ */
60
+ export declare function auditSessionRevoked(userId: string, sessionId: string, ip: string): Promise<void>;
61
+ /**
62
+ * Record an account lockout to the audit trail.
63
+ *
64
+ * @param email - The locked account's email
65
+ * @param ip - Client IP address
66
+ * @param failedAttempts - Number of failed attempts that triggered the lockout
67
+ */
68
+ export declare function auditAccountLocked(email: string, ip: string, failedAttempts: number): Promise<void>;
69
+ //# sourceMappingURL=audit-bridge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit-bridge.d.ts","sourceRoot":"","sources":["../../src/server/audit-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAgCH;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,MAAM,EACV,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CASf;AAED;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,MAAM,EACb,EAAE,EAAE,MAAM,EACV,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAUf;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CASnF;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAUjF;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAS/E;AAED;;;;;GAKG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAShF;AAED;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,EAAE,EAAE,MAAM,GACT,OAAO,CAAC,IAAI,CAAC,CAUf;AAED;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,MAAM,EACb,EAAE,EAAE,MAAM,EACV,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,IAAI,CAAC,CAUf"}
@@ -0,0 +1,169 @@
1
+ /**
2
+ * Security Event Bridge — Connects auth events to the audit trail.
3
+ *
4
+ * Each function wraps an auth operation with structured audit logging
5
+ * via the AuditSystem from @revealui/security. Uses lazy import to
6
+ * avoid circular dependency issues at module load time.
7
+ */
8
+ /**
9
+ * Lazily resolve the global audit system from @revealui/security.
10
+ * Returns null if the module cannot be loaded (e.g. in test environments
11
+ * where @revealui/security is not available).
12
+ */
13
+ async function getAudit() {
14
+ try {
15
+ const { audit } = await import('@revealui/security');
16
+ return audit;
17
+ }
18
+ catch {
19
+ return null;
20
+ }
21
+ }
22
+ /**
23
+ * Internal helper — logs an audit event, silently skipping if the
24
+ * audit system is unavailable.
25
+ */
26
+ async function logAuditEvent(event) {
27
+ const auditSystem = await getAudit();
28
+ if (!auditSystem) {
29
+ return;
30
+ }
31
+ await auditSystem.log(event);
32
+ }
33
+ /**
34
+ * Record a successful login to the audit trail.
35
+ *
36
+ * @param userId - The authenticated user's ID
37
+ * @param ip - Client IP address
38
+ * @param userAgent - Client User-Agent header
39
+ */
40
+ export async function auditLoginSuccess(userId, ip, userAgent) {
41
+ await logAuditEvent({
42
+ type: 'auth.login',
43
+ severity: 'low',
44
+ actor: { id: userId, type: 'user', ip, userAgent },
45
+ action: 'login',
46
+ result: 'success',
47
+ message: 'User logged in successfully',
48
+ });
49
+ }
50
+ /**
51
+ * Record a failed login attempt to the audit trail.
52
+ *
53
+ * @param email - The email used in the failed attempt
54
+ * @param ip - Client IP address
55
+ * @param userAgent - Client User-Agent header
56
+ * @param reason - Why the login failed (e.g. 'invalid_password', 'account_locked')
57
+ */
58
+ export async function auditLoginFailure(email, ip, userAgent, reason) {
59
+ await logAuditEvent({
60
+ type: 'auth.failed_login',
61
+ severity: 'medium',
62
+ actor: { id: email, type: 'user', ip, userAgent },
63
+ action: 'login',
64
+ result: 'failure',
65
+ message: `Login failed: ${reason}`,
66
+ metadata: { email, reason },
67
+ });
68
+ }
69
+ /**
70
+ * Record a password change to the audit trail.
71
+ *
72
+ * @param userId - The user who changed their password
73
+ * @param ip - Client IP address
74
+ */
75
+ export async function auditPasswordChange(userId, ip) {
76
+ await logAuditEvent({
77
+ type: 'auth.password_change',
78
+ severity: 'medium',
79
+ actor: { id: userId, type: 'user', ip },
80
+ action: 'password_change',
81
+ result: 'success',
82
+ message: 'Password changed',
83
+ });
84
+ }
85
+ /**
86
+ * Record a password reset request to the audit trail.
87
+ *
88
+ * @param email - The email for which a reset was requested
89
+ * @param ip - Client IP address
90
+ */
91
+ export async function auditPasswordReset(email, ip) {
92
+ await logAuditEvent({
93
+ type: 'auth.password_reset',
94
+ severity: 'medium',
95
+ actor: { id: email, type: 'user', ip },
96
+ action: 'password_reset',
97
+ result: 'success',
98
+ message: 'Password reset requested',
99
+ metadata: { email },
100
+ });
101
+ }
102
+ /**
103
+ * Record MFA being enabled on an account to the audit trail.
104
+ *
105
+ * @param userId - The user who enabled MFA
106
+ * @param ip - Client IP address
107
+ */
108
+ export async function auditMfaEnabled(userId, ip) {
109
+ await logAuditEvent({
110
+ type: 'auth.mfa_enabled',
111
+ severity: 'medium',
112
+ actor: { id: userId, type: 'user', ip },
113
+ action: 'mfa_enabled',
114
+ result: 'success',
115
+ message: 'MFA enabled',
116
+ });
117
+ }
118
+ /**
119
+ * Record MFA being disabled on an account to the audit trail.
120
+ *
121
+ * @param userId - The user who disabled MFA
122
+ * @param ip - Client IP address
123
+ */
124
+ export async function auditMfaDisabled(userId, ip) {
125
+ await logAuditEvent({
126
+ type: 'auth.mfa_disabled',
127
+ severity: 'high',
128
+ actor: { id: userId, type: 'user', ip },
129
+ action: 'mfa_disabled',
130
+ result: 'success',
131
+ message: 'MFA disabled',
132
+ });
133
+ }
134
+ /**
135
+ * Record a session revocation to the audit trail.
136
+ *
137
+ * @param userId - The user whose session was revoked
138
+ * @param sessionId - The revoked session's ID
139
+ * @param ip - Client IP address
140
+ */
141
+ export async function auditSessionRevoked(userId, sessionId, ip) {
142
+ await logAuditEvent({
143
+ type: 'security.alert',
144
+ severity: 'medium',
145
+ actor: { id: userId, type: 'user', ip },
146
+ action: 'session_revoked',
147
+ result: 'success',
148
+ message: 'Session revoked',
149
+ metadata: { sessionId },
150
+ });
151
+ }
152
+ /**
153
+ * Record an account lockout to the audit trail.
154
+ *
155
+ * @param email - The locked account's email
156
+ * @param ip - Client IP address
157
+ * @param failedAttempts - Number of failed attempts that triggered the lockout
158
+ */
159
+ export async function auditAccountLocked(email, ip, failedAttempts) {
160
+ await logAuditEvent({
161
+ type: 'security.alert',
162
+ severity: 'high',
163
+ actor: { id: email, type: 'user', ip },
164
+ action: 'account_locked',
165
+ result: 'failure',
166
+ message: `Account locked after ${String(failedAttempts)} failed attempts`,
167
+ metadata: { email, failedAttempts },
168
+ });
169
+ }
@@ -5,6 +5,7 @@
5
5
  * Inspired by Better Auth and Neon Auth patterns.
6
6
  */
7
7
  export type { SignInResult, SignUpResult } from '../types.js';
8
+ export { auditAccountLocked, auditLoginFailure, auditLoginSuccess, auditMfaDisabled, auditMfaEnabled, auditPasswordChange, auditPasswordReset, auditSessionRevoked, } from './audit-bridge.js';
8
9
  export { isSignupAllowed, signIn, signUp } from './auth.js';
9
10
  export { clearFailedAttempts, configureBruteForce, getFailedAttemptCount, isAccountLocked, recordFailedAttempt, resetBruteForceConfig, } from './brute-force.js';
10
11
  export { AuthError, AuthenticationError, DatabaseError, OAuthAccountConflictError, SessionError, TokenError, } from './errors.js';
@@ -12,6 +13,8 @@ export type { MagicLinkConfig } from './magic-link.js';
12
13
  export { configureMagicLink, createMagicLink, resetMagicLinkConfig, verifyMagicLink, } from './magic-link.js';
13
14
  export type { MFAConfig, MFADisableProof, MFASetupResult } from './mfa.js';
14
15
  export { configureMFA, disableMFA, initiateMFASetup, isMFAEnabled, regenerateBackupCodes, resetMFAConfig, verifyBackupCode, verifyMFACode, verifyMFASetup, } from './mfa.js';
16
+ export type { MfaCheckResult, MfaEnforcementOptions, MfaErrorResponse, MfaRequest, MfaSession, MfaSessionUser, } from './mfa-enforcement.js';
17
+ export { requireMfa } from './mfa-enforcement.js';
15
18
  export { buildAuthUrl, exchangeCode, fetchProviderUser, generateOAuthState, getLinkedProviders, linkOAuthAccount, type ProviderUser, type UpsertOAuthOptions, unlinkOAuthAccount, upsertOAuthUser, verifyOAuthState, } from './oauth.js';
16
19
  export type { PasskeyConfig } from './passkey.js';
17
20
  export { configurePasskey, countUserCredentials, deletePasskey, generateAuthenticationChallenge, generateRegistrationChallenge, listPasskeys, renamePasskey, resetPasskeyConfig, storePasskey, verifyAuthentication, verifyRegistration, } from './passkey.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAC5D,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACnB,qBAAqB,EACrB,eAAe,EACf,mBAAmB,EACnB,qBAAqB,GACtB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,SAAS,EACT,mBAAmB,EACnB,aAAa,EACb,yBAAyB,EACzB,YAAY,EACZ,UAAU,GACX,MAAM,aAAa,CAAC;AAErB,YAAY,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,oBAAoB,EACpB,eAAe,GAChB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,SAAS,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC3E,OAAO,EACL,YAAY,EACZ,UAAU,EACV,gBAAgB,EAChB,YAAY,EACZ,qBAAqB,EACrB,cAAc,EACd,gBAAgB,EAChB,aAAa,EACb,cAAc,GACf,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,EAChB,KAAK,YAAY,EACjB,KAAK,kBAAkB,EACvB,kBAAkB,EAClB,eAAe,EACf,gBAAgB,GACjB,MAAM,YAAY,CAAC;AAEpB,YAAY,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,aAAa,EACb,+BAA+B,EAC/B,6BAA6B,EAC7B,YAAY,EACZ,aAAa,EACb,kBAAkB,EAClB,YAAY,EACZ,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,cAAc,CAAC;AACtB,YAAY,EACV,oBAAoB,EACpB,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,cAAc,EACd,0BAA0B,EAC1B,4BAA4B,EAC5B,sBAAsB,EACtB,0BAA0B,GAC3B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,gCAAgC,EAChC,wBAAwB,GACzB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,kBAAkB,EAClB,cAAc,EACd,oBAAoB,GACrB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,cAAc,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AACtF,OAAO,EACL,uBAAuB,EACvB,aAAa,EACb,qBAAqB,EACrB,uBAAuB,EACvB,aAAa,EACb,UAAU,EACV,iBAAiB,EACjB,yBAAyB,EACzB,aAAa,EACb,sBAAsB,GACvB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC5E,YAAY,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EACL,aAAa,EACb,eAAe,EACf,UAAU,EACV,eAAe,EACf,YAAY,GACb,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"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"}
@@ -4,11 +4,14 @@
4
4
  * Server-side authentication functions for Next.js and TanStack Start.
5
5
  * Inspired by Better Auth and Neon Auth patterns.
6
6
  */
7
+ // Audit bridge
8
+ export { auditAccountLocked, auditLoginFailure, auditLoginSuccess, auditMfaDisabled, auditMfaEnabled, auditPasswordChange, auditPasswordReset, auditSessionRevoked, } from './audit-bridge.js';
7
9
  export { isSignupAllowed, signIn, signUp } from './auth.js';
8
10
  export { clearFailedAttempts, configureBruteForce, getFailedAttemptCount, isAccountLocked, recordFailedAttempt, resetBruteForceConfig, } from './brute-force.js';
9
11
  export { AuthError, AuthenticationError, DatabaseError, OAuthAccountConflictError, SessionError, TokenError, } from './errors.js';
10
12
  export { configureMagicLink, createMagicLink, resetMagicLinkConfig, verifyMagicLink, } from './magic-link.js';
11
13
  export { configureMFA, disableMFA, initiateMFASetup, isMFAEnabled, regenerateBackupCodes, resetMFAConfig, verifyBackupCode, verifyMFACode, verifyMFASetup, } from './mfa.js';
14
+ export { requireMfa } from './mfa-enforcement.js';
12
15
  export { buildAuthUrl, exchangeCode, fetchProviderUser, generateOAuthState, getLinkedProviders, linkOAuthAccount, unlinkOAuthAccount, upsertOAuthUser, verifyOAuthState, } from './oauth.js';
13
16
  export { configurePasskey, countUserCredentials, deletePasskey, generateAuthenticationChallenge, generateRegistrationChallenge, listPasskeys, renamePasskey, resetPasskeyConfig, storePasskey, verifyAuthentication, verifyRegistration, } from './passkey.js';
14
17
  export { changePassword, generatePasswordResetToken, invalidatePasswordResetToken, resetPasswordWithToken, validatePasswordResetToken, } from './password-reset.js';
@@ -0,0 +1,76 @@
1
+ /**
2
+ * MFA Enforcement Middleware
3
+ *
4
+ * Provides a middleware factory that enforces Multi-Factor Authentication
5
+ * for specific roles and sensitive operations. SOC2 6.2 compliant.
6
+ */
7
+ /** Options for the MFA enforcement middleware. */
8
+ export interface MfaEnforcementOptions {
9
+ /** Roles that require MFA to be enabled (default: ['admin']). */
10
+ roles?: string[];
11
+ /** Operations that require MFA (e.g. 'delete_user', 'change_role'). */
12
+ operations?: string[];
13
+ }
14
+ /** Shape of a session user as expected by the middleware. */
15
+ export interface MfaSessionUser {
16
+ /** User ID. */
17
+ id: string;
18
+ /** User's role. */
19
+ role: string;
20
+ /** Whether MFA is enabled on the account. */
21
+ mfaEnabled: boolean;
22
+ /** Whether MFA was verified in this session. */
23
+ mfaVerified?: boolean;
24
+ }
25
+ /** Shape of the session object the middleware reads from. */
26
+ export interface MfaSession {
27
+ user: MfaSessionUser;
28
+ }
29
+ /** Request shape expected by the middleware. */
30
+ export interface MfaRequest {
31
+ /** The current session, if any. */
32
+ session?: MfaSession | null;
33
+ /** The operation being performed (matched against `options.operations`). */
34
+ operation?: string;
35
+ }
36
+ /** Standard JSON error body returned when MFA is not satisfied. */
37
+ export interface MfaErrorResponse {
38
+ error: string;
39
+ code: 'MFA_REQUIRED' | 'MFA_VERIFY_REQUIRED';
40
+ }
41
+ /** Result of the MFA enforcement check. */
42
+ export interface MfaCheckResult {
43
+ /** Whether the request may proceed. */
44
+ allowed: boolean;
45
+ /** HTTP status code (403 when blocked, undefined when allowed). */
46
+ status?: number;
47
+ /** Error body (present only when blocked). */
48
+ body?: MfaErrorResponse;
49
+ }
50
+ /**
51
+ * Create an MFA enforcement checker.
52
+ *
53
+ * Returns a function that inspects a request's session and determines
54
+ * whether MFA requirements are satisfied. Consumers are responsible
55
+ * for translating the result into their framework's response format
56
+ * (Hono, Express, Next.js, etc.).
57
+ *
58
+ * @param options - Roles and operations that require MFA
59
+ * @returns A check function that evaluates MFA requirements
60
+ *
61
+ * @example
62
+ * ```ts
63
+ * const checkMfa = requireMfa({ roles: ['admin'], operations: ['delete_user'] });
64
+ *
65
+ * // In a Hono route:
66
+ * app.delete('/users/:id', async (c) => {
67
+ * const result = checkMfa({ session: c.get('session'), operation: 'delete_user' });
68
+ * if (!result.allowed) {
69
+ * return c.json(result.body, result.status);
70
+ * }
71
+ * // proceed...
72
+ * });
73
+ * ```
74
+ */
75
+ export declare function requireMfa(options?: MfaEnforcementOptions): (request: MfaRequest) => MfaCheckResult;
76
+ //# sourceMappingURL=mfa-enforcement.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mfa-enforcement.d.ts","sourceRoot":"","sources":["../../src/server/mfa-enforcement.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,kDAAkD;AAClD,MAAM,WAAW,qBAAqB;IACpC,iEAAiE;IACjE,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,uEAAuE;IACvE,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,6DAA6D;AAC7D,MAAM,WAAW,cAAc;IAC7B,eAAe;IACf,EAAE,EAAE,MAAM,CAAC;IACX,mBAAmB;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,6CAA6C;IAC7C,UAAU,EAAE,OAAO,CAAC;IACpB,gDAAgD;IAChD,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,6DAA6D;AAC7D,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,cAAc,CAAC;CACtB;AAED,gDAAgD;AAChD,MAAM,WAAW,UAAU;IACzB,mCAAmC;IACnC,OAAO,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IAC5B,4EAA4E;IAC5E,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,mEAAmE;AACnE,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,cAAc,GAAG,qBAAqB,CAAC;CAC9C;AAED,2CAA2C;AAC3C,MAAM,WAAW,cAAc;IAC7B,uCAAuC;IACvC,OAAO,EAAE,OAAO,CAAC;IACjB,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8CAA8C;IAC9C,IAAI,CAAC,EAAE,gBAAgB,CAAC;CACzB;AAQD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,UAAU,CACxB,OAAO,GAAE,qBAA0B,GAClC,CAAC,OAAO,EAAE,UAAU,KAAK,cAAc,CAgDzC"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * MFA Enforcement Middleware
3
+ *
4
+ * Provides a middleware factory that enforces Multi-Factor Authentication
5
+ * for specific roles and sensitive operations. SOC2 6.2 compliant.
6
+ */
7
+ // =============================================================================
8
+ // Middleware
9
+ // =============================================================================
10
+ const DEFAULT_ROLES = ['admin'];
11
+ /**
12
+ * Create an MFA enforcement checker.
13
+ *
14
+ * Returns a function that inspects a request's session and determines
15
+ * whether MFA requirements are satisfied. Consumers are responsible
16
+ * for translating the result into their framework's response format
17
+ * (Hono, Express, Next.js, etc.).
18
+ *
19
+ * @param options - Roles and operations that require MFA
20
+ * @returns A check function that evaluates MFA requirements
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * const checkMfa = requireMfa({ roles: ['admin'], operations: ['delete_user'] });
25
+ *
26
+ * // In a Hono route:
27
+ * app.delete('/users/:id', async (c) => {
28
+ * const result = checkMfa({ session: c.get('session'), operation: 'delete_user' });
29
+ * if (!result.allowed) {
30
+ * return c.json(result.body, result.status);
31
+ * }
32
+ * // proceed...
33
+ * });
34
+ * ```
35
+ */
36
+ export function requireMfa(options = {}) {
37
+ const requiredRoles = options.roles ?? DEFAULT_ROLES;
38
+ const requiredOperations = options.operations ?? [];
39
+ return (request) => {
40
+ const session = request.session;
41
+ if (!session) {
42
+ // No session — nothing to enforce (auth middleware handles this)
43
+ return { allowed: true };
44
+ }
45
+ const user = session.user;
46
+ // Determine if MFA is required for this request
47
+ const roleRequiresMfa = requiredRoles.includes(user.role);
48
+ const operationRequiresMfa = request.operation !== undefined && requiredOperations.includes(request.operation);
49
+ if (!(roleRequiresMfa || operationRequiresMfa)) {
50
+ return { allowed: true };
51
+ }
52
+ // MFA is required but not enabled on the account
53
+ if (!user.mfaEnabled) {
54
+ return {
55
+ allowed: false,
56
+ status: 403,
57
+ body: {
58
+ error: 'MFA required',
59
+ code: 'MFA_REQUIRED',
60
+ },
61
+ };
62
+ }
63
+ // MFA is enabled but not verified in this session
64
+ if (!user.mfaVerified) {
65
+ return {
66
+ allowed: false,
67
+ status: 403,
68
+ body: {
69
+ error: 'MFA verification required',
70
+ code: 'MFA_VERIFY_REQUIRED',
71
+ },
72
+ };
73
+ }
74
+ return { allowed: true };
75
+ };
76
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"vercel.d.ts","sourceRoot":"","sources":["../../../src/server/providers/vercel.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAMzF;AAED,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA2BrF;AAED,wBAAsB,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAiC1E"}
1
+ {"version":3,"file":"vercel.d.ts","sourceRoot":"","sources":["../../../src/server/providers/vercel.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAMzF;AAED,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAiCrF;AAED,wBAAsB,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAiC1E"}
@@ -34,6 +34,12 @@ export async function exchangeCode(code, redirectUri) {
34
34
  throw new Error(`Vercel token exchange failed: ${response.status}${detail ? ` — ${detail}` : ''}`);
35
35
  }
36
36
  const data = (await response.json());
37
+ if (data.error) {
38
+ throw new Error(`Vercel token exchange error: ${data.error}`);
39
+ }
40
+ if (!data.access_token || typeof data.access_token !== 'string') {
41
+ throw new Error('Vercel token exchange returned no access_token');
42
+ }
37
43
  return data.access_token;
38
44
  }
39
45
  export async function fetchUser(accessToken) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@revealui/auth",
3
- "version": "0.3.5",
3
+ "version": "0.3.6",
4
4
  "description": "Authentication system for RevealUI - database-backed sessions with Better Auth patterns",
5
5
  "keywords": [
6
6
  "auth",
@@ -14,10 +14,11 @@
14
14
  "bcryptjs": "^3.0.3",
15
15
  "drizzle-orm": "^0.45.2",
16
16
  "zod": "^4.3.6",
17
- "@revealui/config": "0.3.1",
18
- "@revealui/contracts": "1.3.4",
19
- "@revealui/core": "0.5.3",
20
- "@revealui/db": "0.3.4"
17
+ "@revealui/config": "0.3.2",
18
+ "@revealui/contracts": "1.3.5",
19
+ "@revealui/core": "0.5.4",
20
+ "@revealui/db": "0.3.5",
21
+ "@revealui/security": "0.2.5"
21
22
  },
22
23
  "devDependencies": {
23
24
  "@simplewebauthn/browser": "^13.3.0",