@payez/next-mvp 4.1.7 → 4.1.9
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/README.md +792 -792
- package/dist/auth/better-auth.js +15 -3
- 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/pages/login/page.js +28 -28
- package/dist/pages/settings/EnhancedSettingsPage.js +17 -17
- package/package.json +1 -1
- package/scripts/dev-broker.ps1 +35 -35
- package/scripts/dev-local.ps1 +45 -45
- package/src/api/auth-handler.ts +548 -548
- package/src/api-handlers/account/change-password.ts +142 -142
- package/src/api-handlers/account/update-phone.ts +79 -79
- package/src/api-handlers/account/verify-email.ts +125 -125
- package/src/api-handlers/account/verify-sms.ts +125 -125
- package/src/api-handlers/admin/analytics.ts +443 -443
- package/src/api-handlers/admin/audit.ts +223 -223
- package/src/api-handlers/admin/index.ts +63 -63
- package/src/api-handlers/admin/redis-sessions.ts +251 -251
- package/src/api-handlers/admin/sessions.ts +318 -318
- package/src/api-handlers/admin/site-logs.ts +365 -365
- package/src/api-handlers/admin/users.ts +242 -242
- package/src/api-handlers/admin/vibe-data.ts +324 -324
- package/src/api-handlers/auth/refresh.ts +682 -682
- package/src/api-handlers/auth/signout.ts +199 -199
- package/src/api-handlers/auth/status.ts +20 -20
- package/src/api-handlers/auth/update-session.ts +110 -110
- package/src/api-handlers/auth/verify-code.ts +112 -112
- package/src/api-handlers/session/refresh-viability.ts +36 -36
- package/src/api-handlers/test/force-expire.ts +60 -60
- package/src/auth/auth-decision.ts +229 -229
- package/src/auth/better-auth.ts +15 -3
- package/src/auth/route-config.ts +219 -219
- package/src/auth/types/auth-types.ts +555 -555
- package/src/auth/types/index.ts +7 -7
- package/src/auth/utils/idp-client.ts +445 -445
- package/src/auth/utils/index.ts +6 -6
- package/src/auth/utils/token-utils.ts +243 -243
- package/src/client/AuthContext.tsx +143 -143
- package/src/client/fetch-with-auth.ts +47 -47
- 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 +266 -266
- 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 +505 -505
- package/src/components/admin/LoggingSettingsTab.tsx +665 -665
- package/src/components/admin/SessionsTab.tsx +414 -414
- package/src/components/admin/StatsTab.tsx +379 -379
- package/src/components/admin/VibeAdminLayout.tsx +187 -187
- package/src/components/admin/index.ts +59 -59
- package/src/components/auth/FederatedAuthSection.tsx +95 -95
- package/src/components/auth/ModeAwareLoginPage.tsx +135 -135
- package/src/components/auth/ModeAwareSignupPage.tsx +267 -267
- package/src/components/auth/TraditionalAuthSection.tsx +99 -99
- package/src/components/recovery/CompleteStep.tsx +36 -36
- package/src/components/recovery/InitiateRecoveryStep.tsx +68 -68
- package/src/components/recovery/SelectMethodStep.tsx +73 -73
- package/src/components/recovery/SetPasswordStep.tsx +97 -97
- 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 +57 -57
- package/src/config/logger.ts +62 -62
- package/src/config/vibe-log-transport.ts +264 -264
- package/src/hooks/useAuth.ts +117 -117
- package/src/hooks/useAuthSettings.ts +97 -97
- package/src/hooks/useAvailableProviders.ts +120 -120
- package/src/hooks/usePasswordValidation.ts +127 -127
- package/src/hooks/usePublicAuthSettings.ts +149 -149
- package/src/hooks/useSessionExpiration.ts +101 -101
- package/src/hooks/useViabilitySession.ts +336 -336
- package/src/index.ts +60 -60
- package/src/lib/api-handler.ts +643 -643
- package/src/lib/app-slug.ts +178 -178
- package/src/lib/geolocation.ts +265 -265
- package/src/lib/idp-fetch.ts +110 -110
- package/src/lib/jwt-decode.ts +82 -82
- package/src/lib/roles.ts +177 -177
- package/src/lib/site-logger.ts +245 -245
- package/src/lib/standardized-client-api.ts +901 -901
- package/src/lib/test-aware-get-token.ts +41 -41
- 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 +663 -663
- package/src/middleware/rbac-check.ts +279 -279
- package/src/middleware/twofa-presets.ts +224 -224
- package/src/pages/admin-login/page.tsx +221 -221
- 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/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 +456 -456
- package/src/pages/profile/EnhancedProfilePage.tsx +485 -485
- package/src/pages/profile/index.ts +9 -9
- package/src/pages/profile/page.tsx +166 -166
- package/src/pages/recovery/page.tsx +234 -234
- 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 +565 -565
- package/src/pages/security/index.ts +9 -9
- 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 +180 -180
- package/src/pages/test-env/JwtInspectPage.tsx +456 -456
- package/src/pages/test-env/RefreshTokenPage.tsx +157 -157
- package/src/pages/test-env/TestEnvPage.tsx +118 -118
- package/src/pages/test-env/index.ts +25 -25
- package/src/pages/verify-code/page.tsx +652 -652
- package/src/routes/account/update-phone.ts +13 -13
- package/src/routes/auth/logout.ts +108 -108
- package/src/routes/auth/nextauth.ts +45 -45
- package/src/routes/auth/settings.ts +75 -75
- package/src/routes/auth/viability.ts +224 -224
- package/src/server/auth-guard.ts +170 -170
- package/src/server/slim-middleware.ts +117 -117
- package/src/server/with-auth.ts +93 -93
- package/src/stores/authStore.ts +1899 -1899
- package/src/templates/instrumentation.ts +41 -41
- package/src/utils/logout.ts +30 -30
- package/src/vibe/client.ts +590 -590
- package/src/vibe/enterprise-auth.ts +208 -208
- package/src/vibe/errors.ts +185 -185
- package/src/vibe/generic.ts +431 -431
- package/src/vibe/hooks/index.ts +367 -367
- package/src/vibe/index.ts +132 -132
- package/src/vibe/sessions.ts +551 -551
- package/src/vibe/types.ts +577 -577
|
@@ -1,142 +1,142 @@
|
|
|
1
|
-
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
-
import { getSession as getBetterAuthSession } from '../../server/auth';
|
|
3
|
-
import { getSession } from '../../lib/session-store';
|
|
4
|
-
|
|
5
|
-
interface ChangePasswordRequest {
|
|
6
|
-
current_password: string;
|
|
7
|
-
new_password: string;
|
|
8
|
-
confirm_password: string;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
import { nanoid } from 'nanoid';
|
|
12
|
-
|
|
13
|
-
// ...
|
|
14
|
-
|
|
15
|
-
export async function POST(req: NextRequest) {
|
|
16
|
-
const requestId = nanoid();
|
|
17
|
-
try {
|
|
18
|
-
// Get session from Better Auth
|
|
19
|
-
const betterAuthSession = await getBetterAuthSession(req);
|
|
20
|
-
const sessionToken = betterAuthSession?.session?.token as string | undefined;
|
|
21
|
-
if (!betterAuthSession || typeof sessionToken !== 'string') {
|
|
22
|
-
return NextResponse.json({ success: false, message: 'Unauthorized' }, { status: 401 });
|
|
23
|
-
}
|
|
24
|
-
const sessionData = await getSession(sessionToken);
|
|
25
|
-
// NOTE: Field is idpAccessToken (not accessToken) per normalized naming convention
|
|
26
|
-
if (!sessionData?.idpAccessToken) {
|
|
27
|
-
return NextResponse.json(
|
|
28
|
-
{
|
|
29
|
-
success: false,
|
|
30
|
-
message: 'Authentication required - no access token available',
|
|
31
|
-
error_code: 'UNAUTHORIZED',
|
|
32
|
-
request_id: requestId,
|
|
33
|
-
},
|
|
34
|
-
{ status: 401 }
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const body = await req.json();
|
|
39
|
-
const { current_password, new_password, confirm_password } = body as ChangePasswordRequest;
|
|
40
|
-
|
|
41
|
-
// Validate input
|
|
42
|
-
if (!current_password || !new_password || !confirm_password) {
|
|
43
|
-
return NextResponse.json(
|
|
44
|
-
{
|
|
45
|
-
success: false,
|
|
46
|
-
message: 'Current password, new password, and confirmation are required',
|
|
47
|
-
error_code: 'VALIDATION_ERROR',
|
|
48
|
-
request_id: requestId,
|
|
49
|
-
},
|
|
50
|
-
{ status: 400 }
|
|
51
|
-
);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (new_password !== confirm_password) {
|
|
55
|
-
return NextResponse.json(
|
|
56
|
-
{
|
|
57
|
-
success: false,
|
|
58
|
-
message: 'New password and confirmation do not match',
|
|
59
|
-
error_code: 'VALIDATION_ERROR',
|
|
60
|
-
request_id: requestId,
|
|
61
|
-
},
|
|
62
|
-
{ status: 400 }
|
|
63
|
-
);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Get IDP base URL from environment
|
|
67
|
-
const idpBaseUrl = process.env.IDP_URL;
|
|
68
|
-
|
|
69
|
-
if (!idpBaseUrl) {
|
|
70
|
-
console.error('[CHANGE_PASSWORD] IDP_URL not configured');
|
|
71
|
-
return NextResponse.json(
|
|
72
|
-
{
|
|
73
|
-
success: false,
|
|
74
|
-
message: 'Service configuration error',
|
|
75
|
-
error_code: 'CONFIGURATION_ERROR',
|
|
76
|
-
request_id: requestId,
|
|
77
|
-
},
|
|
78
|
-
{ status: 500 }
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Proxy request to IDP
|
|
83
|
-
const idpUrl = `${idpBaseUrl}/api/Account/change-password`;
|
|
84
|
-
const idpResponse = await fetch(idpUrl, {
|
|
85
|
-
method: 'POST',
|
|
86
|
-
headers: {
|
|
87
|
-
'Content-Type': 'application/json',
|
|
88
|
-
'Authorization': `Bearer ${sessionData.idpAccessToken}`,
|
|
89
|
-
'x-request-id': requestId,
|
|
90
|
-
},
|
|
91
|
-
body: JSON.stringify({
|
|
92
|
-
current_password,
|
|
93
|
-
new_password,
|
|
94
|
-
confirm_password,
|
|
95
|
-
}),
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
const responseData = await idpResponse.json().catch(() => ({}));
|
|
99
|
-
|
|
100
|
-
if (!idpResponse.ok) {
|
|
101
|
-
// Extract error message from IDP response
|
|
102
|
-
let errorMessage = 'Failed to change password';
|
|
103
|
-
|
|
104
|
-
if (responseData.message) {
|
|
105
|
-
errorMessage = responseData.message;
|
|
106
|
-
} else if (responseData.details?.value && Array.isArray(responseData.details.value) && responseData.details.value.length > 0) {
|
|
107
|
-
errorMessage = responseData.details.value[0].message || errorMessage;
|
|
108
|
-
} else if (responseData.details?.message) {
|
|
109
|
-
errorMessage = responseData.details.message;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
return NextResponse.json(
|
|
113
|
-
{
|
|
114
|
-
success: false,
|
|
115
|
-
message: errorMessage,
|
|
116
|
-
error_code: responseData.error_code || 'CHANGE_PASSWORD_FAILED',
|
|
117
|
-
request_id: requestId,
|
|
118
|
-
details: responseData.details,
|
|
119
|
-
},
|
|
120
|
-
{ status: idpResponse.status }
|
|
121
|
-
);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
return NextResponse.json({
|
|
125
|
-
success: true,
|
|
126
|
-
message: responseData.message || 'Password changed successfully',
|
|
127
|
-
request_id: requestId,
|
|
128
|
-
});
|
|
129
|
-
} catch (error) {
|
|
130
|
-
console.error('[CHANGE_PASSWORD] Error:', error);
|
|
131
|
-
const requestId = req.headers.get('x-request-id') ?? crypto.randomUUID();
|
|
132
|
-
return NextResponse.json(
|
|
133
|
-
{
|
|
134
|
-
success: false,
|
|
135
|
-
message: error instanceof Error ? error.message : 'Failed to change password',
|
|
136
|
-
error_code: 'INTERNAL_ERROR',
|
|
137
|
-
request_id: requestId,
|
|
138
|
-
},
|
|
139
|
-
{ status: 500 }
|
|
140
|
-
);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { getSession as getBetterAuthSession } from '../../server/auth';
|
|
3
|
+
import { getSession } from '../../lib/session-store';
|
|
4
|
+
|
|
5
|
+
interface ChangePasswordRequest {
|
|
6
|
+
current_password: string;
|
|
7
|
+
new_password: string;
|
|
8
|
+
confirm_password: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
import { nanoid } from 'nanoid';
|
|
12
|
+
|
|
13
|
+
// ...
|
|
14
|
+
|
|
15
|
+
export async function POST(req: NextRequest) {
|
|
16
|
+
const requestId = nanoid();
|
|
17
|
+
try {
|
|
18
|
+
// Get session from Better Auth
|
|
19
|
+
const betterAuthSession = await getBetterAuthSession(req);
|
|
20
|
+
const sessionToken = betterAuthSession?.session?.token as string | undefined;
|
|
21
|
+
if (!betterAuthSession || typeof sessionToken !== 'string') {
|
|
22
|
+
return NextResponse.json({ success: false, message: 'Unauthorized' }, { status: 401 });
|
|
23
|
+
}
|
|
24
|
+
const sessionData = await getSession(sessionToken);
|
|
25
|
+
// NOTE: Field is idpAccessToken (not accessToken) per normalized naming convention
|
|
26
|
+
if (!sessionData?.idpAccessToken) {
|
|
27
|
+
return NextResponse.json(
|
|
28
|
+
{
|
|
29
|
+
success: false,
|
|
30
|
+
message: 'Authentication required - no access token available',
|
|
31
|
+
error_code: 'UNAUTHORIZED',
|
|
32
|
+
request_id: requestId,
|
|
33
|
+
},
|
|
34
|
+
{ status: 401 }
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const body = await req.json();
|
|
39
|
+
const { current_password, new_password, confirm_password } = body as ChangePasswordRequest;
|
|
40
|
+
|
|
41
|
+
// Validate input
|
|
42
|
+
if (!current_password || !new_password || !confirm_password) {
|
|
43
|
+
return NextResponse.json(
|
|
44
|
+
{
|
|
45
|
+
success: false,
|
|
46
|
+
message: 'Current password, new password, and confirmation are required',
|
|
47
|
+
error_code: 'VALIDATION_ERROR',
|
|
48
|
+
request_id: requestId,
|
|
49
|
+
},
|
|
50
|
+
{ status: 400 }
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (new_password !== confirm_password) {
|
|
55
|
+
return NextResponse.json(
|
|
56
|
+
{
|
|
57
|
+
success: false,
|
|
58
|
+
message: 'New password and confirmation do not match',
|
|
59
|
+
error_code: 'VALIDATION_ERROR',
|
|
60
|
+
request_id: requestId,
|
|
61
|
+
},
|
|
62
|
+
{ status: 400 }
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Get IDP base URL from environment
|
|
67
|
+
const idpBaseUrl = process.env.IDP_URL;
|
|
68
|
+
|
|
69
|
+
if (!idpBaseUrl) {
|
|
70
|
+
console.error('[CHANGE_PASSWORD] IDP_URL not configured');
|
|
71
|
+
return NextResponse.json(
|
|
72
|
+
{
|
|
73
|
+
success: false,
|
|
74
|
+
message: 'Service configuration error',
|
|
75
|
+
error_code: 'CONFIGURATION_ERROR',
|
|
76
|
+
request_id: requestId,
|
|
77
|
+
},
|
|
78
|
+
{ status: 500 }
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Proxy request to IDP
|
|
83
|
+
const idpUrl = `${idpBaseUrl}/api/Account/change-password`;
|
|
84
|
+
const idpResponse = await fetch(idpUrl, {
|
|
85
|
+
method: 'POST',
|
|
86
|
+
headers: {
|
|
87
|
+
'Content-Type': 'application/json',
|
|
88
|
+
'Authorization': `Bearer ${sessionData.idpAccessToken}`,
|
|
89
|
+
'x-request-id': requestId,
|
|
90
|
+
},
|
|
91
|
+
body: JSON.stringify({
|
|
92
|
+
current_password,
|
|
93
|
+
new_password,
|
|
94
|
+
confirm_password,
|
|
95
|
+
}),
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
const responseData = await idpResponse.json().catch(() => ({}));
|
|
99
|
+
|
|
100
|
+
if (!idpResponse.ok) {
|
|
101
|
+
// Extract error message from IDP response
|
|
102
|
+
let errorMessage = 'Failed to change password';
|
|
103
|
+
|
|
104
|
+
if (responseData.message) {
|
|
105
|
+
errorMessage = responseData.message;
|
|
106
|
+
} else if (responseData.details?.value && Array.isArray(responseData.details.value) && responseData.details.value.length > 0) {
|
|
107
|
+
errorMessage = responseData.details.value[0].message || errorMessage;
|
|
108
|
+
} else if (responseData.details?.message) {
|
|
109
|
+
errorMessage = responseData.details.message;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return NextResponse.json(
|
|
113
|
+
{
|
|
114
|
+
success: false,
|
|
115
|
+
message: errorMessage,
|
|
116
|
+
error_code: responseData.error_code || 'CHANGE_PASSWORD_FAILED',
|
|
117
|
+
request_id: requestId,
|
|
118
|
+
details: responseData.details,
|
|
119
|
+
},
|
|
120
|
+
{ status: idpResponse.status }
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return NextResponse.json({
|
|
125
|
+
success: true,
|
|
126
|
+
message: responseData.message || 'Password changed successfully',
|
|
127
|
+
request_id: requestId,
|
|
128
|
+
});
|
|
129
|
+
} catch (error) {
|
|
130
|
+
console.error('[CHANGE_PASSWORD] Error:', error);
|
|
131
|
+
const requestId = req.headers.get('x-request-id') ?? crypto.randomUUID();
|
|
132
|
+
return NextResponse.json(
|
|
133
|
+
{
|
|
134
|
+
success: false,
|
|
135
|
+
message: error instanceof Error ? error.message : 'Failed to change password',
|
|
136
|
+
error_code: 'INTERNAL_ERROR',
|
|
137
|
+
request_id: requestId,
|
|
138
|
+
},
|
|
139
|
+
{ status: 500 }
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
@@ -1,79 +1,79 @@
|
|
|
1
|
-
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
-
import { idpFetchJSON } from '../../lib/idp-fetch';
|
|
3
|
-
import { ENV_CONFIG } from '../../config/env';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Update Phone Number API Handler
|
|
7
|
-
*
|
|
8
|
-
* PATCH /api/account/update-phone - Update user's phone number
|
|
9
|
-
* Used for 2FA setup - users need to add a phone to enable SMS verification.
|
|
10
|
-
*/
|
|
11
|
-
export async function POST(req: NextRequest) {
|
|
12
|
-
try {
|
|
13
|
-
// Parse request body
|
|
14
|
-
let body: { phoneNumber?: string };
|
|
15
|
-
try {
|
|
16
|
-
body = await req.json();
|
|
17
|
-
} catch {
|
|
18
|
-
return NextResponse.json(
|
|
19
|
-
{ success: false, error: { code: 'INVALID_JSON', message: 'Invalid request body' } },
|
|
20
|
-
{ status: 400 }
|
|
21
|
-
);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const { phoneNumber } = body;
|
|
25
|
-
|
|
26
|
-
if (!phoneNumber) {
|
|
27
|
-
return NextResponse.json(
|
|
28
|
-
{ success: false, error: { code: 'INVALID_INPUT', message: 'Phone number is required' } },
|
|
29
|
-
{ status: 400 }
|
|
30
|
-
);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// PATCH profile with phone_number only
|
|
34
|
-
const url = `${ENV_CONFIG.IDP_URL}/api/Account/profile`;
|
|
35
|
-
const result = await idpFetchJSON(req, url, {
|
|
36
|
-
method: 'PATCH',
|
|
37
|
-
headers: { 'Content-Type': 'application/json' },
|
|
38
|
-
body: JSON.stringify({ phone_number: phoneNumber }),
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
if (!result.ok) {
|
|
42
|
-
console.error('[UPDATE_PHONE] IDP error:', result.status, result.json);
|
|
43
|
-
return NextResponse.json(
|
|
44
|
-
{
|
|
45
|
-
success: false,
|
|
46
|
-
error: {
|
|
47
|
-
code: (result.json as any)?.error?.code || 'UPDATE_FAILED',
|
|
48
|
-
message: (result.json as any)?.error?.message || 'Failed to update phone number',
|
|
49
|
-
},
|
|
50
|
-
meta: { attemptedRefresh: result.attemptedRefresh },
|
|
51
|
-
},
|
|
52
|
-
{ status: result.status }
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const responseData = result.json as any;
|
|
57
|
-
|
|
58
|
-
// Unwrap if IDP returns envelope { success, data }
|
|
59
|
-
if (responseData && typeof responseData === 'object' && 'success' in responseData && 'data' in responseData) {
|
|
60
|
-
return NextResponse.json({
|
|
61
|
-
success: true,
|
|
62
|
-
message: 'Phone number updated successfully',
|
|
63
|
-
data: responseData.data,
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
return NextResponse.json({
|
|
68
|
-
success: true,
|
|
69
|
-
message: 'Phone number updated successfully',
|
|
70
|
-
data: responseData,
|
|
71
|
-
});
|
|
72
|
-
} catch (error) {
|
|
73
|
-
console.error('[UPDATE_PHONE] Error:', error);
|
|
74
|
-
return NextResponse.json(
|
|
75
|
-
{ success: false, error: { code: 'INTERNAL_ERROR', message: 'Failed to update phone number' } },
|
|
76
|
-
{ status: 500 }
|
|
77
|
-
);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { idpFetchJSON } from '../../lib/idp-fetch';
|
|
3
|
+
import { ENV_CONFIG } from '../../config/env';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Update Phone Number API Handler
|
|
7
|
+
*
|
|
8
|
+
* PATCH /api/account/update-phone - Update user's phone number
|
|
9
|
+
* Used for 2FA setup - users need to add a phone to enable SMS verification.
|
|
10
|
+
*/
|
|
11
|
+
export async function POST(req: NextRequest) {
|
|
12
|
+
try {
|
|
13
|
+
// Parse request body
|
|
14
|
+
let body: { phoneNumber?: string };
|
|
15
|
+
try {
|
|
16
|
+
body = await req.json();
|
|
17
|
+
} catch {
|
|
18
|
+
return NextResponse.json(
|
|
19
|
+
{ success: false, error: { code: 'INVALID_JSON', message: 'Invalid request body' } },
|
|
20
|
+
{ status: 400 }
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const { phoneNumber } = body;
|
|
25
|
+
|
|
26
|
+
if (!phoneNumber) {
|
|
27
|
+
return NextResponse.json(
|
|
28
|
+
{ success: false, error: { code: 'INVALID_INPUT', message: 'Phone number is required' } },
|
|
29
|
+
{ status: 400 }
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// PATCH profile with phone_number only
|
|
34
|
+
const url = `${ENV_CONFIG.IDP_URL}/api/Account/profile`;
|
|
35
|
+
const result = await idpFetchJSON(req, url, {
|
|
36
|
+
method: 'PATCH',
|
|
37
|
+
headers: { 'Content-Type': 'application/json' },
|
|
38
|
+
body: JSON.stringify({ phone_number: phoneNumber }),
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
if (!result.ok) {
|
|
42
|
+
console.error('[UPDATE_PHONE] IDP error:', result.status, result.json);
|
|
43
|
+
return NextResponse.json(
|
|
44
|
+
{
|
|
45
|
+
success: false,
|
|
46
|
+
error: {
|
|
47
|
+
code: (result.json as any)?.error?.code || 'UPDATE_FAILED',
|
|
48
|
+
message: (result.json as any)?.error?.message || 'Failed to update phone number',
|
|
49
|
+
},
|
|
50
|
+
meta: { attemptedRefresh: result.attemptedRefresh },
|
|
51
|
+
},
|
|
52
|
+
{ status: result.status }
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const responseData = result.json as any;
|
|
57
|
+
|
|
58
|
+
// Unwrap if IDP returns envelope { success, data }
|
|
59
|
+
if (responseData && typeof responseData === 'object' && 'success' in responseData && 'data' in responseData) {
|
|
60
|
+
return NextResponse.json({
|
|
61
|
+
success: true,
|
|
62
|
+
message: 'Phone number updated successfully',
|
|
63
|
+
data: responseData.data,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return NextResponse.json({
|
|
68
|
+
success: true,
|
|
69
|
+
message: 'Phone number updated successfully',
|
|
70
|
+
data: responseData,
|
|
71
|
+
});
|
|
72
|
+
} catch (error) {
|
|
73
|
+
console.error('[UPDATE_PHONE] Error:', error);
|
|
74
|
+
return NextResponse.json(
|
|
75
|
+
{ success: false, error: { code: 'INTERNAL_ERROR', message: 'Failed to update phone number' } },
|
|
76
|
+
{ status: 500 }
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
}
|