| 1 | import assert from "node:assert/strict"; |
| 2 | import { test } from "node:test"; |
| 3 | |
| 4 | import { ssoAddresses } from "@g1t/contracts/sso"; |
| 5 | |
| 6 | import { SSO_ERROR_COOKIE, afterSso, bindingCookie, errorCookie, readBinding, readErrorCookie, samlPath } from "./sso.ts"; |
| 7 | |
| 8 | test("only a workspace's SAML address is answered before React Router", () => { |
| 9 | assert.equal(samlPath("/auth/sso/acme/saml"), "acme"); |
| 10 | assert.equal(samlPath("/auth/sso/Acme-Co/saml/"), "acme-co"); |
| 11 | assert.equal(samlPath("/auth/sso/wsp_01m54abc/saml"), "wsp_01m54abc"); |
| 12 | assert.equal(samlPath("/auth/sso/acme/callback"), null); |
| 13 | assert.equal(samlPath("/auth/sso/acme/saml/extra"), null); |
| 14 | assert.equal(samlPath("/auth/sso/-acme/saml"), null); |
| 15 | assert.equal(samlPath("/acme/-/sso"), null); |
| 16 | }); |
| 17 | |
| 18 | test("afterwards it goes on to the workspace, never off the site", () => { |
| 19 | assert.equal(afterSso({ slug: "acme", next: "/" }), "/acme"); |
| 20 | assert.equal(afterSso({ slug: "acme", next: "/acme/-/sso" }), "/acme/-/sso"); |
| 21 | assert.equal(afterSso({ slug: "acme", next: "//evil.test" }), "/acme"); |
| 22 | assert.equal(afterSso({ slug: "acme", next: "https://evil.test/" }), "/acme"); |
| 23 | }); |
| 24 | |
| 25 | test("why a SAML sign-in failed survives the trip in a cookie", () => { |
| 26 | const set = errorCookie("The assertion is not for g1t; set the audience = x."); |
| 27 | assert.match(set, /HttpOnly; Secure; SameSite=Lax; Max-Age=60/); |
| 28 | const value = set.split(";")[0]; |
| 29 | assert.equal(readErrorCookie(`a=b; ${value}`), "The assertion is not for g1t; set the audience = x."); |
| 30 | assert.equal(readErrorCookie(`${SSO_ERROR_COOKIE}=%E0%A4%A`), null); |
| 31 | assert.equal(readErrorCookie(null), null); |
| 32 | }); |
| 33 | |
| 34 | test("a workspace's provider is pointed at its id, which a rename never changes", () => { |
| 35 | assert.deepEqual(ssoAddresses("https://g1t.sh/", "wsp_1", "Acme"), { |
| 36 | entityId: "https://g1t.sh/auth/sso/wsp_1/metadata", |
| 37 | metadataUrl: "https://g1t.sh/auth/sso/wsp_1/metadata", |
| 38 | acsUrl: "https://g1t.sh/auth/sso/wsp_1/saml", |
| 39 | callbackUrl: "https://g1t.sh/auth/sso/wsp_1/callback", |
| 40 | signInUrl: "https://g1t.sh/auth/sso/acme", |
| 41 | }); |
| 42 | }); |
| 43 | |
| 44 | test("the SAML binding cookie crosses sites to /auth/sso alone", () => { |
| 45 | const value = "a".repeat(64); |
| 46 | assert.match(bindingCookie(value), /Path=\/auth\/sso; HttpOnly; Secure; SameSite=None; Max-Age=600/); |
| 47 | assert.equal(readBinding(`x=1; ${bindingCookie(value).split(";")[0]}`), value); |
| 48 | assert.equal(readBinding("g1t_sso_saml=nothex"), null); |
| 49 | assert.match(bindingCookie(""), /Max-Age=0/); |
| 50 | }); |