@payez/next-mvp 4.8.4 → 4.8.5
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/components/auth/CredentialSignInForm.js +6 -1
- package/dist/components/auth/DevCodeNotice.d.ts +10 -0
- package/dist/components/auth/DevCodeNotice.js +16 -0
- package/dist/lib/credential-sign-in-client.d.ts +1 -0
- package/dist/lib/credential-sign-in-client.js +6 -2
- package/package.json +1 -1
- package/src/components/auth/CredentialSignInForm.tsx +6 -0
- package/src/components/auth/DevCodeNotice.tsx +17 -0
- package/src/lib/credential-sign-in-client.ts +8 -3
|
@@ -9,6 +9,7 @@ const TraditionalAuthSection_1 = require("./TraditionalAuthSection");
|
|
|
9
9
|
const safe_callback_1 = require("../../auth/safe-callback");
|
|
10
10
|
const authorize_resume_1 = require("../../lib/authorize-resume");
|
|
11
11
|
const code_step_copy_1 = require("../../lib/code-step-copy");
|
|
12
|
+
const DevCodeNotice_1 = require("./DevCodeNotice");
|
|
12
13
|
const credential_sign_in_client_1 = require("../../lib/credential-sign-in-client");
|
|
13
14
|
const inputStyle = {
|
|
14
15
|
borderColor: 'var(--border-default, #e5e7eb)',
|
|
@@ -39,6 +40,7 @@ function CredentialSignInForm({ callbackUrl, fetchImpl, onSignedIn, showForgotPa
|
|
|
39
40
|
const [fieldError, setFieldError] = (0, react_1.useState)(null);
|
|
40
41
|
const [codeRequest, setCodeRequest] = (0, react_1.useState)(code_step_copy_1.NO_CODE_REQUEST);
|
|
41
42
|
const [offerEmailCode, setOfferEmailCode] = (0, react_1.useState)(false);
|
|
43
|
+
const [devCode, setDevCode] = (0, react_1.useState)(null);
|
|
42
44
|
const [emailCodeMsg, setEmailCodeMsg] = (0, react_1.useState)(null);
|
|
43
45
|
async function submit(withCode) {
|
|
44
46
|
setBusy(true);
|
|
@@ -56,6 +58,7 @@ function CredentialSignInForm({ callbackUrl, fetchImpl, onSignedIn, showForgotPa
|
|
|
56
58
|
}
|
|
57
59
|
if (r.kind === 'two_factor') {
|
|
58
60
|
setCodeRequest(r.codeRequest);
|
|
61
|
+
setDevCode(r.devCode);
|
|
59
62
|
setStep('code');
|
|
60
63
|
return;
|
|
61
64
|
}
|
|
@@ -90,6 +93,7 @@ function CredentialSignInForm({ callbackUrl, fetchImpl, onSignedIn, showForgotPa
|
|
|
90
93
|
function onBack() {
|
|
91
94
|
setStep('credentials');
|
|
92
95
|
setCodeRequest(code_step_copy_1.NO_CODE_REQUEST);
|
|
96
|
+
setDevCode(null);
|
|
93
97
|
setCode('');
|
|
94
98
|
setFieldError(null);
|
|
95
99
|
setError(null);
|
|
@@ -103,6 +107,7 @@ function CredentialSignInForm({ callbackUrl, fetchImpl, onSignedIn, showForgotPa
|
|
|
103
107
|
const r = await (0, credential_sign_in_client_1.requestEmailCode)(fetchImpl);
|
|
104
108
|
if (r.kind === 'requested') {
|
|
105
109
|
setCodeRequest(r.codeRequest);
|
|
110
|
+
setDevCode(r.devCode);
|
|
106
111
|
setError(null);
|
|
107
112
|
setOfferEmailCode(false);
|
|
108
113
|
setEmailCodeMsg('We asked for a code to be emailed to you. Enter it above when it arrives.');
|
|
@@ -121,7 +126,7 @@ function CredentialSignInForm({ callbackUrl, fetchImpl, onSignedIn, showForgotPa
|
|
|
121
126
|
return ((0, jsx_runtime_1.jsxs)("div", { "data-testid": "credential-sign-in-code-step", className: "text-left", children: [(0, jsx_runtime_1.jsx)("p", { className: "text-sm mb-4", style: { color: 'var(--text-primary, #111827)' }, children: (0, code_step_copy_1.codeStepInstruction)(email, codeRequest) }), (0, jsx_runtime_1.jsxs)("form", { onSubmit: onCodeSubmit, noValidate: true, className: "space-y-4", children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("label", { htmlFor: "credential-code", className: "block text-sm font-medium mb-2", style: { color: 'var(--text-primary, #111827)' }, children: "Authentication code" }), (0, jsx_runtime_1.jsx)("input", { id: "credential-code", name: "code", type: "text", inputMode: "numeric", autoComplete: "one-time-code", required: true, placeholder: "000000", value: code, disabled: busy, onChange: (e) => {
|
|
122
127
|
setCode(e.target.value);
|
|
123
128
|
setFieldError(null);
|
|
124
|
-
}, className: "w-full px-4 py-3 rounded-lg border transition-colors focus:outline-none focus:ring-2 disabled:opacity-50", style: inputStyle })] }), (0, jsx_runtime_1.jsx)("button", { type: "submit", disabled: busy, className: "w-full py-3 px-4 rounded-lg font-semibold focus:outline-none focus:ring-2 disabled:opacity-50 disabled:cursor-not-allowed transition-colors", style: { background: 'var(--btn-primary-bg, #11B588)', color: 'var(--btn-primary-text, white)' }, children: busy ? 'Verifying…' : 'Verify' })] }), (0, jsx_runtime_1.jsx)("button", { type: "button", onClick: onBack, disabled: busy, className: "text-sm underline mt-4", style: { background: 'none', border: 'none', padding: 0, color: 'var(--text-muted, #6b7280)' }, children: "Back" }), problemLine, (offerEmailCode || emailCodeMsg) && ((0, jsx_runtime_1.jsxs)("div", { "data-testid": "email-code-offer", className: "mt-4", children: [offerEmailCode && ((0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => void askForEmailCode(), disabled: busy, className: "text-sm font-semibold underline", style: { background: 'none', border: 'none', padding: 0, color: 'var(--text-primary, #111827)' }, children: "Email me a code instead" })), emailCodeMsg && ((0, jsx_runtime_1.jsx)("p", { role: "status", "data-testid": "email-code-offer-msg", className: "text-sm mt-2", style: { color: 'var(--text-primary, #111827)' }, children: emailCodeMsg }))] }))] }));
|
|
129
|
+
}, className: "w-full px-4 py-3 rounded-lg border transition-colors focus:outline-none focus:ring-2 disabled:opacity-50", style: inputStyle }), (0, jsx_runtime_1.jsx)(DevCodeNotice_1.DevCodeNotice, { code: devCode })] }), (0, jsx_runtime_1.jsx)("button", { type: "submit", disabled: busy, className: "w-full py-3 px-4 rounded-lg font-semibold focus:outline-none focus:ring-2 disabled:opacity-50 disabled:cursor-not-allowed transition-colors", style: { background: 'var(--btn-primary-bg, #11B588)', color: 'var(--btn-primary-text, white)' }, children: busy ? 'Verifying…' : 'Verify' })] }), (0, jsx_runtime_1.jsx)("button", { type: "button", onClick: onBack, disabled: busy, className: "text-sm underline mt-4", style: { background: 'none', border: 'none', padding: 0, color: 'var(--text-muted, #6b7280)' }, children: "Back" }), problemLine, (offerEmailCode || emailCodeMsg) && ((0, jsx_runtime_1.jsxs)("div", { "data-testid": "email-code-offer", className: "mt-4", children: [offerEmailCode && ((0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => void askForEmailCode(), disabled: busy, className: "text-sm font-semibold underline", style: { background: 'none', border: 'none', padding: 0, color: 'var(--text-primary, #111827)' }, children: "Email me a code instead" })), emailCodeMsg && ((0, jsx_runtime_1.jsx)("p", { role: "status", "data-testid": "email-code-offer-msg", className: "text-sm mt-2", style: { color: 'var(--text-primary, #111827)' }, children: emailCodeMsg }))] }))] }));
|
|
125
130
|
}
|
|
126
131
|
return ((0, jsx_runtime_1.jsxs)("div", { "data-testid": "credential-sign-in", className: "text-left", children: [(0, jsx_runtime_1.jsx)(TraditionalAuthSection_1.TraditionalAuthSection, { email: email, password: password, onEmailChange: (v) => {
|
|
127
132
|
setEmail(v);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AUTH-045 (BAPert 75627): on a DEV identity service that echoes the emailed sign-in code in its own answer (Email:DevCodeEcho, an
|
|
3
|
+
* IdP setting that is on only in Development), the sign-in form shows that code under the code box, because on a dev rig the email
|
|
4
|
+
* goes to nobody. This component is a PURE PASSTHROUGH of the server's `devVerificationCode`: it renders nothing unless it is given
|
|
5
|
+
* a non-empty string, and CredentialSignInForm gives it nothing but the value the door returned. There is no environment flag in the
|
|
6
|
+
* app, so a production IdP (echo off, field absent) can never make it appear.
|
|
7
|
+
*/
|
|
8
|
+
export declare function DevCodeNotice({ code }: {
|
|
9
|
+
code?: string | null;
|
|
10
|
+
}): import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DevCodeNotice = DevCodeNotice;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
/**
|
|
6
|
+
* AUTH-045 (BAPert 75627): on a DEV identity service that echoes the emailed sign-in code in its own answer (Email:DevCodeEcho, an
|
|
7
|
+
* IdP setting that is on only in Development), the sign-in form shows that code under the code box, because on a dev rig the email
|
|
8
|
+
* goes to nobody. This component is a PURE PASSTHROUGH of the server's `devVerificationCode`: it renders nothing unless it is given
|
|
9
|
+
* a non-empty string, and CredentialSignInForm gives it nothing but the value the door returned. There is no environment flag in the
|
|
10
|
+
* app, so a production IdP (echo off, field absent) can never make it appear.
|
|
11
|
+
*/
|
|
12
|
+
function DevCodeNotice({ code }) {
|
|
13
|
+
if (typeof code !== 'string' || code === '')
|
|
14
|
+
return null;
|
|
15
|
+
return ((0, jsx_runtime_1.jsxs)("p", { "data-testid": "dev-sign-in-code", className: "text-sm mt-3", style: { color: 'var(--text-muted, #6b7280)' }, children: ["Development sign-in code: ", (0, jsx_runtime_1.jsx)("strong", { children: code })] }));
|
|
16
|
+
}
|
|
@@ -43,6 +43,10 @@ function validateCredentials(email, password) {
|
|
|
43
43
|
errors.password = 'Enter your password.';
|
|
44
44
|
return errors.email || errors.password ? errors : null;
|
|
45
45
|
}
|
|
46
|
+
/** The dev echo: the door passes the IdP's DevVerificationCode through as `devVerificationCode`. Nothing else is ever a dev code. */
|
|
47
|
+
function readDevCode(body) {
|
|
48
|
+
return typeof body?.devVerificationCode === 'string' && body.devVerificationCode ? body.devVerificationCode : null;
|
|
49
|
+
}
|
|
46
50
|
function asString(v) {
|
|
47
51
|
return typeof v === 'string' && v ? v : undefined;
|
|
48
52
|
}
|
|
@@ -58,7 +62,7 @@ async function credentialSignIn(args, fetchImpl = (input, init) => fetch(input,
|
|
|
58
62
|
if (res.status === 429)
|
|
59
63
|
return { kind: 'rate_limited', message: asString(body?.error) ?? exports.MSG_RATE_LIMITED };
|
|
60
64
|
if (body?.requiresTwoFactor)
|
|
61
|
-
return { kind: 'two_factor', codeRequest: (0, code_step_copy_1.readCodeRequest)(body) };
|
|
65
|
+
return { kind: 'two_factor', codeRequest: (0, code_step_copy_1.readCodeRequest)(body), devCode: readDevCode(body) };
|
|
62
66
|
if (!res.ok || body?.success !== true) {
|
|
63
67
|
return { kind: 'error', message: asString(body?.error) ?? exports.MSG_CANNOT_SIGN_IN, offerEmailCode: body?.offerEmailCode === true };
|
|
64
68
|
}
|
|
@@ -78,7 +82,7 @@ async function requestEmailCode(fetchImpl = (input, init) => fetch(input, init))
|
|
|
78
82
|
return {
|
|
79
83
|
kind: 'requested',
|
|
80
84
|
codeRequest: (0, code_step_copy_1.readCodeRequest)(body),
|
|
81
|
-
devCode:
|
|
85
|
+
devCode: readDevCode(body),
|
|
82
86
|
};
|
|
83
87
|
}
|
|
84
88
|
return { kind: 'error', message: asString(body?.error) ?? exports.MSG_EMAIL_CODE_FAILED };
|
package/package.json
CHANGED
|
@@ -5,6 +5,7 @@ import { TraditionalAuthSection } from './TraditionalAuthSection';
|
|
|
5
5
|
import { safeCallbackPath } from '../../auth/safe-callback';
|
|
6
6
|
import { authorizeResumeHref } from '../../lib/authorize-resume';
|
|
7
7
|
import { NO_CODE_REQUEST, codeStepInstruction, type CodeRequest } from '../../lib/code-step-copy';
|
|
8
|
+
import { DevCodeNotice } from './DevCodeNotice';
|
|
8
9
|
import {
|
|
9
10
|
credentialSignIn,
|
|
10
11
|
requestEmailCode,
|
|
@@ -73,6 +74,7 @@ export function CredentialSignInForm({ callbackUrl, fetchImpl, onSignedIn, showF
|
|
|
73
74
|
const [fieldError, setFieldError] = useState<string | null>(null);
|
|
74
75
|
const [codeRequest, setCodeRequest] = useState<CodeRequest>(NO_CODE_REQUEST);
|
|
75
76
|
const [offerEmailCode, setOfferEmailCode] = useState(false);
|
|
77
|
+
const [devCode, setDevCode] = useState<string | null>(null);
|
|
76
78
|
const [emailCodeMsg, setEmailCodeMsg] = useState<string | null>(null);
|
|
77
79
|
|
|
78
80
|
async function submit(withCode?: string) {
|
|
@@ -89,6 +91,7 @@ export function CredentialSignInForm({ callbackUrl, fetchImpl, onSignedIn, showF
|
|
|
89
91
|
}
|
|
90
92
|
if (r.kind === 'two_factor') {
|
|
91
93
|
setCodeRequest(r.codeRequest);
|
|
94
|
+
setDevCode(r.devCode);
|
|
92
95
|
setStep('code');
|
|
93
96
|
return;
|
|
94
97
|
}
|
|
@@ -124,6 +127,7 @@ export function CredentialSignInForm({ callbackUrl, fetchImpl, onSignedIn, showF
|
|
|
124
127
|
function onBack() {
|
|
125
128
|
setStep('credentials');
|
|
126
129
|
setCodeRequest(NO_CODE_REQUEST);
|
|
130
|
+
setDevCode(null);
|
|
127
131
|
setCode('');
|
|
128
132
|
setFieldError(null);
|
|
129
133
|
setError(null);
|
|
@@ -138,6 +142,7 @@ export function CredentialSignInForm({ callbackUrl, fetchImpl, onSignedIn, showF
|
|
|
138
142
|
const r = await requestEmailCode(fetchImpl);
|
|
139
143
|
if (r.kind === 'requested') {
|
|
140
144
|
setCodeRequest(r.codeRequest);
|
|
145
|
+
setDevCode(r.devCode);
|
|
141
146
|
setError(null);
|
|
142
147
|
setOfferEmailCode(false);
|
|
143
148
|
setEmailCodeMsg('We asked for a code to be emailed to you. Enter it above when it arrives.');
|
|
@@ -182,6 +187,7 @@ export function CredentialSignInForm({ callbackUrl, fetchImpl, onSignedIn, showF
|
|
|
182
187
|
className="w-full px-4 py-3 rounded-lg border transition-colors focus:outline-none focus:ring-2 disabled:opacity-50"
|
|
183
188
|
style={inputStyle}
|
|
184
189
|
/>
|
|
190
|
+
<DevCodeNotice code={devCode} />
|
|
185
191
|
</div>
|
|
186
192
|
<button
|
|
187
193
|
type="submit"
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* AUTH-045 (BAPert 75627): on a DEV identity service that echoes the emailed sign-in code in its own answer (Email:DevCodeEcho, an
|
|
5
|
+
* IdP setting that is on only in Development), the sign-in form shows that code under the code box, because on a dev rig the email
|
|
6
|
+
* goes to nobody. This component is a PURE PASSTHROUGH of the server's `devVerificationCode`: it renders nothing unless it is given
|
|
7
|
+
* a non-empty string, and CredentialSignInForm gives it nothing but the value the door returned. There is no environment flag in the
|
|
8
|
+
* app, so a production IdP (echo off, field absent) can never make it appear.
|
|
9
|
+
*/
|
|
10
|
+
export function DevCodeNotice({ code }: { code?: string | null }) {
|
|
11
|
+
if (typeof code !== 'string' || code === '') return null;
|
|
12
|
+
return (
|
|
13
|
+
<p data-testid="dev-sign-in-code" className="text-sm mt-3" style={{ color: 'var(--text-muted, #6b7280)' }}>
|
|
14
|
+
Development sign-in code: <strong>{code}</strong>
|
|
15
|
+
</p>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
@@ -22,7 +22,7 @@ export type FetchLike = (input: string, init?: RequestInit) => Promise<Response>
|
|
|
22
22
|
|
|
23
23
|
export type CredentialSignInResult =
|
|
24
24
|
| { kind: 'success' }
|
|
25
|
-
| { kind: 'two_factor'; codeRequest: CodeRequest }
|
|
25
|
+
| { kind: 'two_factor'; codeRequest: CodeRequest; devCode: string | null }
|
|
26
26
|
| { kind: 'rate_limited'; message: string }
|
|
27
27
|
| { kind: 'error'; message: string; offerEmailCode: boolean };
|
|
28
28
|
|
|
@@ -46,6 +46,11 @@ export function validateCredentials(email: string, password: string): { email?:
|
|
|
46
46
|
return errors.email || errors.password ? errors : null;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
/** The dev echo: the door passes the IdP's DevVerificationCode through as `devVerificationCode`. Nothing else is ever a dev code. */
|
|
50
|
+
function readDevCode(body: any): string | null {
|
|
51
|
+
return typeof body?.devVerificationCode === 'string' && body.devVerificationCode ? body.devVerificationCode : null;
|
|
52
|
+
}
|
|
53
|
+
|
|
49
54
|
function asString(v: unknown): string | undefined {
|
|
50
55
|
return typeof v === 'string' && v ? v : undefined;
|
|
51
56
|
}
|
|
@@ -64,7 +69,7 @@ export async function credentialSignIn(
|
|
|
64
69
|
const body: any = await res.json().catch(() => null);
|
|
65
70
|
|
|
66
71
|
if (res.status === 429) return { kind: 'rate_limited', message: asString(body?.error) ?? MSG_RATE_LIMITED };
|
|
67
|
-
if (body?.requiresTwoFactor) return { kind: 'two_factor', codeRequest: readCodeRequest(body) };
|
|
72
|
+
if (body?.requiresTwoFactor) return { kind: 'two_factor', codeRequest: readCodeRequest(body), devCode: readDevCode(body) };
|
|
68
73
|
if (!res.ok || body?.success !== true) {
|
|
69
74
|
return { kind: 'error', message: asString(body?.error) ?? MSG_CANNOT_SIGN_IN, offerEmailCode: body?.offerEmailCode === true };
|
|
70
75
|
}
|
|
@@ -89,7 +94,7 @@ export async function requestEmailCode(fetchImpl: FetchLike = (input, init) => f
|
|
|
89
94
|
return {
|
|
90
95
|
kind: 'requested',
|
|
91
96
|
codeRequest: readCodeRequest(body),
|
|
92
|
-
devCode:
|
|
97
|
+
devCode: readDevCode(body),
|
|
93
98
|
};
|
|
94
99
|
}
|
|
95
100
|
return { kind: 'error', message: asString(body?.error) ?? MSG_EMAIL_CODE_FAILED };
|