feat(identity): test-only fixed OTP channel and rectification candidate cards
Staging Backend Quality Gate / validate (push) Has been cancelled
Staging Backend Quality Gate / publish (push) Has been cancelled

- IDENTITY_TEST_OTP env: when set to a 6-digit code, no real email is
  delivered; login page surfaces the fixed code so testers can register
  and sign in without a mailbox. Opt-in, never set in production.
- email-otp-login: test-channel notice with the pinned code.
- Rectification candidate list restyled with the project design system
  (warm canvas, action color, display serif, soft shadow, hover lift).
- Regression tests for config parsing, pinned OTP generation, login UI
  notice, and candidate card styles.
This commit is contained in:
Jesse
2026-08-10 19:38:27 +08:00
parent ed2294875b
commit 2697d16ef1
12 changed files with 131 additions and 32 deletions
@@ -39,11 +39,13 @@ export function EmailOtpLogin({
passwordEnabled = false,
passwordOnly = false,
successPath = "/",
testOtp = null,
}: {
provider: AuthProvider;
passwordEnabled?: boolean;
passwordOnly?: boolean;
successPath?: "/" | "/admin";
testOtp?: string | null;
}) {
const [mode, setMode] = useState<AuthMode>(passwordOnly ? "password" : "otp");
const [step, setStep] = useState<AuthStep>("email");
@@ -57,6 +59,7 @@ export function EmailOtpLogin({
const [notice, setNotice] = useState("");
const canUsePassword = provider === "self-hosted" && passwordEnabled;
const testMode = testOtp !== null && testOtp !== undefined && testOtp !== "";
const showLoginNavigation =
step === "email"
&& canUsePassword
@@ -307,6 +310,12 @@ export function EmailOtpLogin({
<h1 id="login-title">{title}</h1>
<p className="page-intro">{intro}</p>
{testMode && (
<p className="auth-test-channel" role="status">
<strong>{testOtp}</strong>
</p>
)}
{showLoginNavigation && (
<nav className="auth-mode-nav" aria-label="登录方式">
<div className="auth-mode-tabs">