@payez/next-mvp 4.8.0 → 4.8.2
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.
- package/POST-INSTALL.md +408 -408
- package/README.md +859 -859
- package/dist/components/account/MobileNavDrawer.js +18 -18
- package/dist/components/account/UserAvatarMenu.js +1 -1
- package/dist/components/admin/AlertSettingsTab.js +40 -40
- package/dist/components/admin/LoggingSettingsTab.js +28 -28
- package/dist/lib/session-store.js +21 -21
- package/dist/lib/startup-init.js +19 -19
- package/dist/middleware/create-middleware.js +6 -5
- package/dist/pages/login/page.js +28 -28
- package/dist/pages/settings/EnhancedSettingsPage.js +17 -17
- package/dist/vibe/sessions.js +28 -3
- package/package.json +2 -2
- package/scripts/check-publish-eol.mjs +33 -15
- package/src/api/auth-handler.ts +550 -550
- package/src/api/index.ts +17 -17
- package/src/api-handlers/account/change-password.ts +170 -170
- package/src/api-handlers/account/masked-info.ts +46 -46
- package/src/api-handlers/account/profile.ts +99 -99
- package/src/api-handlers/account/recovery/forward.ts +117 -117
- package/src/api-handlers/account/recovery/initiate.ts +59 -59
- package/src/api-handlers/account/recovery/send-code.ts +21 -21
- package/src/api-handlers/account/recovery/verify-code.ts +20 -20
- package/src/api-handlers/account/reset-password.ts +87 -87
- package/src/api-handlers/account/send-code.ts +70 -70
- package/src/api-handlers/account/update-phone.ts +84 -84
- package/src/api-handlers/account/validate-password.ts +139 -139
- package/src/api-handlers/account/verify-email.ts +134 -134
- package/src/api-handlers/account/verify-sms.ts +134 -134
- package/src/api-handlers/admin/analytics.ts +452 -452
- package/src/api-handlers/admin/audit.ts +232 -232
- package/src/api-handlers/admin/index.ts +63 -63
- package/src/api-handlers/admin/redis-sessions.ts +259 -259
- package/src/api-handlers/admin/sessions.ts +327 -327
- package/src/api-handlers/admin/site-logs.ts +388 -388
- package/src/api-handlers/admin/stats.ts +260 -260
- package/src/api-handlers/admin/users.ts +251 -251
- package/src/api-handlers/admin/vibe-data.ts +333 -333
- package/src/api-handlers/anon/preferences.ts +123 -123
- package/src/api-handlers/auth/credential-login-email-code.ts +92 -92
- package/src/api-handlers/auth/credential-login.ts +948 -948
- package/src/api-handlers/auth/jwks.ts +31 -31
- package/src/api-handlers/auth/login.ts +252 -252
- package/src/api-handlers/auth/passkey-signin.ts +116 -116
- package/src/api-handlers/auth/refresh.ts +829 -829
- package/src/api-handlers/auth/signout.ts +201 -201
- package/src/api-handlers/auth/status.ts +23 -23
- package/src/api-handlers/auth/update-session.ts +144 -144
- package/src/api-handlers/auth/validate.ts +45 -45
- package/src/api-handlers/auth/verify-code.ts +116 -116
- package/src/api-handlers/mfa/backup-code-verify.ts +170 -170
- package/src/api-handlers/mfa/backup-codes.ts +42 -42
- package/src/api-handlers/mfa/methods.ts +47 -47
- package/src/api-handlers/mfa/totp-confirm.ts +43 -43
- package/src/api-handlers/mfa/totp-enroll.ts +43 -43
- package/src/api-handlers/mfa/totp-verify.ts +172 -172
- package/src/api-handlers/oauth/resume.ts +182 -182
- package/src/api-handlers/onboarding/send-email-code.ts +18 -18
- package/src/api-handlers/onboarding/set-password.ts +21 -21
- package/src/api-handlers/onboarding/verify-code.ts +20 -20
- package/src/api-handlers/onboarding/verify-welcome-email.ts +17 -17
- package/src/api-handlers/passkeys/list.ts +32 -32
- package/src/api-handlers/passkeys/manage.ts +55 -55
- package/src/api-handlers/passkeys/register-options.ts +43 -43
- package/src/api-handlers/passkeys/register-verify.ts +42 -42
- package/src/api-handlers/session/refresh-viability.ts +56 -56
- package/src/api-handlers/session/viability.ts +238 -238
- package/src/api-handlers/test/force-expire.ts +167 -167
- package/src/auth/auth-decision.ts +292 -292
- package/src/auth/better-auth.ts +1688 -1688
- package/src/auth/idp-oauth.ts +310 -310
- package/src/auth/password-reset-bridge.ts +98 -98
- package/src/auth/route-config.ts +236 -236
- package/src/auth/safe-callback.ts +199 -199
- package/src/auth/session-cookie-clear.ts +30 -30
- package/src/auth/session-reset-link.ts +159 -159
- package/src/auth/session-response-sanitizer.ts +151 -151
- package/src/auth/types/auth-types.ts +555 -555
- package/src/auth/types/index.ts +7 -7
- package/src/auth/unauthenticated-routes.ts +2 -2
- package/src/auth/utils/idp-client.ts +364 -364
- package/src/auth/utils/index.ts +6 -6
- package/src/auth/utils/token-utils.ts +243 -243
- package/src/client/AuthContext.tsx +192 -192
- package/src/client/better-auth-client.ts +70 -70
- package/src/client/fetch-with-auth.ts +47 -47
- package/src/client/fetchWithSession.ts +21 -21
- package/src/client/index.ts +13 -13
- package/src/client/useAnonSession.ts +131 -131
- package/src/components/SessionSync.tsx +138 -138
- package/src/components/SignalRHealthCheck.tsx +131 -131
- package/src/components/account/MobileNavDrawer.tsx +305 -305
- package/src/components/account/UserAvatarMenu.tsx +277 -277
- package/src/components/account/index.ts +13 -13
- package/src/components/admin/AlertSettingsTab.tsx +728 -728
- package/src/components/admin/AnalyticsTab.tsx +703 -703
- package/src/components/admin/DataBrowserTab.tsx +508 -508
- package/src/components/admin/LoggingSettingsTab.tsx +665 -665
- package/src/components/admin/SessionsTab.tsx +421 -421
- package/src/components/admin/StatsTab.tsx +379 -379
- package/src/components/admin/VibeAdminContext.tsx +87 -87
- package/src/components/admin/VibeAdminLayout.tsx +187 -187
- package/src/components/admin/index.ts +59 -59
- package/src/components/auth/CredentialSignInForm.tsx +240 -240
- package/src/components/auth/FederatedAuthSection.tsx +129 -129
- package/src/components/auth/ModeAwareLoginPage.tsx +146 -146
- package/src/components/auth/ModeAwareSignupPage.tsx +200 -200
- package/src/components/auth/PasswordResetBridge.tsx +30 -30
- package/src/components/auth/TraditionalAuthSection.tsx +103 -103
- package/src/components/recovery/CompleteStep.tsx +36 -36
- package/src/components/recovery/InitiateRecoveryStep.tsx +68 -68
- package/src/components/recovery/RequestedStep.tsx +30 -30
- package/src/components/recovery/SelectMethodStep.tsx +73 -73
- package/src/components/recovery/SetPasswordStep.tsx +118 -118
- package/src/components/recovery/VerifyCodeStep.tsx +90 -90
- package/src/components/reserved/ReservedRecoveryWarning.tsx +160 -160
- package/src/components/reserved/ReservedStatusBox.tsx +118 -118
- package/src/components/ui/BetaBadge.tsx +58 -58
- package/src/components/ui/Footer.tsx +93 -93
- package/src/config/env.ts +55 -55
- package/src/config/logger.ts +62 -62
- package/src/config/logging-config.ts +82 -82
- package/src/config/unauthenticated-routes.ts +19 -19
- package/src/config/vibe-log-transport.ts +264 -264
- package/src/edge/internal-api-url.ts +65 -65
- package/src/edge/middleware.ts +42 -42
- package/src/hooks/useAuth.ts +117 -117
- package/src/hooks/useAuthSettings.ts +109 -109
- package/src/hooks/useAvailableProviders.ts +143 -143
- package/src/hooks/usePasswordValidation.ts +127 -127
- package/src/hooks/useProfile.ts +82 -82
- package/src/hooks/usePublicAuthSettings.ts +171 -171
- package/src/hooks/useSessionExpiration.ts +101 -101
- package/src/hooks/useViabilitySession.ts +337 -337
- package/src/index.ts +64 -64
- package/src/lib/anon-session.ts +213 -213
- package/src/lib/api-handler.ts +643 -643
- package/src/lib/app-slug.ts +228 -228
- package/src/lib/authorize-resume.ts +172 -172
- package/src/lib/bearer-fetch.ts +31 -31
- package/src/lib/client-key.ts +88 -88
- package/src/lib/code-step-copy.ts +83 -83
- package/src/lib/credential-sign-in-client.ts +101 -101
- package/src/lib/demo-mode.ts +18 -18
- package/src/lib/device-id.ts +67 -67
- package/src/lib/ensure-fresh-access-token.ts +509 -509
- package/src/lib/geolocation.ts +265 -265
- package/src/lib/idp-client-config.ts +1005 -1005
- package/src/lib/idp-fetch.ts +169 -169
- package/src/lib/idp-url-guard.ts +37 -37
- package/src/lib/internal-api.ts +171 -171
- package/src/lib/jwt-decode-client.ts +45 -45
- package/src/lib/jwt-decode.ts +82 -82
- package/src/lib/lock-window.ts +49 -49
- package/src/lib/login-error-message.ts +41 -41
- package/src/lib/never-in-response.ts +64 -64
- package/src/lib/nextauth-secret.ts +168 -168
- package/src/lib/onboarding-proxy.ts +57 -57
- package/src/lib/password-rules.ts +50 -50
- package/src/lib/rate-limit-service.ts +9 -9
- package/src/lib/redis.ts +105 -105
- package/src/lib/refresh-token-validator.ts +64 -64
- package/src/lib/roles.ts +177 -177
- package/src/lib/safe-error-response.ts +149 -149
- package/src/lib/secret-validation.ts +8 -8
- package/src/lib/session-cookie-shape.ts +185 -185
- package/src/lib/session-store.ts +1092 -1092
- package/src/lib/session.ts +34 -34
- package/src/lib/single-flight.ts +64 -64
- package/src/lib/site-logger.ts +245 -245
- package/src/lib/standardized-client-api.ts +901 -901
- package/src/lib/startup-init.ts +276 -276
- package/src/lib/test-aware-get-token.ts +41 -41
- package/src/lib/token-expiry.ts +40 -40
- package/src/lib/token-lifecycle.ts +546 -546
- package/src/lib/token-log-id.ts +69 -69
- package/src/lib/twofa-rejection.ts +91 -91
- package/src/lib/types/api-responses.ts +336 -336
- package/src/lib/user-agent-parser.ts +252 -252
- package/src/logging/api/admin-analytics.ts +51 -51
- package/src/logging/api/audit-log.ts +53 -53
- package/src/logging/components/AdminAnalyticsLayout.tsx +49 -49
- package/src/logging/components/AuditLogViewer.tsx +125 -125
- package/src/logging/components/ErrorMetricsCard.tsx +98 -98
- package/src/logging/components/HealthMetricsCard.tsx +70 -70
- package/src/logging/hooks/useAdminAnalytics.ts +22 -22
- package/src/logging/hooks/useAuditLog.ts +24 -24
- package/src/logging/hooks/useErrorMetrics.ts +40 -40
- package/src/logging/hooks/useHealthMetrics.ts +44 -44
- package/src/logging/index.ts +18 -18
- package/src/logging/types/analytics.ts +81 -81
- package/src/logging/types/audit.ts +31 -31
- package/src/logging/types/index.ts +3 -3
- package/src/middleware/create-middleware.ts +1139 -1138
- package/src/middleware/rbac-check.ts +279 -279
- package/src/middleware/twofa-presets.ts +224 -224
- package/src/models/DecodedAccessToken.ts +16 -16
- package/src/models/SessionModel.ts +331 -331
- package/src/pages/admin-login/page.tsx +111 -111
- package/src/pages/admin-page-permissions/PagePermissionsAdminPage.tsx +527 -527
- package/src/pages/admin-page-permissions/index.ts +7 -7
- package/src/pages/admin-roles/RolesAdminPage.tsx +533 -533
- package/src/pages/admin-roles/index.ts +9 -9
- package/src/pages/admin-roles/modals.tsx +469 -469
- package/src/pages/client-admin/ClientSiteAdminPage.tsx +382 -382
- package/src/pages/client-admin/index.ts +33 -33
- package/src/pages/coming-soon/page.tsx +83 -83
- package/src/pages/login/page.tsx +338 -338
- package/src/pages/onboarding/index.ts +11 -11
- package/src/pages/onboarding/page.tsx +212 -212
- package/src/pages/profile/EnhancedProfilePage.tsx +514 -514
- package/src/pages/profile/index.ts +9 -9
- package/src/pages/profile/page.tsx +166 -166
- package/src/pages/recovery/page.tsx +267 -267
- package/src/pages/roles/MyRolesPage.tsx +211 -211
- package/src/pages/roles/components.tsx +294 -294
- package/src/pages/roles/index.ts +17 -17
- package/src/pages/security/EnhancedSecurityPage.tsx +660 -660
- package/src/pages/security/index.ts +12 -12
- package/src/pages/security/page.tsx +507 -507
- package/src/pages/settings/EnhancedSettingsPage.tsx +642 -642
- package/src/pages/settings/index.ts +9 -9
- package/src/pages/settings/page.tsx +47 -47
- package/src/pages/showcase/ShowcasePage.tsx +532 -532
- package/src/pages/showcase/index.ts +13 -13
- package/src/pages/test-env/EmergencyLogoutPage.tsx +192 -192
- package/src/pages/test-env/JwtInspectPage.tsx +449 -449
- package/src/pages/test-env/RefreshTokenPage.tsx +178 -178
- package/src/pages/test-env/TestEnvPage.tsx +130 -130
- package/src/pages/test-env/guarded.tsx +81 -81
- package/src/pages/test-env/index.ts +77 -77
- package/src/pages/test-env/redact-credentials.ts +96 -96
- package/src/pages/verify-code/page.tsx +746 -746
- package/src/routes/account/index.ts +31 -31
- package/src/routes/account/masked-info.ts +36 -36
- package/src/routes/account/send-code.ts +39 -39
- package/src/routes/account/update-phone.ts +13 -13
- package/src/routes/account/verify-email.ts +40 -40
- package/src/routes/account/verify-sms.ts +40 -40
- package/src/routes/auth/credential-login-email-code.ts +6 -6
- package/src/routes/auth/credential-login.ts +6 -6
- package/src/routes/auth/index.ts +22 -22
- package/src/routes/auth/logout.ts +111 -111
- package/src/routes/auth/nextauth.ts +45 -45
- package/src/routes/auth/passkey-signin.ts +13 -13
- package/src/routes/auth/refresh.ts +77 -77
- package/src/routes/auth/session-reset.ts +37 -37
- package/src/routes/auth/session.ts +261 -261
- package/src/routes/auth/settings.ts +181 -181
- package/src/routes/auth/viability.ts +519 -519
- package/src/routes/index.ts +17 -17
- package/src/routes/mfa/backup-code-verify.ts +1 -1
- package/src/routes/mfa/backup-codes.ts +10 -10
- package/src/routes/mfa/methods.ts +22 -22
- package/src/routes/mfa/totp-confirm.ts +10 -10
- package/src/routes/mfa/totp-enroll.ts +10 -10
- package/src/routes/mfa/totp-verify.ts +14 -14
- package/src/routes/onboarding/index.ts +15 -15
- package/src/routes/onboarding/send-email-code.ts +2 -2
- package/src/routes/onboarding/set-password.ts +2 -2
- package/src/routes/onboarding/verify-code.ts +2 -2
- package/src/routes/onboarding/verify-welcome-email.ts +2 -2
- package/src/routes/passkeys/list.ts +10 -10
- package/src/routes/passkeys/manage.ts +23 -23
- package/src/routes/passkeys/register-options.ts +10 -10
- package/src/routes/passkeys/register-verify.ts +10 -10
- package/src/routes/session/index.ts +7 -7
- package/src/routes/session/refresh-viability.ts +17 -17
- package/src/server/auth-guard.ts +203 -203
- package/src/server/auth.ts +486 -486
- package/src/server/decode-session.ts +309 -309
- package/src/server/slim-middleware.ts +117 -117
- package/src/server/verified-session-token.ts +103 -103
- package/src/server/with-auth.ts +93 -93
- package/src/services/signalrActivityService.ts +258 -258
- package/src/stores/authStore.ts +1900 -1900
- package/src/templates/instrumentation.ts +60 -60
- package/src/theme/ThemeProvider.tsx +39 -39
- package/src/theme/default.ts +33 -33
- package/src/theme/index.ts +31 -31
- package/src/theme/types.ts +69 -69
- package/src/theme/useTheme.ts +57 -57
- package/src/theme/utils.ts +40 -40
- package/src/types/api.ts +12 -12
- package/src/types/auth.d.ts +23 -23
- package/src/types/auth.ts +48 -48
- package/src/types/logging.ts +11 -11
- package/src/types/recovery.ts +54 -54
- package/src/utils/api.ts +401 -401
- package/src/utils/circuitBreaker.ts +40 -40
- package/src/utils/error-message.ts +108 -108
- package/src/utils/layout/reservedSpace.ts +124 -124
- package/src/utils/logout.ts +30 -30
- package/src/vibe/client.ts +877 -877
- package/src/vibe/enterprise-auth.ts +208 -208
- package/src/vibe/errors.ts +209 -209
- package/src/vibe/generic.ts +339 -339
- package/src/vibe/hooks/index.ts +367 -367
- package/src/vibe/index.ts +133 -133
- package/src/vibe/sessions.ts +925 -899
- package/src/vibe/types.ts +577 -577
|
@@ -1,134 +1,134 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Verify SMS 2FA Code Handler
|
|
3
|
-
*
|
|
4
|
-
* Verifies the 2FA SMS verification code and completes the 2FA flow.
|
|
5
|
-
* Updates the session with new tokens upon successful verification.
|
|
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
|
-
import { getTokenTestAware } from '../../lib/test-aware-get-token';
|
|
14
|
-
import { transitionTo2FASession } from '../../lib/session-store';
|
|
15
|
-
import { jwtDecode } from '../../lib/jwt-decode';
|
|
16
|
-
import { twoFactorVerifyFailure } from '../../lib/two-factor-verify-failure';
|
|
17
|
-
|
|
18
|
-
export async function POST(req: NextRequest) {
|
|
19
|
-
try {
|
|
20
|
-
// Parse request body
|
|
21
|
-
const body = await req.json();
|
|
22
|
-
const verificationCode = body.verificationCode || body.verification_code;
|
|
23
|
-
|
|
24
|
-
if (!verificationCode) {
|
|
25
|
-
return NextResponse.json(
|
|
26
|
-
{
|
|
27
|
-
success: false,
|
|
28
|
-
error: 'Verification code is required',
|
|
29
|
-
code: 'INVALID_REQUEST',
|
|
30
|
-
},
|
|
31
|
-
{ status: 400 }
|
|
32
|
-
);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// Call IDP using idpFetchJSON which auto-injects Bearer token from Redis session
|
|
36
|
-
const result = await idpFetchJSON(req, `${ENV_CONFIG.IDP_URL}/api/ExternalAuth/twofa/sms/verify`, {
|
|
37
|
-
method: 'POST',
|
|
38
|
-
headers: { 'Content-Type': 'application/json' },
|
|
39
|
-
body: JSON.stringify({ verification_code: verificationCode }),
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
if (!result.ok) {
|
|
43
|
-
// PAY-1443: never relay the IDP's error text - it arrives as a C# anonymous-object
|
|
44
|
-
// ToString ("{ message = ... }") on the deployed IDP. Classify it, answer a fixed sentence.
|
|
45
|
-
return twoFactorVerifyFailure(result, 'sms');
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Unwrap IDP envelope
|
|
49
|
-
const unwrappedData = result.json?.data || result.json;
|
|
50
|
-
|
|
51
|
-
// If we have new tokens, update the session to complete 2FA
|
|
52
|
-
// Reports whether the session ACTUALLY transitioned. Must not be assumed
|
|
53
|
-
// from a 2xx: the IDP can verify the code while we fail to transition
|
|
54
|
-
// (no tokens returned, or no resolvable session token).
|
|
55
|
-
let sessionTransitioned = false;
|
|
56
|
-
|
|
57
|
-
if (unwrappedData.access_token && unwrappedData.refresh_token) {
|
|
58
|
-
// Get session token from NextAuth
|
|
59
|
-
// Support both field names: sessionToken (auth.ts JWT) and redisSessionId (legacy)
|
|
60
|
-
const token = await getTokenTestAware(req);
|
|
61
|
-
const sessionToken = (token?.sessionToken || token?.redisSessionId) as string | undefined;
|
|
62
|
-
|
|
63
|
-
if (sessionToken) {
|
|
64
|
-
console.log('[VERIFY_SMS] Updating session with new tokens');
|
|
65
|
-
|
|
66
|
-
// Decode access token to get actual expiration
|
|
67
|
-
let accessTokenExpires = Date.now() + (15 * 60 * 1000); // Default: 15 minutes
|
|
68
|
-
try {
|
|
69
|
-
const decoded = jwtDecode<{ exp?: number }>(unwrappedData.access_token);
|
|
70
|
-
if (decoded?.exp) {
|
|
71
|
-
accessTokenExpires = decoded.exp * 1000; // Convert to milliseconds
|
|
72
|
-
}
|
|
73
|
-
} catch (err) {
|
|
74
|
-
console.warn('[VERIFY_SMS] Could not decode access token, using default expiration');
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Decode refresh token to get actual expiration (optional)
|
|
78
|
-
let refreshTokenExpires = Date.now() + (3 * 24 * 60 * 60 * 1000); // Default: 3 days
|
|
79
|
-
try {
|
|
80
|
-
const decoded = jwtDecode<{ exp?: number }>(unwrappedData.refresh_token);
|
|
81
|
-
if (decoded?.exp) {
|
|
82
|
-
refreshTokenExpires = decoded.exp * 1000;
|
|
83
|
-
}
|
|
84
|
-
} catch {
|
|
85
|
-
// Refresh token may not have exp claim, use default
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Update session with new tokens and mark 2FA complete
|
|
89
|
-
await transitionTo2FASession(
|
|
90
|
-
sessionToken,
|
|
91
|
-
{
|
|
92
|
-
accessToken: unwrappedData.access_token,
|
|
93
|
-
refreshToken: unwrappedData.refresh_token,
|
|
94
|
-
accessTokenExpires,
|
|
95
|
-
refreshTokenExpires
|
|
96
|
-
},
|
|
97
|
-
'sms' // Store 2FA method for refresh token flow
|
|
98
|
-
);
|
|
99
|
-
|
|
100
|
-
sessionTransitioned = true;
|
|
101
|
-
|
|
102
|
-
console.log('[VERIFY_SMS] Session updated successfully', {
|
|
103
|
-
accessTokenExpires: new Date(accessTokenExpires).toISOString(),
|
|
104
|
-
refreshTokenExpires: new Date(refreshTokenExpires).toISOString()
|
|
105
|
-
});
|
|
106
|
-
} else {
|
|
107
|
-
console.warn('[VERIFY_SMS] IDP verified the code but no session token was resolvable — session NOT transitioned');
|
|
108
|
-
}
|
|
109
|
-
} else {
|
|
110
|
-
console.warn('[VERIFY_SMS] IDP returned 2xx without tokens — session NOT transitioned');
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Return simplified success response (don't expose tokens to client)
|
|
114
|
-
return NextResponse.json({
|
|
115
|
-
success: true,
|
|
116
|
-
verificationSuccessful: true,
|
|
117
|
-
// NOT a copy of `success` — reports what actually happened. A client that
|
|
118
|
-
// trusted a hardcoded true would show the user as MFA-verified while the
|
|
119
|
-
// session still says otherwise.
|
|
120
|
-
twoFactorSessionVerified: sessionTransitioned,
|
|
121
|
-
message: unwrappedData.message || 'SMS code verified successfully'
|
|
122
|
-
}, { status: 200 });
|
|
123
|
-
} catch (error) {
|
|
124
|
-
console.error('[VERIFY_SMS] Error:', error);
|
|
125
|
-
return NextResponse.json(
|
|
126
|
-
{
|
|
127
|
-
success: false,
|
|
128
|
-
error: 'Failed to verify code',
|
|
129
|
-
code: 'INTERNAL_ERROR',
|
|
130
|
-
},
|
|
131
|
-
{ status: 500 }
|
|
132
|
-
);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Verify SMS 2FA Code Handler
|
|
3
|
+
*
|
|
4
|
+
* Verifies the 2FA SMS verification code and completes the 2FA flow.
|
|
5
|
+
* Updates the session with new tokens upon successful verification.
|
|
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
|
+
import { getTokenTestAware } from '../../lib/test-aware-get-token';
|
|
14
|
+
import { transitionTo2FASession } from '../../lib/session-store';
|
|
15
|
+
import { jwtDecode } from '../../lib/jwt-decode';
|
|
16
|
+
import { twoFactorVerifyFailure } from '../../lib/two-factor-verify-failure';
|
|
17
|
+
|
|
18
|
+
export async function POST(req: NextRequest) {
|
|
19
|
+
try {
|
|
20
|
+
// Parse request body
|
|
21
|
+
const body = await req.json();
|
|
22
|
+
const verificationCode = body.verificationCode || body.verification_code;
|
|
23
|
+
|
|
24
|
+
if (!verificationCode) {
|
|
25
|
+
return NextResponse.json(
|
|
26
|
+
{
|
|
27
|
+
success: false,
|
|
28
|
+
error: 'Verification code is required',
|
|
29
|
+
code: 'INVALID_REQUEST',
|
|
30
|
+
},
|
|
31
|
+
{ status: 400 }
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Call IDP using idpFetchJSON which auto-injects Bearer token from Redis session
|
|
36
|
+
const result = await idpFetchJSON(req, `${ENV_CONFIG.IDP_URL}/api/ExternalAuth/twofa/sms/verify`, {
|
|
37
|
+
method: 'POST',
|
|
38
|
+
headers: { 'Content-Type': 'application/json' },
|
|
39
|
+
body: JSON.stringify({ verification_code: verificationCode }),
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
if (!result.ok) {
|
|
43
|
+
// PAY-1443: never relay the IDP's error text - it arrives as a C# anonymous-object
|
|
44
|
+
// ToString ("{ message = ... }") on the deployed IDP. Classify it, answer a fixed sentence.
|
|
45
|
+
return twoFactorVerifyFailure(result, 'sms');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Unwrap IDP envelope
|
|
49
|
+
const unwrappedData = result.json?.data || result.json;
|
|
50
|
+
|
|
51
|
+
// If we have new tokens, update the session to complete 2FA
|
|
52
|
+
// Reports whether the session ACTUALLY transitioned. Must not be assumed
|
|
53
|
+
// from a 2xx: the IDP can verify the code while we fail to transition
|
|
54
|
+
// (no tokens returned, or no resolvable session token).
|
|
55
|
+
let sessionTransitioned = false;
|
|
56
|
+
|
|
57
|
+
if (unwrappedData.access_token && unwrappedData.refresh_token) {
|
|
58
|
+
// Get session token from NextAuth
|
|
59
|
+
// Support both field names: sessionToken (auth.ts JWT) and redisSessionId (legacy)
|
|
60
|
+
const token = await getTokenTestAware(req);
|
|
61
|
+
const sessionToken = (token?.sessionToken || token?.redisSessionId) as string | undefined;
|
|
62
|
+
|
|
63
|
+
if (sessionToken) {
|
|
64
|
+
console.log('[VERIFY_SMS] Updating session with new tokens');
|
|
65
|
+
|
|
66
|
+
// Decode access token to get actual expiration
|
|
67
|
+
let accessTokenExpires = Date.now() + (15 * 60 * 1000); // Default: 15 minutes
|
|
68
|
+
try {
|
|
69
|
+
const decoded = jwtDecode<{ exp?: number }>(unwrappedData.access_token);
|
|
70
|
+
if (decoded?.exp) {
|
|
71
|
+
accessTokenExpires = decoded.exp * 1000; // Convert to milliseconds
|
|
72
|
+
}
|
|
73
|
+
} catch (err) {
|
|
74
|
+
console.warn('[VERIFY_SMS] Could not decode access token, using default expiration');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Decode refresh token to get actual expiration (optional)
|
|
78
|
+
let refreshTokenExpires = Date.now() + (3 * 24 * 60 * 60 * 1000); // Default: 3 days
|
|
79
|
+
try {
|
|
80
|
+
const decoded = jwtDecode<{ exp?: number }>(unwrappedData.refresh_token);
|
|
81
|
+
if (decoded?.exp) {
|
|
82
|
+
refreshTokenExpires = decoded.exp * 1000;
|
|
83
|
+
}
|
|
84
|
+
} catch {
|
|
85
|
+
// Refresh token may not have exp claim, use default
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Update session with new tokens and mark 2FA complete
|
|
89
|
+
await transitionTo2FASession(
|
|
90
|
+
sessionToken,
|
|
91
|
+
{
|
|
92
|
+
accessToken: unwrappedData.access_token,
|
|
93
|
+
refreshToken: unwrappedData.refresh_token,
|
|
94
|
+
accessTokenExpires,
|
|
95
|
+
refreshTokenExpires
|
|
96
|
+
},
|
|
97
|
+
'sms' // Store 2FA method for refresh token flow
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
sessionTransitioned = true;
|
|
101
|
+
|
|
102
|
+
console.log('[VERIFY_SMS] Session updated successfully', {
|
|
103
|
+
accessTokenExpires: new Date(accessTokenExpires).toISOString(),
|
|
104
|
+
refreshTokenExpires: new Date(refreshTokenExpires).toISOString()
|
|
105
|
+
});
|
|
106
|
+
} else {
|
|
107
|
+
console.warn('[VERIFY_SMS] IDP verified the code but no session token was resolvable — session NOT transitioned');
|
|
108
|
+
}
|
|
109
|
+
} else {
|
|
110
|
+
console.warn('[VERIFY_SMS] IDP returned 2xx without tokens — session NOT transitioned');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Return simplified success response (don't expose tokens to client)
|
|
114
|
+
return NextResponse.json({
|
|
115
|
+
success: true,
|
|
116
|
+
verificationSuccessful: true,
|
|
117
|
+
// NOT a copy of `success` — reports what actually happened. A client that
|
|
118
|
+
// trusted a hardcoded true would show the user as MFA-verified while the
|
|
119
|
+
// session still says otherwise.
|
|
120
|
+
twoFactorSessionVerified: sessionTransitioned,
|
|
121
|
+
message: unwrappedData.message || 'SMS code verified successfully'
|
|
122
|
+
}, { status: 200 });
|
|
123
|
+
} catch (error) {
|
|
124
|
+
console.error('[VERIFY_SMS] Error:', error);
|
|
125
|
+
return NextResponse.json(
|
|
126
|
+
{
|
|
127
|
+
success: false,
|
|
128
|
+
error: 'Failed to verify code',
|
|
129
|
+
code: 'INTERNAL_ERROR',
|
|
130
|
+
},
|
|
131
|
+
{ status: 500 }
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
}
|