Files
9router/tests/auth/saml.test.js
Duc Nguyen 65197ad11c feat(auth): add native SAML 2.0 SSO integration
Add SAML 2.0 as a second SSO protocol alongside OIDC under a unified
authMode/ssoType model. SP flows via @node-saml/node-saml: AuthnRequest
generation, ACS POST assertion handling, SP metadata export, and admin
config test endpoint. Replay-protected via saml_state cookie (httpOnly,
SameSite=Lax) matched against InResponseTo; wantAssertionsSigned enforced.

- src/lib/auth/saml.js: SAML instance builder, X.509 cert formatter, claim pickers
- 4 routes under src/app/api/auth/saml/: start, acs, metadata, test
- settingsRepo: ssoType + saml* defaults; login/status routes dispatch by type
- profile page: SSO protocol switcher, IdP metadata XML + cert uploaders
- login page: dynamic SAML sign-in button; Header: SAML user badge
2026-08-13 17:56:34 +07:00

41 lines
1.6 KiB
JavaScript

import test from "node:test";
import assert from "node:assert/strict";
import {
formatX509Certificate,
isSamlConfigured,
generateSamlMetadata,
pickSamlEmail,
pickSamlDisplayName,
} from "../../src/lib/auth/saml.js";
test("formatX509Certificate normalizes Base64 strings into PEM blocks", () => {
const rawBase64 = "MIIC1234567890123456789012345678901234567890123456789012345678901234567890";
const formatted = formatX509Certificate(rawBase64);
assert.match(formatted, /-----BEGIN CERTIFICATE-----/);
assert.match(formatted, /-----END CERTIFICATE-----/);
assert.equal(formatX509Certificate(""), "");
});
test("isSamlConfigured checks required fields", () => {
assert.equal(isSamlConfigured({ samlEntryPoint: "https://idp.com/sso", samlCert: "cert" }), true);
assert.equal(isSamlConfigured({ samlEntryPoint: "https://idp.com/sso" }), false);
assert.equal(isSamlConfigured({}), false);
});
test("generateSamlMetadata produces valid SP XML", () => {
const settings = {
samlEntryPoint: "https://idp.example.com/sso",
samlIssuer: "urn:9router:sp",
samlCert: "MIIC123456789012345678901234567890123456789012345678901234567890",
};
const xml = generateSamlMetadata("https://localhost:20127", settings);
assert.match(xml, /entityID="urn:9router:sp"/);
assert.match(xml, /Location="https:\/\/localhost:20127\/api\/auth\/saml\/acs"/);
});
test("Claims Extraction pickSamlEmail & pickSamlDisplayName", () => {
const profile = { email: "test@example.com", name: "Test User" };
assert.equal(pickSamlEmail(profile, {}), "test@example.com");
assert.equal(pickSamlDisplayName(profile, {}), "Test User");
});