Files
Hare f17b5212c7 fix(auth): confirm passkey operations and preserve safe legacy migration
Fence displayed identity and native prompt generations, classify secret-free outcomes, recover atomic addition receipts without restoring old credentials, and expose truthful own-key verification metadata. Preserve required verifier and enrollment authority boundaries. Include real-store, production browser, native CTAP2 and visible responsive regressions.

Validation: root check/fmt; API 115; passkey security 25; Web unit 712/component 605; auth/entry/activation/hosting browsers; product auth E2E 8 and two explicit browser suites. Server suite: 1262 lib+12 CLI, 3 ignored and 2 environment-specific exclusions. State-name checker retains 15 byte-identical baseline findings; no new exceptions. Physical keys/mobile and hosted CI remain unverified.
2026-10-26 07:18:23 +09:00

90 lines
3.3 KiB
TypeScript

// Setupの表示条件だけを代替する。Bearerやrequest bodyは読まず、記録もしない。
// WebAuthnは固定の非暗号credentialを返すため、本物の認証・署名検証ではない。
export const setupWebAuthnProjection = `
(() => {
const bytes = () => new Uint8Array([117, 105]).buffer;
const core = () => ({ id: 'ui-projection-only', rawId: bytes(), type: 'public-key', getClientExtensionResults: () => ({}) });
Object.defineProperty(navigator.credentials, 'create', { configurable: true, value: async () => ({ ...core(), response: { clientDataJSON: bytes(), attestationObject: bytes(), getTransports: () => ['internal'] } }) });
Object.defineProperty(navigator.credentials, 'get', { configurable: true, value: async () => ({ ...core(), response: { clientDataJSON: bytes(), authenticatorData: bytes(), signature: bytes(), userHandle: null } }) });
})();
`;
const user = {
user_id: "fixture-user",
account_id: "fixture-account",
handle: "alice",
display_name: "Alice",
};
const stages = new Map<string, "enrolled" | "login">();
export function setupProjectionResponse(
path: string,
mode: string,
reset: boolean,
tokenRequest = false,
): Response | null {
if (!mode.startsWith("setup-")) return null;
if (reset) stages.delete(mode);
if (path === "/__ui_fixture/webauthn.js") {
return new Response(setupWebAuthnProjection, {
headers: { "content-type": "text/javascript" },
});
}
if (path === "/api/auth/whoami") {
if (tokenRequest) {
return Response.json({
actor: { ...user, auth_method: "api_token" },
authentication_reference: "fixture-token-binding",
session: { kind: "authenticated", expires_at: "2099-01-01T00:00:00Z" },
});
}
return Response.json({
authentication_reference: stages.get(mode) === "login"
? "fixture-authentication"
: null,
session: stages.get(mode) === "login"
? { kind: "authenticated", expires_at: "2099-01-01T00:00:00Z" }
: { kind: "anonymous" },
actor: stages.get(mode) === "login"
? { ...user, auth_method: "browser_session" }
: null,
});
}
if (path === "/api/hosting/access") {
return Response.json({ is_admin: stages.get(mode) === "login" });
}
if (path === "/api/auth/passkeys/addition/options") {
if (mode === "setup-reject") {
return new Response("synthetic enrollment rejection", { status: 403 });
}
return Response.json({
challenge_id: "fixture-add",
public_key: {
challenge: "dWk",
rp: { name: "UI projection" },
user: { id: "dWk", name: "alice", displayName: "Alice" },
pubKeyCredParams: [{ type: "public-key", alg: -7 }],
},
});
}
if (path === "/api/auth/passkeys/addition/complete") {
stages.set(mode, "enrolled");
return Response.json({
user,
authentication_reference: "fixture-authentication",
});
}
if (path === "/api/auth/passkeys/login/options") {
return Response.json({
challenge_id: "fixture-login",
public_key: { challenge: "dWk" },
});
}
if (path === "/api/auth/passkeys/login/complete") {
stages.set(mode, "login");
return Response.json({
user,
authentication_reference: "fixture-authentication",
});
}
return null;
}