接入 AD 密码验证与直接所属组,保留待 MFA 边界

This commit is contained in:
2026-09-25 21:06:39 +00:00
parent 2840f58397
commit d14151928d
18 changed files with 699 additions and 33 deletions
+37 -4
View File
@@ -3,7 +3,8 @@ import { useState, useLayoutEffect, type ReactNode } from "react";
import "./style.css";
type PageContext = {
step: "identity" | "verification" | "complete";
step: "identity" | "verification" | "complete" | "password" | "mfa-pending";
identity?: { username: string; objectGuid: string; email: string; groups: string[]; groupDns: string[] };
name: string;
error: string;
action: string;
@@ -32,8 +33,8 @@ function Form({ children }: { children: ReactNode }) {
<button className="primary" type="submit" disabled={pending}>
{pending
? "正在继续…"
: context.step === "complete"
? "重新体验"
: (context.step === "complete" || context.step === "mfa-pending")
? (context.step === "mfa-pending" ? "退出并重新验证" : "重新体验")
: "继续"}
</button>
</form>
@@ -156,4 +157,36 @@ function App() {
);
}
createRoot(document.getElementById("root")!).render(<App />);
function SignIn() {
useLayoutEffect(() => { performance.mark("iam-page-ready"); }, []);
const identity = context.identity;
return <main>
<header><a href="/signin" className="brand">i<span>am</span></a></header>
<section className="card" aria-labelledby="title">
<div className="eyebrow">AD 登录验证</div>
<h1 id="title">{context.step === "password" ? "登录你的账号" : "密码已验证,等待 MFA"}</h1>
<p className="intro">{context.step === "password"
? "使用 AD 用户名或完整 UPN 登录。"
: `${context.name},目录验证成功。第二因素尚未接入,本次没有完成登录或向应用授权。`}</p>
{context.error && <p className="error" role="alert">{context.error}</p>}
{identity && <div className="directory-result">
<dl><dt>账号</dt><dd>{identity.username}</dd>
<dt>目录标识</dt><dd>{identity.objectGuid}</dd>
<dt>邮箱</dt><dd>{identity.email || "未设置"}</dd></dl>
<h2>直接所属组</h2>
{identity.groups.length ? <ul>{identity.groups.map(group => <li key={group}>{group}</li>)}</ul> : <p>没有直接所属组。</p>}
<details><summary>组 DN</summary><ul>{identity.groupDns.map(dn => <li key={dn}>{dn}</li>)}</ul></details>
<p>当前仅读取 memberOf,不展开嵌套组,也不包含主组。</p>
</div>}
<Form>{context.step === "password" && <>
<label>用户名<input name="username" autoComplete="username" autoFocus required maxLength={256} defaultValue={context.name} /></label>
<label>密码<input name="password" type="password" autoComplete="current-password" required maxLength={1024} /></label>
</>}</Form>
</section>
<footer>独立 IAM · AD 接入验证</footer>
</main>;
}
createRoot(document.getElementById("root")!).render(
context.step === "password" || context.step === "mfa-pending" ? <SignIn /> : <App />,
);
+4
View File
@@ -238,3 +238,7 @@ button:focus-visible {
color: #ffc3b9;
}
}
.directory-result { overflow-wrap: anywhere; }
.directory-result dd { margin: 0 0 1rem; }
.directory-result h2 { font-size: 1rem; }