@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.
@@ -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
+ }
@@ -22,6 +22,7 @@ export type CredentialSignInResult = {
22
22
  } | {
23
23
  kind: 'two_factor';
24
24
  codeRequest: CodeRequest;
25
+ devCode: string | null;
25
26
  } | {
26
27
  kind: 'rate_limited';
27
28
  message: string;
@@ -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: typeof body?.devVerificationCode === 'string' && body.devVerificationCode ? body.devVerificationCode : null,
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@payez/next-mvp",
3
- "version": "4.8.4",
3
+ "version": "4.8.5",
4
4
  "sideEffects": false,
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -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: typeof body?.devVerificationCode === 'string' && body.devVerificationCode ? body.devVerificationCode : null,
97
+ devCode: readDevCode(body),
93
98
  };
94
99
  }
95
100
  return { kind: 'error', message: asString(body?.error) ?? MSG_EMAIL_CODE_FAILED };