@payez/next-mvp 4.5.7 → 4.7.10

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 (68) hide show
  1. package/dist/api-handlers/auth/credential-login-email-code.d.ts +24 -0
  2. package/dist/api-handlers/auth/credential-login-email-code.js +83 -0
  3. package/dist/api-handlers/auth/credential-login.d.ts +41 -0
  4. package/dist/api-handlers/auth/credential-login.js +858 -0
  5. package/dist/api-handlers/oauth/resume.d.ts +16 -0
  6. package/dist/api-handlers/oauth/resume.js +207 -0
  7. package/dist/auth/better-auth.d.ts +14 -2
  8. package/dist/auth/better-auth.js +59 -34
  9. package/dist/auth/idp-oauth.d.ts +106 -0
  10. package/dist/auth/idp-oauth.js +280 -0
  11. package/dist/auth/safe-callback.js +10 -2
  12. package/dist/client/AuthContext.d.ts +2 -0
  13. package/dist/client/AuthContext.js +14 -1
  14. package/dist/components/auth/CredentialSignInForm.d.ts +33 -0
  15. package/dist/components/auth/CredentialSignInForm.js +134 -0
  16. package/dist/components/auth/ModeAwareLoginPage.js +4 -1
  17. package/dist/components/auth/ModeAwareSignupPage.js +7 -22
  18. package/dist/lib/authorize-resume.d.ts +87 -0
  19. package/dist/lib/authorize-resume.js +190 -0
  20. package/dist/lib/bearer-fetch.d.ts +17 -0
  21. package/dist/lib/bearer-fetch.js +32 -0
  22. package/dist/lib/code-step-copy.d.ts +53 -0
  23. package/dist/lib/code-step-copy.js +73 -0
  24. package/dist/lib/credential-sign-in-client.d.ts +59 -0
  25. package/dist/lib/credential-sign-in-client.js +89 -0
  26. package/dist/lib/device-id.d.ts +37 -0
  27. package/dist/lib/device-id.js +70 -0
  28. package/dist/lib/lock-window.d.ts +29 -0
  29. package/dist/lib/lock-window.js +53 -0
  30. package/dist/lib/login-error-message.d.ts +18 -0
  31. package/dist/lib/login-error-message.js +47 -0
  32. package/dist/lib/twofa-rejection.d.ts +20 -0
  33. package/dist/lib/twofa-rejection.js +73 -0
  34. package/dist/middleware/create-middleware.d.ts +12 -1
  35. package/dist/middleware/create-middleware.js +19 -5
  36. package/dist/pages/login/page.js +11 -37
  37. package/dist/routes/auth/credential-login-email-code.d.ts +6 -0
  38. package/dist/routes/auth/credential-login-email-code.js +10 -0
  39. package/dist/routes/auth/credential-login.d.ts +6 -0
  40. package/dist/routes/auth/credential-login.js +10 -0
  41. package/dist/routes/oauth/resume.d.ts +11 -0
  42. package/dist/routes/oauth/resume.js +16 -0
  43. package/dist/types/auth.d.ts +6 -0
  44. package/package.json +1102 -1047
  45. package/src/api-handlers/auth/credential-login-email-code.ts +92 -0
  46. package/src/api-handlers/auth/credential-login.ts +948 -0
  47. package/src/api-handlers/oauth/resume.ts +182 -0
  48. package/src/auth/better-auth.ts +81 -5
  49. package/src/auth/idp-oauth.ts +310 -0
  50. package/src/auth/safe-callback.ts +11 -2
  51. package/src/client/AuthContext.tsx +14 -1
  52. package/src/components/auth/CredentialSignInForm.tsx +240 -0
  53. package/src/components/auth/ModeAwareLoginPage.tsx +21 -1
  54. package/src/components/auth/ModeAwareSignupPage.tsx +38 -108
  55. package/src/lib/authorize-resume.ts +187 -0
  56. package/src/lib/bearer-fetch.ts +31 -0
  57. package/src/lib/code-step-copy.ts +83 -0
  58. package/src/lib/credential-sign-in-client.ts +101 -0
  59. package/src/lib/device-id.ts +67 -0
  60. package/src/lib/lock-window.ts +49 -0
  61. package/src/lib/login-error-message.ts +41 -0
  62. package/src/lib/twofa-rejection.ts +91 -0
  63. package/src/middleware/create-middleware.ts +35 -4
  64. package/src/pages/login/page.tsx +20 -34
  65. package/src/routes/auth/credential-login-email-code.ts +6 -0
  66. package/src/routes/auth/credential-login.ts +6 -0
  67. package/src/routes/oauth/resume.ts +11 -0
  68. package/src/types/auth.ts +6 -0
@@ -0,0 +1,24 @@
1
+ import { NextRequest, NextResponse } from 'next/server';
2
+ /**
3
+ * POST /api/auth/admin-login/email-code - PAY-2284: "email me a code instead".
4
+ *
5
+ * WHY THIS EXISTS. The sign-in door sends NO code at all to a user who holds an authenticator ("TOTP wins when present: there is
6
+ * nothing to send"). When that authenticator's secret cannot be decrypted on our side (dev-93, encryption key version 57 gone), the
7
+ * verify step answers "a problem on our side ... use another sign-in method" - and there was no other method on the screen and no
8
+ * code in the user's mailbox, because none had been requested. This route is the missing action: it asks the IdP to EMAIL a code,
9
+ * using the provisional bearer the door already parked in the httpOnly `adm2fa` cookie. The verify turn already tries
10
+ * twofa/email/verify after twofa/totp/verify, so the code is accepted in the same box.
11
+ *
12
+ * What it takes from the browser: NOTHING. No body, no client id, no address - the code goes to the account the provisional bearer
13
+ * belongs to (the IdP decides the recipient). What it answers: { success, codeRequested, sentVia:'email', devVerificationCode? }.
14
+ * `codeRequested` means "we asked", never "a mail arrived" (twofa/email/send returns 200 even when the SMTP send threw, 267561).
15
+ */
16
+ export declare function POST(req: NextRequest): Promise<NextResponse<{
17
+ success: boolean;
18
+ error: string;
19
+ }> | NextResponse<{
20
+ devVerificationCode?: string | undefined;
21
+ success: boolean;
22
+ codeRequested: boolean;
23
+ sentVia: string;
24
+ }>>;
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.POST = POST;
4
+ const server_1 = require("next/server");
5
+ const idp_client_config_1 = require("../../lib/idp-client-config");
6
+ const client_key_1 = require("../../lib/client-key");
7
+ const bearer_fetch_1 = require("../../lib/bearer-fetch");
8
+ // `export const runtime = 'nodejs'` must be written LITERALLY in the app's route file (Next ignores a re-exported segment config).
9
+ /** One email-code request per this many seconds per browser (the IdP rate-limits too; this stops a double click and a loop). */
10
+ const EMAIL_CODE_COOLDOWN_S = 30;
11
+ /**
12
+ * POST /api/auth/admin-login/email-code - PAY-2284: "email me a code instead".
13
+ *
14
+ * WHY THIS EXISTS. The sign-in door sends NO code at all to a user who holds an authenticator ("TOTP wins when present: there is
15
+ * nothing to send"). When that authenticator's secret cannot be decrypted on our side (dev-93, encryption key version 57 gone), the
16
+ * verify step answers "a problem on our side ... use another sign-in method" - and there was no other method on the screen and no
17
+ * code in the user's mailbox, because none had been requested. This route is the missing action: it asks the IdP to EMAIL a code,
18
+ * using the provisional bearer the door already parked in the httpOnly `adm2fa` cookie. The verify turn already tries
19
+ * twofa/email/verify after twofa/totp/verify, so the code is accepted in the same box.
20
+ *
21
+ * What it takes from the browser: NOTHING. No body, no client id, no address - the code goes to the account the provisional bearer
22
+ * belongs to (the IdP decides the recipient). What it answers: { success, codeRequested, sentVia:'email', devVerificationCode? }.
23
+ * `codeRequested` means "we asked", never "a mail arrived" (twofa/email/send returns 200 even when the SMTP send threw, 267561).
24
+ */
25
+ async function POST(req) {
26
+ const provisional = req.cookies.get('adm2fa')?.value;
27
+ if (!provisional) {
28
+ return server_1.NextResponse.json({ success: false, error: 'Your verification window expired. Sign in again.' }, { status: 401 });
29
+ }
30
+ const last = Number(req.cookies.get('adm2fa_emailreq')?.value);
31
+ const now = Math.floor(Date.now() / 1000);
32
+ if (Number.isFinite(last) && last > 0 && now - last < EMAIL_CODE_COOLDOWN_S) {
33
+ const wait = EMAIL_CODE_COOLDOWN_S - (now - last);
34
+ return server_1.NextResponse.json({ success: false, error: `We just asked for a code. Give it a moment (about ${wait} seconds) before asking again.`, retryAfter: wait }, { status: 429 });
35
+ }
36
+ let clientKey = '';
37
+ try {
38
+ const config = await (0, idp_client_config_1.getIDPClientConfig)();
39
+ clientKey = (0, client_key_1.resolveClientKey)(config, 'graft/admin-login/email-code').key ?? '';
40
+ }
41
+ catch (err) {
42
+ console.error('[ADMIN_LOGIN_EMAIL_CODE] IDP client config unavailable:', err instanceof Error ? err.message : String(err));
43
+ return server_1.NextResponse.json({ success: false, error: 'Sign-in is temporarily unavailable. This is a problem on our side. Please try again shortly.' }, { status: 503 });
44
+ }
45
+ let sendOk = false;
46
+ let devCode;
47
+ try {
48
+ const sendResp = await (0, bearer_fetch_1.bearerFetch)(`${process.env.IDP_URL}/api/ExternalAuth/twofa/email/send`, {
49
+ method: 'POST',
50
+ headers: {
51
+ 'Content-Type': 'application/json',
52
+ Accept: 'application/json',
53
+ Authorization: `Bearer ${provisional}`,
54
+ 'X-Client-Id': clientKey,
55
+ },
56
+ body: JSON.stringify({ client_id: clientKey }),
57
+ });
58
+ sendOk = sendResp.ok;
59
+ const text = await sendResp.text().catch(() => '');
60
+ if (!sendOk)
61
+ console.error('[ADMIN_LOGIN_EMAIL_CODE] twofa/email/send failed', sendResp.status, text.slice(0, 200));
62
+ try {
63
+ const j = JSON.parse(text);
64
+ const echoed = j?.dev_verification_code ?? j?.data?.dev_verification_code ?? j?.data?.result?.dev_verification_code ?? j?.result?.dev_verification_code;
65
+ if (typeof echoed === 'string' && echoed)
66
+ devCode = echoed; // present only when the server chose to echo (dev rigs); absence is the contract
67
+ }
68
+ catch {
69
+ /* not JSON - nothing to echo */
70
+ }
71
+ }
72
+ catch (err) {
73
+ console.error('[ADMIN_LOGIN_EMAIL_CODE] twofa/email/send fetch failed:', err instanceof Error ? err.message : String(err));
74
+ }
75
+ if (!sendOk) {
76
+ return server_1.NextResponse.json({ success: false, error: 'We could not ask for an email code just now - this is a problem on our side. Try again shortly.' }, { status: 502 });
77
+ }
78
+ const res = server_1.NextResponse.json({ success: true, codeRequested: true, sentVia: 'email', ...(devCode ? { devVerificationCode: devCode } : {}) }, { status: 200 });
79
+ const cookie = { httpOnly: true, secure: process.env.NODE_ENV === 'production', sameSite: 'lax', path: '/', maxAge: 600 };
80
+ res.cookies.set('adm2fa_lane', 'email', cookie); // the verify turn then tries totp -> email, exactly as for any non-SMS user
81
+ res.cookies.set('adm2fa_emailreq', String(now), { ...cookie, maxAge: EMAIL_CODE_COOLDOWN_S });
82
+ return res;
83
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * PAY-2302 (cut 1): THIS HANDLER NOW LIVES IN @payez/next-mvp. It was written as an app-local route in
3
+ * VibeID-Customer-Portal (src/app/api/auth/admin-login/route.ts, card 238225) because the package had no credential sign-in:
4
+ * its Better Auth instance configures socialProviders only (no emailAndPassword) and createLoginHandler (api-handlers/auth/login.ts)
5
+ * proxies the IdP without writing a session. It moved here so a generated app (create-next-app) can sign a user in; the portal
6
+ * re-exports it. Where the history below says "the package", "app-local" or "the graft", read it as written BEFORE the move.
7
+ *
8
+ * Admin password sign-in — card 238225 stage-2.
9
+ *
10
+ * Deliberately NOT a better-auth flow: better-auth's own auth/better-auth.js
11
+ * only configures socialProviders (Google) - zero emailAndPassword - and
12
+ * BAPert's ruling (32776, re 32769) was explicit: keep the shared package
13
+ * untouched, land this as an app-local custom route instead. It talks to the
14
+ * IDP the same way @payez/next-mvp's createLoginHandler does
15
+ * (api-handlers/auth/login.js: POST {IDP_URL}/api/ExternalAuth/login), which
16
+ * is the exact endpoint QAPert's TOTP fixture already proved works (mail
17
+ * 32622). createLoginHandler itself was not reused here - its own doc
18
+ * comment says it normalizes "for NextAuth authorize()", a caller this
19
+ * better-auth-only package no longer has.
20
+ *
21
+ * Two-step, same shape testing/qa/H1-AUTH-MECHANISM-2026-07-31.md documents
22
+ * for the IDP: an access token with NO refresh token means 2FA is
23
+ * outstanding (refresh is deliberately withheld until the second factor
24
+ * clears). Step 1 (email+password) either finishes in one shot or comes back
25
+ * asking for a code; step 2 resubmits email+password+code together - the IDP
26
+ * endpoint accepts two_factor_code directly on the same login call, so this
27
+ * never needs the already-authenticated totp-verify/verify-code endpoints
28
+ * (those add a *second* factor to an existing session; this establishes the
29
+ * first one).
30
+ *
31
+ * Session write: this is the "legacy JWT + Redis" path decode-session.js
32
+ * still actively maintains as a fallback (see its 183047 rework, and
33
+ * session-cookie-shape.js's JWS branch) - not dead NextAuth-era code, just
34
+ * unused by anything else in the package because better-auth mints its own
35
+ * ba-hmac cookies for every OTHER sign-in path. Nothing else in the package
36
+ * currently WRITES this shape; this route is the one place that now does,
37
+ * by design (BAPert 32776: "custom route ... writes session via
38
+ * lib/session-store.js").
39
+ */
40
+ import { NextRequest, NextResponse } from 'next/server';
41
+ export declare function POST(req: NextRequest): Promise<NextResponse<unknown>>;