@payez/next-mvp 4.1.11 → 4.1.13

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.
Files changed (92) hide show
  1. package/dist/api-handlers/auth/refresh.js +15 -38
  2. package/dist/auth/better-auth.d.ts +0 -3
  3. package/dist/auth/better-auth.js +164 -69
  4. package/dist/client/better-auth-client.d.ts +14 -2
  5. package/dist/lib/ensure-fresh-access-token.js +8 -42
  6. package/dist/lib/session-store.d.ts +0 -1
  7. package/dist/lib/session-store.js +29 -33
  8. package/dist/lib/startup-init.js +19 -19
  9. package/dist/lib/token-lifecycle.d.ts +0 -2
  10. package/dist/lib/token-lifecycle.js +3 -21
  11. package/package.json +5 -4
  12. package/scripts/check-internal-url-usage.sh +73 -73
  13. package/src/api/index.ts +17 -17
  14. package/src/api-handlers/account/masked-info.ts +45 -45
  15. package/src/api-handlers/account/profile.ts +80 -80
  16. package/src/api-handlers/account/recovery/initiate.ts +23 -23
  17. package/src/api-handlers/account/recovery/send-code.ts +25 -25
  18. package/src/api-handlers/account/recovery/verify-code.ts +25 -25
  19. package/src/api-handlers/account/reset-password.ts +23 -23
  20. package/src/api-handlers/account/send-code.ts +76 -76
  21. package/src/api-handlers/account/validate-password.ts +118 -118
  22. package/src/api-handlers/admin/stats.ts +249 -249
  23. package/src/api-handlers/anon/preferences.ts +123 -123
  24. package/src/api-handlers/auth/jwks.ts +20 -20
  25. package/src/api-handlers/auth/login.ts +240 -240
  26. package/src/api-handlers/auth/refresh.ts +15 -38
  27. package/src/api-handlers/auth/validate.ts +43 -43
  28. package/src/api-handlers/session/viability.ts +142 -142
  29. package/src/auth/better-auth.ts +130 -29
  30. package/src/auth/unauthenticated-routes.ts +2 -2
  31. package/src/client/better-auth-client.ts +70 -70
  32. package/src/client/fetchWithSession.ts +21 -21
  33. package/src/client/index.ts +13 -13
  34. package/src/client/useAnonSession.ts +131 -131
  35. package/src/components/admin/VibeAdminContext.tsx +87 -87
  36. package/src/config/logging-config.ts +82 -82
  37. package/src/config/unauthenticated-routes.ts +19 -19
  38. package/src/edge/internal-api-url.ts +65 -65
  39. package/src/edge/middleware.ts +42 -42
  40. package/src/hooks/useProfile.ts +75 -75
  41. package/src/lib/anon-session.ts +213 -213
  42. package/src/lib/auth-secret.ts +137 -137
  43. package/src/lib/demo-mode.ts +17 -17
  44. package/src/lib/ensure-fresh-access-token.ts +8 -42
  45. package/src/lib/idp-client-config.ts +559 -559
  46. package/src/lib/idp-fetch.ts +126 -126
  47. package/src/lib/internal-api.ts +171 -171
  48. package/src/lib/jwt-decode-client.ts +45 -45
  49. package/src/lib/rate-limit-service.ts +9 -9
  50. package/src/lib/redis.ts +26 -26
  51. package/src/lib/refresh-token-validator.ts +64 -64
  52. package/src/lib/secret-validation.ts +8 -8
  53. package/src/lib/session-store.ts +692 -695
  54. package/src/lib/session.ts +34 -34
  55. package/src/lib/startup-init.ts +251 -251
  56. package/src/lib/token-expiry.ts +40 -40
  57. package/src/lib/token-lifecycle.ts +470 -497
  58. package/src/lib/types/api-responses.ts +336 -336
  59. package/src/middleware/auth-decision.ts +43 -43
  60. package/src/models/DecodedAccessToken.ts +16 -16
  61. package/src/models/SessionModel.ts +264 -264
  62. package/src/pages/admin-roles/index.ts +9 -9
  63. package/src/pages/security/EnhancedSecurityPage.tsx +9 -9
  64. package/src/routes/account/index.ts +31 -31
  65. package/src/routes/account/masked-info.ts +36 -36
  66. package/src/routes/account/send-code.ts +39 -39
  67. package/src/routes/account/verify-email.ts +40 -40
  68. package/src/routes/account/verify-sms.ts +40 -40
  69. package/src/routes/auth/index.ts +22 -22
  70. package/src/routes/auth/refresh.ts +48 -48
  71. package/src/routes/auth/session.ts +166 -166
  72. package/src/routes/index.ts +17 -17
  73. package/src/routes/session/index.ts +7 -7
  74. package/src/routes/session/refresh-viability.ts +17 -17
  75. package/src/server/auth.ts +373 -373
  76. package/src/server/decode-session.ts +202 -202
  77. package/src/services/signalrActivityService.ts +258 -258
  78. package/src/theme/ThemeProvider.tsx +39 -39
  79. package/src/theme/default.ts +33 -33
  80. package/src/theme/index.ts +31 -31
  81. package/src/theme/types.ts +69 -69
  82. package/src/theme/useTheme.ts +57 -57
  83. package/src/theme/utils.ts +40 -40
  84. package/src/types/api.ts +12 -12
  85. package/src/types/auth.d.ts +23 -23
  86. package/src/types/auth.ts +22 -22
  87. package/src/types/logging.ts +11 -11
  88. package/src/types/recovery.ts +54 -54
  89. package/src/utils/api.ts +353 -353
  90. package/src/utils/circuitBreaker.ts +40 -40
  91. package/src/utils/error-message.ts +108 -108
  92. package/src/utils/layout/reservedSpace.ts +124 -124
@@ -1,25 +1,25 @@
1
- import { NextRequest, NextResponse } from 'next/server';
2
-
3
- function getIdpUrl(): string {
4
- const url = process.env.IDP_URL;
5
- if (!url) throw new Error('[IDP_URL] FATAL: IDP_URL environment variable is REQUIRED.');
6
- return url;
7
- }
8
-
9
- export async function POST(req: NextRequest) {
10
- const IDP_URL = getIdpUrl();
11
- try {
12
- const auth = req.headers.get('authorization') || '';
13
- const token = auth.startsWith('Bearer ') ? auth.slice(7) : '';
14
- const { method } = await req.json();
15
- const upstream = await fetch(`${IDP_URL}/api/Account/recovery/send-code`, {
16
- method: 'POST',
17
- headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` },
18
- body: JSON.stringify({ method }),
19
- });
20
- const data = await upstream.json().catch(() => ({}));
21
- return NextResponse.json(data, { status: upstream.status });
22
- } catch {
23
- return NextResponse.json({ success: false, error: 'send_code_failed' }, { status: 500 });
24
- }
25
- }
1
+ import { NextRequest, NextResponse } from 'next/server';
2
+
3
+ function getIdpUrl(): string {
4
+ const url = process.env.IDP_URL;
5
+ if (!url) throw new Error('[IDP_URL] FATAL: IDP_URL environment variable is REQUIRED.');
6
+ return url;
7
+ }
8
+
9
+ export async function POST(req: NextRequest) {
10
+ const IDP_URL = getIdpUrl();
11
+ try {
12
+ const auth = req.headers.get('authorization') || '';
13
+ const token = auth.startsWith('Bearer ') ? auth.slice(7) : '';
14
+ const { method } = await req.json();
15
+ const upstream = await fetch(`${IDP_URL}/api/Account/recovery/send-code`, {
16
+ method: 'POST',
17
+ headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` },
18
+ body: JSON.stringify({ method }),
19
+ });
20
+ const data = await upstream.json().catch(() => ({}));
21
+ return NextResponse.json(data, { status: upstream.status });
22
+ } catch {
23
+ return NextResponse.json({ success: false, error: 'send_code_failed' }, { status: 500 });
24
+ }
25
+ }
@@ -1,25 +1,25 @@
1
- import { NextRequest, NextResponse } from 'next/server';
2
-
3
- function getIdpUrl(): string {
4
- const url = process.env.IDP_URL;
5
- if (!url) throw new Error('[IDP_URL] FATAL: IDP_URL environment variable is REQUIRED.');
6
- return url;
7
- }
8
-
9
- export async function POST(req: NextRequest) {
10
- const IDP_URL = getIdpUrl();
11
- try {
12
- const auth = req.headers.get('authorization') || '';
13
- const token = auth.startsWith('Bearer ') ? auth.slice(7) : '';
14
- const { code, method } = await req.json();
15
- const upstream = await fetch(`${IDP_URL}/api/Account/recovery/verify-code`, {
16
- method: 'POST',
17
- headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` },
18
- body: JSON.stringify({ code, method }),
19
- });
20
- const data = await upstream.json().catch(() => ({}));
21
- return NextResponse.json(data, { status: upstream.status });
22
- } catch {
23
- return NextResponse.json({ success: false, error: 'verify_code_failed' }, { status: 500 });
24
- }
25
- }
1
+ import { NextRequest, NextResponse } from 'next/server';
2
+
3
+ function getIdpUrl(): string {
4
+ const url = process.env.IDP_URL;
5
+ if (!url) throw new Error('[IDP_URL] FATAL: IDP_URL environment variable is REQUIRED.');
6
+ return url;
7
+ }
8
+
9
+ export async function POST(req: NextRequest) {
10
+ const IDP_URL = getIdpUrl();
11
+ try {
12
+ const auth = req.headers.get('authorization') || '';
13
+ const token = auth.startsWith('Bearer ') ? auth.slice(7) : '';
14
+ const { code, method } = await req.json();
15
+ const upstream = await fetch(`${IDP_URL}/api/Account/recovery/verify-code`, {
16
+ method: 'POST',
17
+ headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` },
18
+ body: JSON.stringify({ code, method }),
19
+ });
20
+ const data = await upstream.json().catch(() => ({}));
21
+ return NextResponse.json(data, { status: upstream.status });
22
+ } catch {
23
+ return NextResponse.json({ success: false, error: 'verify_code_failed' }, { status: 500 });
24
+ }
25
+ }
@@ -1,23 +1,23 @@
1
- import { NextRequest, NextResponse } from 'next/server';
2
-
3
- function getIdpUrl(): string {
4
- const url = process.env.IDP_URL;
5
- if (!url) throw new Error('[IDP_URL] FATAL: IDP_URL environment variable is REQUIRED.');
6
- return url;
7
- }
8
-
9
- export async function POST(req: NextRequest) {
10
- const IDP_URL = getIdpUrl();
11
- try {
12
- const payload = await req.json();
13
- const upstream = await fetch(`${IDP_URL}/api/Account/reset-password`, {
14
- method: 'POST',
15
- headers: { 'Content-Type': 'application/json' },
16
- body: JSON.stringify(payload),
17
- });
18
- const data = await upstream.json().catch(() => ({}));
19
- return NextResponse.json(data, { status: upstream.status });
20
- } catch {
21
- return NextResponse.json({ success: false, error: 'reset_password_failed' }, { status: 500 });
22
- }
23
- }
1
+ import { NextRequest, NextResponse } from 'next/server';
2
+
3
+ function getIdpUrl(): string {
4
+ const url = process.env.IDP_URL;
5
+ if (!url) throw new Error('[IDP_URL] FATAL: IDP_URL environment variable is REQUIRED.');
6
+ return url;
7
+ }
8
+
9
+ export async function POST(req: NextRequest) {
10
+ const IDP_URL = getIdpUrl();
11
+ try {
12
+ const payload = await req.json();
13
+ const upstream = await fetch(`${IDP_URL}/api/Account/reset-password`, {
14
+ method: 'POST',
15
+ headers: { 'Content-Type': 'application/json' },
16
+ body: JSON.stringify(payload),
17
+ });
18
+ const data = await upstream.json().catch(() => ({}));
19
+ return NextResponse.json(data, { status: upstream.status });
20
+ } catch {
21
+ return NextResponse.json({ success: false, error: 'reset_password_failed' }, { status: 500 });
22
+ }
23
+ }
@@ -1,76 +1,76 @@
1
- /**
2
- * Send 2FA Verification Code Handler
3
- *
4
- * Sends a verification code via email or SMS to the authenticated user.
5
- * Requires a provisional Bearer token (ACR=1) from initial login.
6
- *
7
- * @package @payez/next-mvp
8
- */
9
-
10
- import { NextRequest, NextResponse } from 'next/server';
11
- import { idpFetchJSON } from '../../lib/idp-fetch';
12
- import { ENV_CONFIG } from '../../config/env';
13
-
14
- export async function POST(req: NextRequest) {
15
- try {
16
- // Parse request body
17
- const body = await req.json();
18
- const method = String(body.method || '').toLowerCase();
19
-
20
- if (method !== 'sms' && method !== 'email') {
21
- return NextResponse.json(
22
- {
23
- success: false,
24
- error: 'Method must be either "sms" or "email"',
25
- code: 'INVALID_METHOD',
26
- },
27
- { status: 400 }
28
- );
29
- }
30
-
31
- // Build IDP endpoint URL
32
- const idpEndpoint = method === 'sms'
33
- ? '/api/ExternalAuth/twofa/sms/send'
34
- : '/api/ExternalAuth/twofa/email/send';
35
-
36
- // Send client_id in body (lower_snake_case per PayEz standards)
37
- const idpBody = JSON.stringify({ client_id: ENV_CONFIG.CLIENT_ID });
38
-
39
- // Call IDP using idpFetchJSON which auto-injects Bearer token from Redis session
40
- const result = await idpFetchJSON(req, `${ENV_CONFIG.IDP_URL}${idpEndpoint}`, {
41
- method: 'POST',
42
- headers: { 'Content-Type': 'application/json' },
43
- body: idpBody,
44
- });
45
-
46
- if (!result.ok) {
47
- return NextResponse.json(
48
- {
49
- success: false,
50
- error: result.json?.message || `Failed to send ${method} code`,
51
- code: result.json?.code || 'IDP_ERROR',
52
- meta: { attemptedRefresh: result.attemptedRefresh },
53
- },
54
- { status: result.status }
55
- );
56
- }
57
-
58
- return NextResponse.json(
59
- {
60
- success: true,
61
- message: `Verification code sent via ${method}`,
62
- },
63
- { status: 200 }
64
- );
65
- } catch (error) {
66
- console.error('[SEND_CODE] Error:', error);
67
- return NextResponse.json(
68
- {
69
- success: false,
70
- error: 'Failed to send verification code',
71
- code: 'INTERNAL_ERROR',
72
- },
73
- { status: 500 }
74
- );
75
- }
76
- }
1
+ /**
2
+ * Send 2FA Verification Code Handler
3
+ *
4
+ * Sends a verification code via email or SMS to the authenticated user.
5
+ * Requires a provisional Bearer token (ACR=1) from initial login.
6
+ *
7
+ * @package @payez/next-mvp
8
+ */
9
+
10
+ import { NextRequest, NextResponse } from 'next/server';
11
+ import { idpFetchJSON } from '../../lib/idp-fetch';
12
+ import { ENV_CONFIG } from '../../config/env';
13
+
14
+ export async function POST(req: NextRequest) {
15
+ try {
16
+ // Parse request body
17
+ const body = await req.json();
18
+ const method = String(body.method || '').toLowerCase();
19
+
20
+ if (method !== 'sms' && method !== 'email') {
21
+ return NextResponse.json(
22
+ {
23
+ success: false,
24
+ error: 'Method must be either "sms" or "email"',
25
+ code: 'INVALID_METHOD',
26
+ },
27
+ { status: 400 }
28
+ );
29
+ }
30
+
31
+ // Build IDP endpoint URL
32
+ const idpEndpoint = method === 'sms'
33
+ ? '/api/ExternalAuth/twofa/sms/send'
34
+ : '/api/ExternalAuth/twofa/email/send';
35
+
36
+ // Send client_id in body (lower_snake_case per PayEz standards)
37
+ const idpBody = JSON.stringify({ client_id: ENV_CONFIG.CLIENT_ID });
38
+
39
+ // Call IDP using idpFetchJSON which auto-injects Bearer token from Redis session
40
+ const result = await idpFetchJSON(req, `${ENV_CONFIG.IDP_URL}${idpEndpoint}`, {
41
+ method: 'POST',
42
+ headers: { 'Content-Type': 'application/json' },
43
+ body: idpBody,
44
+ });
45
+
46
+ if (!result.ok) {
47
+ return NextResponse.json(
48
+ {
49
+ success: false,
50
+ error: result.json?.message || `Failed to send ${method} code`,
51
+ code: result.json?.code || 'IDP_ERROR',
52
+ meta: { attemptedRefresh: result.attemptedRefresh },
53
+ },
54
+ { status: result.status }
55
+ );
56
+ }
57
+
58
+ return NextResponse.json(
59
+ {
60
+ success: true,
61
+ message: `Verification code sent via ${method}`,
62
+ },
63
+ { status: 200 }
64
+ );
65
+ } catch (error) {
66
+ console.error('[SEND_CODE] Error:', error);
67
+ return NextResponse.json(
68
+ {
69
+ success: false,
70
+ error: 'Failed to send verification code',
71
+ code: 'INTERNAL_ERROR',
72
+ },
73
+ { status: 500 }
74
+ );
75
+ }
76
+ }
@@ -1,118 +1,118 @@
1
- import { NextRequest, NextResponse } from 'next/server';
2
-
3
- interface ValidatePasswordRequest {
4
- password: string;
5
- }
6
-
7
- interface ValidatePasswordResponse {
8
- is_valid: boolean;
9
- score: number;
10
- failed_requirements: string[];
11
- tip?: string;
12
- policy?: {
13
- min_length?: number;
14
- require_uppercase?: boolean;
15
- require_lowercase?: boolean;
16
- require_digit?: boolean;
17
- require_special?: boolean;
18
- min_strength_score?: number;
19
- };
20
- }
21
-
22
- export async function POST(req: NextRequest) {
23
- try {
24
- const body = await req.json();
25
- const { password } = body as ValidatePasswordRequest;
26
- const requestId = req.headers.get('x-request-id') ?? crypto.randomUUID();
27
-
28
- // Validate input
29
- if (!password || typeof password !== 'string') {
30
- return NextResponse.json(
31
- {
32
- is_valid: false,
33
- score: 0,
34
- failed_requirements: ['Password is required'],
35
- } as ValidatePasswordResponse,
36
- {
37
- status: 200, // Return 200 even for validation errors to keep UI responsive
38
- headers: { 'Cache-Control': 'no-store' },
39
- }
40
- );
41
- }
42
-
43
- // Get IDP base URL and client ID from environment
44
- const idpBaseUrl = process.env.IDP_URL;
45
-
46
- const clientId = process.env.CLIENT_ID || process.env.NEXT_PUBLIC_CLIENT_ID;
47
-
48
- if (!idpBaseUrl) {
49
- console.error('[VALIDATE_PASSWORD] IDP_URL not configured');
50
- return NextResponse.json(
51
- {
52
- is_valid: false,
53
- score: 0,
54
- failed_requirements: ['Password validation service unavailable'],
55
- } as ValidatePasswordResponse,
56
- {
57
- status: 200,
58
- headers: { 'Cache-Control': 'no-store' },
59
- }
60
- );
61
- }
62
-
63
- // Proxy request to IDP
64
- const idpUrl = `${idpBaseUrl}/api/Account/validate-password`;
65
- const payload = {
66
- password,
67
- client_id: clientId,
68
- };
69
-
70
- const idpResponse = await fetch(idpUrl, {
71
- method: 'POST',
72
- headers: {
73
- 'Content-Type': 'application/json',
74
- 'x-request-id': requestId,
75
- },
76
- body: JSON.stringify(payload),
77
- });
78
-
79
- const responseData = await idpResponse.json().catch(() => ({}));
80
-
81
- if (!idpResponse.ok) {
82
- console.error('[VALIDATE_PASSWORD] IDP error:', {
83
- status: idpResponse.status,
84
- response: responseData,
85
- });
86
- return NextResponse.json(
87
- {
88
- is_valid: false,
89
- score: 0,
90
- failed_requirements: ['Password validation failed'],
91
- } as ValidatePasswordResponse,
92
- {
93
- status: 200,
94
- headers: { 'Cache-Control': 'no-store' },
95
- }
96
- );
97
- }
98
-
99
- // Return the IDP response with proper structure
100
- return NextResponse.json(responseData as ValidatePasswordResponse, {
101
- status: 200,
102
- headers: { 'Cache-Control': 'no-store' },
103
- });
104
- } catch (error) {
105
- console.error('[VALIDATE_PASSWORD] Error:', error);
106
- return NextResponse.json(
107
- {
108
- is_valid: false,
109
- score: 0,
110
- failed_requirements: ['Password validation failed'],
111
- } as ValidatePasswordResponse,
112
- {
113
- status: 200,
114
- headers: { 'Cache-Control': 'no-store' },
115
- }
116
- );
117
- }
118
- }
1
+ import { NextRequest, NextResponse } from 'next/server';
2
+
3
+ interface ValidatePasswordRequest {
4
+ password: string;
5
+ }
6
+
7
+ interface ValidatePasswordResponse {
8
+ is_valid: boolean;
9
+ score: number;
10
+ failed_requirements: string[];
11
+ tip?: string;
12
+ policy?: {
13
+ min_length?: number;
14
+ require_uppercase?: boolean;
15
+ require_lowercase?: boolean;
16
+ require_digit?: boolean;
17
+ require_special?: boolean;
18
+ min_strength_score?: number;
19
+ };
20
+ }
21
+
22
+ export async function POST(req: NextRequest) {
23
+ try {
24
+ const body = await req.json();
25
+ const { password } = body as ValidatePasswordRequest;
26
+ const requestId = req.headers.get('x-request-id') ?? crypto.randomUUID();
27
+
28
+ // Validate input
29
+ if (!password || typeof password !== 'string') {
30
+ return NextResponse.json(
31
+ {
32
+ is_valid: false,
33
+ score: 0,
34
+ failed_requirements: ['Password is required'],
35
+ } as ValidatePasswordResponse,
36
+ {
37
+ status: 200, // Return 200 even for validation errors to keep UI responsive
38
+ headers: { 'Cache-Control': 'no-store' },
39
+ }
40
+ );
41
+ }
42
+
43
+ // Get IDP base URL and client ID from environment
44
+ const idpBaseUrl = process.env.IDP_URL;
45
+
46
+ const clientId = process.env.CLIENT_ID || process.env.NEXT_PUBLIC_CLIENT_ID;
47
+
48
+ if (!idpBaseUrl) {
49
+ console.error('[VALIDATE_PASSWORD] IDP_URL not configured');
50
+ return NextResponse.json(
51
+ {
52
+ is_valid: false,
53
+ score: 0,
54
+ failed_requirements: ['Password validation service unavailable'],
55
+ } as ValidatePasswordResponse,
56
+ {
57
+ status: 200,
58
+ headers: { 'Cache-Control': 'no-store' },
59
+ }
60
+ );
61
+ }
62
+
63
+ // Proxy request to IDP
64
+ const idpUrl = `${idpBaseUrl}/api/Account/validate-password`;
65
+ const payload = {
66
+ password,
67
+ client_id: clientId,
68
+ };
69
+
70
+ const idpResponse = await fetch(idpUrl, {
71
+ method: 'POST',
72
+ headers: {
73
+ 'Content-Type': 'application/json',
74
+ 'x-request-id': requestId,
75
+ },
76
+ body: JSON.stringify(payload),
77
+ });
78
+
79
+ const responseData = await idpResponse.json().catch(() => ({}));
80
+
81
+ if (!idpResponse.ok) {
82
+ console.error('[VALIDATE_PASSWORD] IDP error:', {
83
+ status: idpResponse.status,
84
+ response: responseData,
85
+ });
86
+ return NextResponse.json(
87
+ {
88
+ is_valid: false,
89
+ score: 0,
90
+ failed_requirements: ['Password validation failed'],
91
+ } as ValidatePasswordResponse,
92
+ {
93
+ status: 200,
94
+ headers: { 'Cache-Control': 'no-store' },
95
+ }
96
+ );
97
+ }
98
+
99
+ // Return the IDP response with proper structure
100
+ return NextResponse.json(responseData as ValidatePasswordResponse, {
101
+ status: 200,
102
+ headers: { 'Cache-Control': 'no-store' },
103
+ });
104
+ } catch (error) {
105
+ console.error('[VALIDATE_PASSWORD] Error:', error);
106
+ return NextResponse.json(
107
+ {
108
+ is_valid: false,
109
+ score: 0,
110
+ failed_requirements: ['Password validation failed'],
111
+ } as ValidatePasswordResponse,
112
+ {
113
+ status: 200,
114
+ headers: { 'Cache-Control': 'no-store' },
115
+ }
116
+ );
117
+ }
118
+ }