@payez/next-mvp 4.1.11 → 4.1.12

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 (91) hide show
  1. package/dist/api-handlers/auth/refresh.js +15 -38
  2. package/dist/auth/better-auth.js +12 -33
  3. package/dist/client/better-auth-client.d.ts +14 -2
  4. package/dist/lib/ensure-fresh-access-token.js +8 -42
  5. package/dist/lib/session-store.d.ts +0 -1
  6. package/dist/lib/session-store.js +29 -33
  7. package/dist/lib/startup-init.js +19 -19
  8. package/dist/lib/token-lifecycle.d.ts +0 -2
  9. package/dist/lib/token-lifecycle.js +3 -21
  10. package/package.json +4 -3
  11. package/scripts/check-internal-url-usage.sh +73 -73
  12. package/src/api/index.ts +17 -17
  13. package/src/api-handlers/account/masked-info.ts +45 -45
  14. package/src/api-handlers/account/profile.ts +80 -80
  15. package/src/api-handlers/account/recovery/initiate.ts +23 -23
  16. package/src/api-handlers/account/recovery/send-code.ts +25 -25
  17. package/src/api-handlers/account/recovery/verify-code.ts +25 -25
  18. package/src/api-handlers/account/reset-password.ts +23 -23
  19. package/src/api-handlers/account/send-code.ts +76 -76
  20. package/src/api-handlers/account/validate-password.ts +118 -118
  21. package/src/api-handlers/admin/stats.ts +249 -249
  22. package/src/api-handlers/anon/preferences.ts +123 -123
  23. package/src/api-handlers/auth/jwks.ts +20 -20
  24. package/src/api-handlers/auth/login.ts +240 -240
  25. package/src/api-handlers/auth/refresh.ts +15 -38
  26. package/src/api-handlers/auth/validate.ts +43 -43
  27. package/src/api-handlers/session/viability.ts +142 -142
  28. package/src/auth/better-auth.ts +450 -467
  29. package/src/auth/unauthenticated-routes.ts +2 -2
  30. package/src/client/better-auth-client.ts +70 -70
  31. package/src/client/fetchWithSession.ts +21 -21
  32. package/src/client/index.ts +13 -13
  33. package/src/client/useAnonSession.ts +131 -131
  34. package/src/components/admin/VibeAdminContext.tsx +87 -87
  35. package/src/config/logging-config.ts +82 -82
  36. package/src/config/unauthenticated-routes.ts +19 -19
  37. package/src/edge/internal-api-url.ts +65 -65
  38. package/src/edge/middleware.ts +42 -42
  39. package/src/hooks/useProfile.ts +75 -75
  40. package/src/lib/anon-session.ts +213 -213
  41. package/src/lib/auth-secret.ts +137 -137
  42. package/src/lib/demo-mode.ts +17 -17
  43. package/src/lib/ensure-fresh-access-token.ts +8 -42
  44. package/src/lib/idp-client-config.ts +559 -559
  45. package/src/lib/idp-fetch.ts +126 -126
  46. package/src/lib/internal-api.ts +171 -171
  47. package/src/lib/jwt-decode-client.ts +45 -45
  48. package/src/lib/rate-limit-service.ts +9 -9
  49. package/src/lib/redis.ts +26 -26
  50. package/src/lib/refresh-token-validator.ts +64 -64
  51. package/src/lib/secret-validation.ts +8 -8
  52. package/src/lib/session-store.ts +692 -695
  53. package/src/lib/session.ts +34 -34
  54. package/src/lib/startup-init.ts +251 -251
  55. package/src/lib/token-expiry.ts +40 -40
  56. package/src/lib/token-lifecycle.ts +470 -497
  57. package/src/lib/types/api-responses.ts +336 -336
  58. package/src/middleware/auth-decision.ts +43 -43
  59. package/src/models/DecodedAccessToken.ts +16 -16
  60. package/src/models/SessionModel.ts +264 -264
  61. package/src/pages/admin-roles/index.ts +9 -9
  62. package/src/pages/security/EnhancedSecurityPage.tsx +9 -9
  63. package/src/routes/account/index.ts +31 -31
  64. package/src/routes/account/masked-info.ts +36 -36
  65. package/src/routes/account/send-code.ts +39 -39
  66. package/src/routes/account/verify-email.ts +40 -40
  67. package/src/routes/account/verify-sms.ts +40 -40
  68. package/src/routes/auth/index.ts +22 -22
  69. package/src/routes/auth/refresh.ts +48 -48
  70. package/src/routes/auth/session.ts +166 -166
  71. package/src/routes/index.ts +17 -17
  72. package/src/routes/session/index.ts +7 -7
  73. package/src/routes/session/refresh-viability.ts +17 -17
  74. package/src/server/auth.ts +373 -373
  75. package/src/server/decode-session.ts +202 -202
  76. package/src/services/signalrActivityService.ts +258 -258
  77. package/src/theme/ThemeProvider.tsx +39 -39
  78. package/src/theme/default.ts +33 -33
  79. package/src/theme/index.ts +31 -31
  80. package/src/theme/types.ts +69 -69
  81. package/src/theme/useTheme.ts +57 -57
  82. package/src/theme/utils.ts +40 -40
  83. package/src/types/api.ts +12 -12
  84. package/src/types/auth.d.ts +23 -23
  85. package/src/types/auth.ts +22 -22
  86. package/src/types/logging.ts +11 -11
  87. package/src/types/recovery.ts +54 -54
  88. package/src/utils/api.ts +353 -353
  89. package/src/utils/circuitBreaker.ts +40 -40
  90. package/src/utils/error-message.ts +108 -108
  91. package/src/utils/layout/reservedSpace.ts +124 -124
@@ -1,73 +1,73 @@
1
- #!/bin/bash
2
- #
3
- # CI Check: Detect incorrect internal API URL patterns
4
- #
5
- # This script fails if code is using req.url or req.nextUrl.origin to construct
6
- # URLs for internal API calls. These patterns cause 403 errors in Kubernetes
7
- # because they use the external HTTPS URL instead of INTERNAL_API_URL.
8
- #
9
- # CORRECT: Use internalFetch() or internalRefresh() from @payez/next-mvp/lib/internal-api
10
- # INCORRECT: new URL('/api/...', req.url) or fetch(`${req.nextUrl.origin}/api/...`)
11
- #
12
- # NOTE: new URL(req.url) alone is FINE - it just parses the incoming request URL.
13
- # The problem is when you construct a NEW path using req.url as the base.
14
- #
15
-
16
- set -e
17
-
18
- SEARCH_PATH="${1:-.}"
19
- ERRORS_FOUND=0
20
-
21
- echo "Checking for incorrect internal URL patterns in: $SEARCH_PATH"
22
- echo "========================================================================"
23
-
24
- # Pattern 1: new URL('/api/...', req.url) - constructing internal API URL from request
25
- # This catches: new URL('/api/...', req.url) and new URL("/api/...", req.url)
26
- # But NOT: new URL(req.url) which is just parsing the request
27
- echo ""
28
- echo "Checking for: new URL('/api/...', req.url) pattern"
29
- if grep -rn --include="*.ts" --include="*.tsx" -E "new URL\(['\"][^'\"]*['\"],\s*req\.url" "$SEARCH_PATH" 2>/dev/null | grep -v "node_modules" | grep -v "\.d\.ts"; then
30
- echo "ERROR: Found 'new URL(path, req.url)' pattern. Use internalFetch() instead."
31
- ERRORS_FOUND=1
32
- fi
33
-
34
- # Pattern 2: new URL('/api/...', req.nextUrl) or new URL(..., req.nextUrl.origin)
35
- echo ""
36
- echo "Checking for: new URL(..., req.nextUrl) pattern"
37
- if grep -rn --include="*.ts" --include="*.tsx" -E "new URL\([^)]+,\s*req\.nextUrl" "$SEARCH_PATH" 2>/dev/null | grep -v "node_modules" | grep -v "\.d\.ts"; then
38
- echo "ERROR: Found 'new URL(..., req.nextUrl)' pattern. Use internalFetch() instead."
39
- ERRORS_FOUND=1
40
- fi
41
-
42
- # Pattern 3: fetch with req.nextUrl.origin and /api/
43
- echo ""
44
- echo "Checking for: fetch with req.nextUrl.origin and /api/"
45
- if grep -rn --include="*.ts" --include="*.tsx" "req\.nextUrl\.origin.*\/api\/" "$SEARCH_PATH" 2>/dev/null | grep -v "node_modules" | grep -v "\.d\.ts"; then
46
- echo "ERROR: Found 'req.nextUrl.origin' used with internal API route. Use internalFetch() instead."
47
- ERRORS_FOUND=1
48
- fi
49
-
50
- # Pattern 4: Direct fetch to /api/auth/refresh without using internalRefresh (warning only)
51
- echo ""
52
- echo "Checking for: Direct fetch to /api/auth/refresh (should use internalRefresh)"
53
- if grep -rn --include="*.ts" --include="*.tsx" "fetch.*\/api\/auth\/refresh" "$SEARCH_PATH" 2>/dev/null | grep -v "node_modules" | grep -v "\.d\.ts" | grep -v "internal-api\.ts"; then
54
- echo "WARNING: Found direct fetch to /api/auth/refresh. Consider using internalRefresh() helper."
55
- # Not failing on this one as it might be intentional in some cases (client-side refresh)
56
- fi
57
-
58
- echo ""
59
- echo "========================================================================"
60
-
61
- if [ $ERRORS_FOUND -eq 1 ]; then
62
- echo "FAILED: Dangerous internal URL patterns detected!"
63
- echo ""
64
- echo "Fix: Import and use helpers from @payez/next-mvp/lib/internal-api:"
65
- echo " import { internalFetch, internalRefresh } from '@payez/next-mvp/lib/internal-api';"
66
- echo ""
67
- echo "Why: Server-to-server calls must use INTERNAL_API_URL (http://service.namespace.svc.cluster.local)"
68
- echo " not the external URL from the request (https://api.example.com) which fails in Kubernetes."
69
- exit 1
70
- else
71
- echo "PASSED: No dangerous internal URL patterns found."
72
- exit 0
73
- fi
1
+ #!/bin/bash
2
+ #
3
+ # CI Check: Detect incorrect internal API URL patterns
4
+ #
5
+ # This script fails if code is using req.url or req.nextUrl.origin to construct
6
+ # URLs for internal API calls. These patterns cause 403 errors in Kubernetes
7
+ # because they use the external HTTPS URL instead of INTERNAL_API_URL.
8
+ #
9
+ # CORRECT: Use internalFetch() or internalRefresh() from @payez/next-mvp/lib/internal-api
10
+ # INCORRECT: new URL('/api/...', req.url) or fetch(`${req.nextUrl.origin}/api/...`)
11
+ #
12
+ # NOTE: new URL(req.url) alone is FINE - it just parses the incoming request URL.
13
+ # The problem is when you construct a NEW path using req.url as the base.
14
+ #
15
+
16
+ set -e
17
+
18
+ SEARCH_PATH="${1:-.}"
19
+ ERRORS_FOUND=0
20
+
21
+ echo "Checking for incorrect internal URL patterns in: $SEARCH_PATH"
22
+ echo "========================================================================"
23
+
24
+ # Pattern 1: new URL('/api/...', req.url) - constructing internal API URL from request
25
+ # This catches: new URL('/api/...', req.url) and new URL("/api/...", req.url)
26
+ # But NOT: new URL(req.url) which is just parsing the request
27
+ echo ""
28
+ echo "Checking for: new URL('/api/...', req.url) pattern"
29
+ if grep -rn --include="*.ts" --include="*.tsx" -E "new URL\(['\"][^'\"]*['\"],\s*req\.url" "$SEARCH_PATH" 2>/dev/null | grep -v "node_modules" | grep -v "\.d\.ts"; then
30
+ echo "ERROR: Found 'new URL(path, req.url)' pattern. Use internalFetch() instead."
31
+ ERRORS_FOUND=1
32
+ fi
33
+
34
+ # Pattern 2: new URL('/api/...', req.nextUrl) or new URL(..., req.nextUrl.origin)
35
+ echo ""
36
+ echo "Checking for: new URL(..., req.nextUrl) pattern"
37
+ if grep -rn --include="*.ts" --include="*.tsx" -E "new URL\([^)]+,\s*req\.nextUrl" "$SEARCH_PATH" 2>/dev/null | grep -v "node_modules" | grep -v "\.d\.ts"; then
38
+ echo "ERROR: Found 'new URL(..., req.nextUrl)' pattern. Use internalFetch() instead."
39
+ ERRORS_FOUND=1
40
+ fi
41
+
42
+ # Pattern 3: fetch with req.nextUrl.origin and /api/
43
+ echo ""
44
+ echo "Checking for: fetch with req.nextUrl.origin and /api/"
45
+ if grep -rn --include="*.ts" --include="*.tsx" "req\.nextUrl\.origin.*\/api\/" "$SEARCH_PATH" 2>/dev/null | grep -v "node_modules" | grep -v "\.d\.ts"; then
46
+ echo "ERROR: Found 'req.nextUrl.origin' used with internal API route. Use internalFetch() instead."
47
+ ERRORS_FOUND=1
48
+ fi
49
+
50
+ # Pattern 4: Direct fetch to /api/auth/refresh without using internalRefresh (warning only)
51
+ echo ""
52
+ echo "Checking for: Direct fetch to /api/auth/refresh (should use internalRefresh)"
53
+ if grep -rn --include="*.ts" --include="*.tsx" "fetch.*\/api\/auth\/refresh" "$SEARCH_PATH" 2>/dev/null | grep -v "node_modules" | grep -v "\.d\.ts" | grep -v "internal-api\.ts"; then
54
+ echo "WARNING: Found direct fetch to /api/auth/refresh. Consider using internalRefresh() helper."
55
+ # Not failing on this one as it might be intentional in some cases (client-side refresh)
56
+ fi
57
+
58
+ echo ""
59
+ echo "========================================================================"
60
+
61
+ if [ $ERRORS_FOUND -eq 1 ]; then
62
+ echo "FAILED: Dangerous internal URL patterns detected!"
63
+ echo ""
64
+ echo "Fix: Import and use helpers from @payez/next-mvp/lib/internal-api:"
65
+ echo " import { internalFetch, internalRefresh } from '@payez/next-mvp/lib/internal-api';"
66
+ echo ""
67
+ echo "Why: Server-to-server calls must use INTERNAL_API_URL (http://service.namespace.svc.cluster.local)"
68
+ echo " not the external URL from the request (https://api.example.com) which fails in Kubernetes."
69
+ exit 1
70
+ else
71
+ echo "PASSED: No dangerous internal URL patterns found."
72
+ exit 0
73
+ fi
package/src/api/index.ts CHANGED
@@ -1,18 +1,18 @@
1
- /**
2
- * @payez/next-mvp API Module Exports
3
- *
4
- * Provides enhanced API route handlers with automatic token management
5
- *
6
- * @version 2.0.0
7
- * @since auth-ready-v2
8
- */
9
-
10
- export {
11
- createAuthHandler,
12
- type AuthContext,
13
- type AuthHandlerOptions,
14
- type HandlerFunction
15
- } from './auth-handler';
16
-
17
- // Default export for convenience
1
+ /**
2
+ * @payez/next-mvp API Module Exports
3
+ *
4
+ * Provides enhanced API route handlers with automatic token management
5
+ *
6
+ * @version 2.0.0
7
+ * @since auth-ready-v2
8
+ */
9
+
10
+ export {
11
+ createAuthHandler,
12
+ type AuthContext,
13
+ type AuthHandlerOptions,
14
+ type HandlerFunction
15
+ } from './auth-handler';
16
+
17
+ // Default export for convenience
18
18
  export { default } from './auth-handler';
@@ -1,45 +1,45 @@
1
- import { NextRequest, NextResponse } from 'next/server';
2
- import { idpFetchJSON } from '../../lib/idp-fetch';
3
- import { ENV_CONFIG } from '../../config/env';
4
-
5
- // IDP masked-info is POST and uses capital 'A' in /api/Account
6
- export async function POST(req: NextRequest) {
7
- const url = `${ENV_CONFIG.IDP_URL}/api/Account/masked-info`;
8
-
9
- // Forward request body if present; IDP often accepts empty object
10
- let body = '{}';
11
- try {
12
- const raw = await req.text();
13
- if (raw && raw.trim().length > 0) body = raw;
14
- } catch {}
15
-
16
- const result = await idpFetchJSON(req, url, {
17
- method: 'POST',
18
- headers: { 'Content-Type': 'application/json' },
19
- body
20
- });
21
-
22
- if (!result.ok) {
23
- return NextResponse.json(
24
- {
25
- success: false,
26
- message: 'Upstream error',
27
- error: { code: 'UPSTREAM_SERVICE_ERROR', status: result.status, details: result.json },
28
- meta: { attemptedRefresh: result.attemptedRefresh },
29
- },
30
- { status: result.status }
31
- );
32
- }
33
-
34
- const bodyJson: any = result.json;
35
- // Unwrap if IDP returns envelope { success, data }
36
- if (bodyJson && typeof bodyJson === 'object' && 'success' in bodyJson && 'data' in bodyJson) {
37
- if ((bodyJson as any).success === true) {
38
- return NextResponse.json((bodyJson as any).data, { status: 200 });
39
- }
40
- return NextResponse.json(bodyJson, { status: 200 });
41
- }
42
-
43
- // Passthrough otherwise
44
- return NextResponse.json(bodyJson ?? {}, { status: 200 });
45
- }
1
+ import { NextRequest, NextResponse } from 'next/server';
2
+ import { idpFetchJSON } from '../../lib/idp-fetch';
3
+ import { ENV_CONFIG } from '../../config/env';
4
+
5
+ // IDP masked-info is POST and uses capital 'A' in /api/Account
6
+ export async function POST(req: NextRequest) {
7
+ const url = `${ENV_CONFIG.IDP_URL}/api/Account/masked-info`;
8
+
9
+ // Forward request body if present; IDP often accepts empty object
10
+ let body = '{}';
11
+ try {
12
+ const raw = await req.text();
13
+ if (raw && raw.trim().length > 0) body = raw;
14
+ } catch {}
15
+
16
+ const result = await idpFetchJSON(req, url, {
17
+ method: 'POST',
18
+ headers: { 'Content-Type': 'application/json' },
19
+ body
20
+ });
21
+
22
+ if (!result.ok) {
23
+ return NextResponse.json(
24
+ {
25
+ success: false,
26
+ message: 'Upstream error',
27
+ error: { code: 'UPSTREAM_SERVICE_ERROR', status: result.status, details: result.json },
28
+ meta: { attemptedRefresh: result.attemptedRefresh },
29
+ },
30
+ { status: result.status }
31
+ );
32
+ }
33
+
34
+ const bodyJson: any = result.json;
35
+ // Unwrap if IDP returns envelope { success, data }
36
+ if (bodyJson && typeof bodyJson === 'object' && 'success' in bodyJson && 'data' in bodyJson) {
37
+ if ((bodyJson as any).success === true) {
38
+ return NextResponse.json((bodyJson as any).data, { status: 200 });
39
+ }
40
+ return NextResponse.json(bodyJson, { status: 200 });
41
+ }
42
+
43
+ // Passthrough otherwise
44
+ return NextResponse.json(bodyJson ?? {}, { status: 200 });
45
+ }
@@ -1,80 +1,80 @@
1
- import { NextRequest, NextResponse } from 'next/server';
2
-
3
- /**
4
- * Account Profile API Handler
5
- * Simple proxy to IDP profile endpoint
6
- *
7
- * GET /api/account/profile - Get user profile
8
- * PUT /api/account/profile - Update user profile
9
- */
10
-
11
- function getIdpUrl(): string {
12
- const url = process.env.IDP_URL;
13
- if (!url) {
14
- throw new Error('[IDP_URL] FATAL: IDP_URL environment variable is REQUIRED.');
15
- }
16
- return url;
17
- }
18
-
19
- export async function GET(req: NextRequest) {
20
- const IDP_URL = getIdpUrl();
21
- const authHeader = req.headers.get('authorization');
22
-
23
- if (!authHeader) {
24
- return NextResponse.json(
25
- { success: false, error: { code: 'UNAUTHORIZED', message: 'Authentication required' } },
26
- { status: 401 }
27
- );
28
- }
29
-
30
- try {
31
- const upstream = await fetch(`${IDP_URL}/api/Account/profile`, {
32
- method: 'GET',
33
- headers: {
34
- 'Content-Type': 'application/json',
35
- 'Authorization': authHeader,
36
- },
37
- });
38
-
39
- const data = await upstream.json().catch(() => ({}));
40
- return NextResponse.json(data, { status: upstream.status });
41
- } catch (error) {
42
- return NextResponse.json(
43
- { success: false, error: { code: 'UPSTREAM_ERROR', message: 'Failed to fetch profile' } },
44
- { status: 500 }
45
- );
46
- }
47
- }
48
-
49
- export async function PUT(req: NextRequest) {
50
- const IDP_URL = getIdpUrl();
51
- const authHeader = req.headers.get('authorization');
52
-
53
- if (!authHeader) {
54
- return NextResponse.json(
55
- { success: false, error: { code: 'UNAUTHORIZED', message: 'Authentication required' } },
56
- { status: 401 }
57
- );
58
- }
59
-
60
- try {
61
- const body = await req.text();
62
-
63
- const upstream = await fetch(`${IDP_URL}/api/Account/profile`, {
64
- method: 'PUT',
65
- headers: {
66
- 'Content-Type': 'application/json',
67
- 'Authorization': authHeader,
68
- },
69
- body,
70
- });
71
-
72
- const data = await upstream.json().catch(() => ({}));
73
- return NextResponse.json(data, { status: upstream.status });
74
- } catch (error) {
75
- return NextResponse.json(
76
- { success: false, error: { code: 'UPSTREAM_ERROR', message: 'Failed to update profile' } },
77
- { status: 500 }
78
- );
79
- }
80
- }
1
+ import { NextRequest, NextResponse } from 'next/server';
2
+
3
+ /**
4
+ * Account Profile API Handler
5
+ * Simple proxy to IDP profile endpoint
6
+ *
7
+ * GET /api/account/profile - Get user profile
8
+ * PUT /api/account/profile - Update user profile
9
+ */
10
+
11
+ function getIdpUrl(): string {
12
+ const url = process.env.IDP_URL;
13
+ if (!url) {
14
+ throw new Error('[IDP_URL] FATAL: IDP_URL environment variable is REQUIRED.');
15
+ }
16
+ return url;
17
+ }
18
+
19
+ export async function GET(req: NextRequest) {
20
+ const IDP_URL = getIdpUrl();
21
+ const authHeader = req.headers.get('authorization');
22
+
23
+ if (!authHeader) {
24
+ return NextResponse.json(
25
+ { success: false, error: { code: 'UNAUTHORIZED', message: 'Authentication required' } },
26
+ { status: 401 }
27
+ );
28
+ }
29
+
30
+ try {
31
+ const upstream = await fetch(`${IDP_URL}/api/Account/profile`, {
32
+ method: 'GET',
33
+ headers: {
34
+ 'Content-Type': 'application/json',
35
+ 'Authorization': authHeader,
36
+ },
37
+ });
38
+
39
+ const data = await upstream.json().catch(() => ({}));
40
+ return NextResponse.json(data, { status: upstream.status });
41
+ } catch (error) {
42
+ return NextResponse.json(
43
+ { success: false, error: { code: 'UPSTREAM_ERROR', message: 'Failed to fetch profile' } },
44
+ { status: 500 }
45
+ );
46
+ }
47
+ }
48
+
49
+ export async function PUT(req: NextRequest) {
50
+ const IDP_URL = getIdpUrl();
51
+ const authHeader = req.headers.get('authorization');
52
+
53
+ if (!authHeader) {
54
+ return NextResponse.json(
55
+ { success: false, error: { code: 'UNAUTHORIZED', message: 'Authentication required' } },
56
+ { status: 401 }
57
+ );
58
+ }
59
+
60
+ try {
61
+ const body = await req.text();
62
+
63
+ const upstream = await fetch(`${IDP_URL}/api/Account/profile`, {
64
+ method: 'PUT',
65
+ headers: {
66
+ 'Content-Type': 'application/json',
67
+ 'Authorization': authHeader,
68
+ },
69
+ body,
70
+ });
71
+
72
+ const data = await upstream.json().catch(() => ({}));
73
+ return NextResponse.json(data, { status: upstream.status });
74
+ } catch (error) {
75
+ return NextResponse.json(
76
+ { success: false, error: { code: 'UPSTREAM_ERROR', message: 'Failed to update profile' } },
77
+ { status: 500 }
78
+ );
79
+ }
80
+ }
@@ -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 { email } = await req.json();
13
- const upstream = await fetch(`${IDP_URL}/api/Account/recovery/initiate`, {
14
- method: 'POST',
15
- headers: { 'Content-Type': 'application/json' },
16
- body: JSON.stringify({ email }),
17
- });
18
- const data = await upstream.json().catch(() => ({}));
19
- return NextResponse.json(data, { status: upstream.status });
20
- } catch (e) {
21
- return NextResponse.json({ success: false, error: 'initiate_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 { email } = await req.json();
13
+ const upstream = await fetch(`${IDP_URL}/api/Account/recovery/initiate`, {
14
+ method: 'POST',
15
+ headers: { 'Content-Type': 'application/json' },
16
+ body: JSON.stringify({ email }),
17
+ });
18
+ const data = await upstream.json().catch(() => ({}));
19
+ return NextResponse.json(data, { status: upstream.status });
20
+ } catch (e) {
21
+ return NextResponse.json({ success: false, error: 'initiate_failed' }, { status: 500 });
22
+ }
23
+ }
@@ -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
+ }