feat: release design system engine, studio and component contracts

This commit is contained in:
Clintchiz committed 2026-10-19 13:09:03 +05:30
1 parent ff1065fb27
commit 3943b946d6
693 files changed
+175546 -3894

No files matched your search

+64
View File
@@ -0,0 +1,64 @@
import { expect, test } from "bun:test";
import { createContext } from "@wrnexus/core";
import { createAuthEngine } from "../src/engine.ts";
import { createAuthHttpHandlers } from "../src/http/index.ts";
import { MemoryAuthStore } from "../src/stores/memory.ts";
import type { AuthDeliveryMessage } from "../src/types.ts";
test("MFA resend creates a fresh code after cooldown and invalidates the previous code", async () => {
let now = 1_790_000_000_000;
const messages: AuthDeliveryMessage[] = [];
const engine = createAuthEngine({
store: new MemoryAuthStore(),
secret: "mfa-resend-test-secret-longer-than-thirty-two-characters",
clock: { now: () => now },
requireTrustedDeviceVerification: true,
delivery: {
async send(message) {
messages.push(message);
},
},
});
await engine.register({ email: "resend@example.test", password: "StrongPassword123!" });
await engine.verifyEmail(messages.find((m) => m.template === "verify-email")!.token!);
const login = await engine.login({
identifier: "resend@example.test",
password: "StrongPassword123!",
fingerprint: "browser",
});
const handlers = createAuthHttpHandlers({ engine });
const request = (resend: boolean) => {
const req = new Request("https://sso.example.test/api/auth/mfa/otp", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ mfaToken: login.mfaToken, method: "email-otp", resend }),
});
return handlers.beginMfaOtp(createContext(req, new URL(req.url)));
};
const first = await (await request(false)).json();
const firstCode = messages.at(-1)!.code!;
const count = messages.length;
expect((await request(true)).status).toBe(400);
expect(messages.length).toBe(count);
expect((await (await request(false)).json()).id).toBe(first.id);
now += 60_000;
const next = await (await request(true)).json();
expect(next.ok).toBe(true);
expect(next.id).not.toBe(first.id);
expect(next.retryAfter).toBe(60);
expect(messages.length).toBe(count + 1);
expect((await engine.verifyOtp(first.id, firstCode, "mfa")).ok).toBe(false);
expect(
(
await engine.completeMfa({
mfaToken: login.mfaToken!,
method: "email-otp",
challengeId: next.id,
code: messages.at(-1)!.code!,
fingerprint: "browser",
})
).ok,
).toBe(true);
now += 60_000;
expect((await request(true)).status).toBe(400);
});
+55 -1
View File
@@ -1,6 +1,60 @@
import { expect, test } from "bun:test";
import { readFileSync } from "node:fs";
import { Window, type HTMLInputElement, type HTMLElement } from "happy-dom";
import { Window, type HTMLInputElement, type HTMLButtonElement, type HTMLElement } from "happy-dom";
test("MFA resend waits for cooldown, clears old code and replaces the active challenge", async () => {
const window = new Window({ url: "https://sso.example.test/mfa" });
try {
let now = Date.now();
window.Date.now = () => now;
window.sessionStorage.setItem(
"wrnexus.auth.mfa",
JSON.stringify({
mfaToken: "synthetic-test-challenge",
mfaExpiresAt: now + 300_000,
requires: { mfa: ["email-otp"] },
}),
);
window.document.body.innerHTML = `<section data-auth-mfa-challenge><form data-auth-mfa-form>
<input name="mfaToken"><input name="challengeId"><input name="code"><input type="radio" name="method" value="email-otp">
<div data-auth-mfa-pin><input name="otpCode"></div><p data-auth-mfa-status></p><p data-error="_form">Verification failed</p>
<div data-auth-mfa-resend-group hidden><button type="button" data-auth-mfa-resend>Resend code</button></div><button type="submit">Verify</button>
</form></section>`;
const requests: any[] = [];
window.fetch = (async (_: unknown, init: RequestInit) => {
requests.push(JSON.parse(String(init.body)));
return Response.json({
id: "challenge-" + requests.length,
retryAfter: 60,
expiresAt: now + 300_000,
});
}) as any;
window.eval(readFileSync(new URL("../assets/client/auth.js", import.meta.url), "utf8"));
(window as any).WRNexusAuth.mount(window.document);
await new Promise((resolve) => setTimeout(resolve, 0));
const button = window.document.querySelector<HTMLButtonElement>("[data-auth-mfa-resend]")!;
expect(button.disabled).toBe(true);
expect(button.textContent).toContain("60s");
button.dispatchEvent(new window.Event("click"));
expect(requests.length).toBe(1);
const code = window.document.querySelector<HTMLInputElement>("[name=otpCode]")!;
code.value = "123456";
now += 60_001;
button.dispatchEvent(new window.Event("click"));
button.dispatchEvent(new window.Event("click"));
await new Promise((resolve) => setTimeout(resolve, 0));
expect(requests.length).toBe(2);
expect(requests[1].resend).toBe(true);
expect(window.document.querySelector<HTMLInputElement>("[name=challengeId]")!.value).toBe(
"challenge-2",
);
expect(code.value).toBe("");
expect(window.document.querySelector("[data-error=_form]")!.textContent).toBe("");
expect(button.disabled).toBe(true);
} finally {
await window.happyDOM.close();
}
});
test("expired and legacy MFA requests are rejected on mount before entering a code", async () => {
for (const deadline of [undefined, Date.now() - 1]) {
+15 -5
View File
@@ -5,8 +5,11 @@ import { MemoryAuthStore } from "../src/stores/memory.ts";
const profile = { id: "google-user", email: "google@example.test", raw: { email_verified: true } };
function setup(providers?: string[]) {
const store = new MemoryAuthStore();
const engine = createAuthEngine({ store, secret: "oauth-device-policy-test-at-least-32-characters",
requireVerifiedEmail: true, requireTrustedDeviceVerification: true,
const engine = createAuthEngine({
store,
secret: "oauth-device-policy-test-at-least-32-characters",
requireVerifiedEmail: true,
requireTrustedDeviceVerification: true,
verifiedOAuthDeviceProviders: providers,
isOAuthEmailVerified: (_provider, value) => value.raw.email_verified === true,
});
@@ -14,18 +17,25 @@ function setup(providers?: string[]) {
}
test("explicit verified Google policy satisfies the device check without trusting future password logins", async () => {
const { engine } = setup(["google"]);
const result = await engine.loginWithOAuth("google", profile, undefined, { fingerprint: "new-browser" });
const result = await engine.loginWithOAuth("google", profile, undefined, {
fingerprint: "new-browser",
});
expect(result.ok).toBe(true);
expect(result.session).toBeDefined();
expect(await engine.listTrustedDevices(result.user!.id)).toHaveLength(0);
});
test("device verification remains the default and applies to other providers", async () => {
expect((await setup().engine.loginWithOAuth("google", profile)).code).toBe("mfa-required");
expect((await setup(["google"]).engine.loginWithOAuth("other", profile)).code).toBe("mfa-required");
expect((await setup(["google"]).engine.loginWithOAuth("other", profile)).code).toBe(
"mfa-required",
);
});
test("unverified provider emails cannot satisfy verification", async () => {
const { engine } = setup(["google"]);
const result = await engine.loginWithOAuth("google", { ...profile, raw: { email_verified: false } });
const result = await engine.loginWithOAuth("google", {
...profile,
raw: { email_verified: false },
});
expect(result.ok).toBe(false);
expect(result.session).toBeUndefined();
});
+51 -14
View File
@@ -3,39 +3,76 @@ import { createContext } from "@wrnexus/core";
import { createAuthEngine } from "../src/engine.ts";
import { MemoryAuthStore } from "../src/stores/memory.ts";
import { configureAuthRuntime } from "../src/plugin.ts";
import { clearDefaultAuthEngine, getDefaultAuthRouteOptions, setDefaultAuthRouteOptions } from "../src/runtime.ts";
import {
clearDefaultAuthEngine,
getDefaultAuthRouteOptions,
setDefaultAuthRouteOptions,
} from "../src/runtime.ts";
import { invokeAuthHandler } from "../src/routes/api.ts";
afterEach(() => { clearDefaultAuthEngine(); setDefaultAuthRouteOptions({}); });
afterEach(() => {
clearDefaultAuthEngine();
setDefaultAuthRouteOptions({});
});
test("production startup restores runtime-only providers and passkey settings for generated routes", async () => {
const engine = createAuthEngine({
store: new MemoryAuthStore(), secret: "production-runtime-test-secret-at-least-32-characters",
store: new MemoryAuthStore(),
secret: "production-runtime-test-secret-at-least-32-characters",
passkeys: {
async authenticationOptions({ rpId }) { return { challenge: "test-challenge", rpId, timeout: 60000, userVerification: "required" }; },
async registrationOptions() { throw new Error("not used"); },
async verifyRegistration() { return { verified: false }; },
async verifyAuthentication() { return { verified: false }; },
async authenticationOptions({ rpId }) {
return { challenge: "test-challenge", rpId, timeout: 60000, userVerification: "required" };
},
async registrationOptions() {
throw new Error("not used");
},
async verifyRegistration() {
return { verified: false };
},
async verifyAuthentication() {
return { verified: false };
},
},
});
// Build-time credentials absent, followed by server-startup runtime secrets.
configureAuthRuntime({ engine, oauth: {} });
const ctx = createContext(new Request("https://sso.example/api/auth/oauth/google"), new URL("https://sso.example/api/auth/oauth/google"));
const ctx = createContext(
new Request("https://sso.example/api/auth/oauth/google"),
new URL("https://sso.example/api/auth/oauth/google"),
);
ctx.params = { provider: "google" };
expect((await invokeAuthHandler("startOAuth", ctx)).status).toBe(404);
configureAuthRuntime({ engine, baseUrl: "https://sso.example", sessionCookieName: "custom_session",
configureAuthRuntime({
engine,
baseUrl: "https://sso.example",
sessionCookieName: "custom_session",
oauth: { google: { clientId: "runtime-client", clientSecret: "runtime-secret" } },
passkey: { rpId: "sso.example", origin: "https://sso.example" }, oauthMfaPath: "/mfa" });
passkey: { rpId: "sso.example", origin: "https://sso.example" },
oauthMfaPath: "/mfa",
});
const response = await invokeAuthHandler("startOAuth", ctx);
expect(response.status).toBe(302);
const location = new URL(response.headers.get("location")!);
expect(location.hostname).toBe("accounts.google.com");
expect(location.searchParams.get("client_id")).toBe("runtime-client");
expect(location.searchParams.get("redirect_uri")).toBe("https://sso.example/api/auth/oauth/google/callback");
expect(location.searchParams.get("redirect_uri")).toBe(
"https://sso.example/api/auth/oauth/google/callback",
);
expect(getDefaultAuthRouteOptions().sessionCookieName).toBe("custom_session");
const request = new Request("https://sso.example/api/auth/passkeys/login/options", { method: "POST",
headers: { "content-type": "application/json", origin: "https://sso.example", cookie: "wrn-csrf=runtime-test-csrf", "x-csrf-token": "runtime-test-csrf" }, body: "{}" });
const result = await invokeAuthHandler("passkeyAuthenticationOptions", createContext(request, new URL(request.url)));
const request = new Request("https://sso.example/api/auth/passkeys/login/options", {
method: "POST",
headers: {
"content-type": "application/json",
origin: "https://sso.example",
cookie: "wrn-csrf=runtime-test-csrf",
"x-csrf-token": "runtime-test-csrf",
},
body: "{}",
});
const result = await invokeAuthHandler(
"passkeyAuthenticationOptions",
createContext(request, new URL(request.url)),
);
expect(result.status).toBe(200);
expect((await result.json()).options.rpId).toBe("sso.example");
configureAuthRuntime({ enabled: false, engine });