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
This commit is contained in:
40
tests/auth/saml.test.js
Normal file
40
tests/auth/saml.test.js
Normal file
@@ -0,0 +1,40 @@
|
||||
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");
|
||||
});
|
||||
Reference in New Issue
Block a user