@payez/next-mvp 4.5.7 → 4.7.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/dist/api-handlers/auth/credential-login-email-code.d.ts +24 -0
- package/dist/api-handlers/auth/credential-login-email-code.js +83 -0
- package/dist/api-handlers/auth/credential-login.d.ts +41 -0
- package/dist/api-handlers/auth/credential-login.js +858 -0
- package/dist/api-handlers/oauth/resume.d.ts +16 -0
- package/dist/api-handlers/oauth/resume.js +207 -0
- package/dist/auth/better-auth.d.ts +14 -2
- package/dist/auth/better-auth.js +59 -34
- package/dist/auth/idp-oauth.d.ts +106 -0
- package/dist/auth/idp-oauth.js +280 -0
- package/dist/auth/safe-callback.js +10 -2
- package/dist/components/auth/CredentialSignInForm.d.ts +33 -0
- package/dist/components/auth/CredentialSignInForm.js +134 -0
- package/dist/lib/authorize-resume.d.ts +87 -0
- package/dist/lib/authorize-resume.js +190 -0
- package/dist/lib/bearer-fetch.d.ts +17 -0
- package/dist/lib/bearer-fetch.js +32 -0
- package/dist/lib/code-step-copy.d.ts +53 -0
- package/dist/lib/code-step-copy.js +73 -0
- package/dist/lib/credential-sign-in-client.d.ts +59 -0
- package/dist/lib/credential-sign-in-client.js +89 -0
- package/dist/lib/device-id.d.ts +37 -0
- package/dist/lib/device-id.js +70 -0
- package/dist/lib/lock-window.d.ts +29 -0
- package/dist/lib/lock-window.js +53 -0
- package/dist/lib/login-error-message.d.ts +18 -0
- package/dist/lib/login-error-message.js +47 -0
- package/dist/lib/twofa-rejection.d.ts +20 -0
- package/dist/lib/twofa-rejection.js +73 -0
- package/dist/middleware/create-middleware.d.ts +12 -1
- package/dist/middleware/create-middleware.js +19 -5
- package/dist/pages/login/page.js +11 -37
- package/dist/routes/auth/credential-login-email-code.d.ts +6 -0
- package/dist/routes/auth/credential-login-email-code.js +10 -0
- package/dist/routes/auth/credential-login.d.ts +6 -0
- package/dist/routes/auth/credential-login.js +10 -0
- package/dist/routes/oauth/resume.d.ts +11 -0
- package/dist/routes/oauth/resume.js +16 -0
- package/package.json +1102 -1047
- package/src/api-handlers/auth/credential-login-email-code.ts +92 -0
- package/src/api-handlers/auth/credential-login.ts +948 -0
- package/src/api-handlers/oauth/resume.ts +182 -0
- package/src/auth/better-auth.ts +81 -5
- package/src/auth/idp-oauth.ts +310 -0
- package/src/auth/safe-callback.ts +11 -2
- package/src/components/auth/CredentialSignInForm.tsx +240 -0
- package/src/lib/authorize-resume.ts +187 -0
- package/src/lib/bearer-fetch.ts +31 -0
- package/src/lib/code-step-copy.ts +83 -0
- package/src/lib/credential-sign-in-client.ts +101 -0
- package/src/lib/device-id.ts +67 -0
- package/src/lib/lock-window.ts +49 -0
- package/src/lib/login-error-message.ts +41 -0
- package/src/lib/twofa-rejection.ts +91 -0
- package/src/middleware/create-middleware.ts +35 -4
- package/src/pages/login/page.tsx +20 -34
- package/src/routes/auth/credential-login-email-code.ts +6 -0
- package/src/routes/auth/credential-login.ts +6 -0
- package/src/routes/oauth/resume.ts +11 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { getIDPClientConfig } from '../../lib/idp-client-config';
|
|
3
|
+
import { resolveClientKey } from '../../lib/client-key';
|
|
4
|
+
import { bearerFetch } from '../../lib/bearer-fetch';
|
|
5
|
+
|
|
6
|
+
// `export const runtime = 'nodejs'` must be written LITERALLY in the app's route file (Next ignores a re-exported segment config).
|
|
7
|
+
|
|
8
|
+
/** One email-code request per this many seconds per browser (the IdP rate-limits too; this stops a double click and a loop). */
|
|
9
|
+
const EMAIL_CODE_COOLDOWN_S = 30;
|
|
10
|
+
|
|
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
|
+
export async function POST(req: NextRequest) {
|
|
26
|
+
const provisional = req.cookies.get('adm2fa')?.value;
|
|
27
|
+
if (!provisional) {
|
|
28
|
+
return NextResponse.json({ success: false, error: 'Your verification window expired. Sign in again.' }, { status: 401 });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const last = Number(req.cookies.get('adm2fa_emailreq')?.value);
|
|
32
|
+
const now = Math.floor(Date.now() / 1000);
|
|
33
|
+
if (Number.isFinite(last) && last > 0 && now - last < EMAIL_CODE_COOLDOWN_S) {
|
|
34
|
+
const wait = EMAIL_CODE_COOLDOWN_S - (now - last);
|
|
35
|
+
return NextResponse.json(
|
|
36
|
+
{ success: false, error: `We just asked for a code. Give it a moment (about ${wait} seconds) before asking again.`, retryAfter: wait },
|
|
37
|
+
{ status: 429 },
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
let clientKey = '';
|
|
42
|
+
try {
|
|
43
|
+
const config = await getIDPClientConfig();
|
|
44
|
+
clientKey = resolveClientKey(config, 'graft/admin-login/email-code').key ?? '';
|
|
45
|
+
} catch (err) {
|
|
46
|
+
console.error('[ADMIN_LOGIN_EMAIL_CODE] IDP client config unavailable:', err instanceof Error ? err.message : String(err));
|
|
47
|
+
return NextResponse.json(
|
|
48
|
+
{ success: false, error: 'Sign-in is temporarily unavailable. This is a problem on our side. Please try again shortly.' },
|
|
49
|
+
{ status: 503 },
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
let sendOk = false;
|
|
54
|
+
let devCode: string | undefined;
|
|
55
|
+
try {
|
|
56
|
+
const sendResp = await bearerFetch(`${process.env.IDP_URL}/api/ExternalAuth/twofa/email/send`, {
|
|
57
|
+
method: 'POST',
|
|
58
|
+
headers: {
|
|
59
|
+
'Content-Type': 'application/json',
|
|
60
|
+
Accept: 'application/json',
|
|
61
|
+
Authorization: `Bearer ${provisional}`,
|
|
62
|
+
'X-Client-Id': clientKey,
|
|
63
|
+
},
|
|
64
|
+
body: JSON.stringify({ client_id: clientKey }),
|
|
65
|
+
});
|
|
66
|
+
sendOk = sendResp.ok;
|
|
67
|
+
const text = await sendResp.text().catch(() => '');
|
|
68
|
+
if (!sendOk) console.error('[ADMIN_LOGIN_EMAIL_CODE] twofa/email/send failed', sendResp.status, text.slice(0, 200));
|
|
69
|
+
try {
|
|
70
|
+
const j = JSON.parse(text) as { dev_verification_code?: unknown; data?: { dev_verification_code?: unknown; result?: { dev_verification_code?: unknown } }; result?: { dev_verification_code?: unknown } } | null;
|
|
71
|
+
const echoed = j?.dev_verification_code ?? j?.data?.dev_verification_code ?? j?.data?.result?.dev_verification_code ?? j?.result?.dev_verification_code;
|
|
72
|
+
if (typeof echoed === 'string' && echoed) devCode = echoed; // present only when the server chose to echo (dev rigs); absence is the contract
|
|
73
|
+
} catch {
|
|
74
|
+
/* not JSON - nothing to echo */
|
|
75
|
+
}
|
|
76
|
+
} catch (err) {
|
|
77
|
+
console.error('[ADMIN_LOGIN_EMAIL_CODE] twofa/email/send fetch failed:', err instanceof Error ? err.message : String(err));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (!sendOk) {
|
|
81
|
+
return 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 });
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const res = NextResponse.json(
|
|
85
|
+
{ success: true, codeRequested: true, sentVia: 'email', ...(devCode ? { devVerificationCode: devCode } : {}) },
|
|
86
|
+
{ status: 200 },
|
|
87
|
+
);
|
|
88
|
+
const cookie = { httpOnly: true, secure: process.env.NODE_ENV === 'production', sameSite: 'lax' as const, path: '/', maxAge: 600 };
|
|
89
|
+
res.cookies.set('adm2fa_lane', 'email', cookie); // the verify turn then tries totp -> email, exactly as for any non-SMS user
|
|
90
|
+
res.cookies.set('adm2fa_emailreq', String(now), { ...cookie, maxAge: EMAIL_CODE_COOLDOWN_S });
|
|
91
|
+
return res;
|
|
92
|
+
}
|