@cboxdk/id-js 0.19.0 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +185 -0
- package/README.md +24 -3
- package/RELEASE-CANDIDATE.md +11 -0
- package/dist/api-keys-BE3R3q_A.d.ts +105 -0
- package/dist/api-keys-C8-Q4eFA.d.cts +105 -0
- package/dist/chunk-EVZ6RB3K.js +98 -0
- package/dist/chunk-EVZ6RB3K.js.map +1 -0
- package/dist/decisions-C84Fzah4.d.ts +23 -0
- package/dist/decisions-DNd9UXwi.d.cts +23 -0
- package/dist/index.cjs +127 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +24 -4
- package/dist/index.js.map +1 -1
- package/dist/management.cjs +352 -19
- package/dist/management.cjs.map +1 -1
- package/dist/management.d.cts +994 -42
- package/dist/management.d.ts +994 -42
- package/dist/management.js +352 -19
- package/dist/management.js.map +1 -1
- package/dist/nextjs.d.cts +1 -1
- package/dist/nextjs.d.ts +1 -1
- package/dist/server.cjs +103 -9
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +3 -105
- package/dist/server.d.ts +3 -105
- package/dist/server.js +1 -0
- package/package.json +8 -3
package/dist/index.cjs
CHANGED
|
@@ -675,11 +675,11 @@ function parseActor(claim, depth) {
|
|
|
675
675
|
if (typeof claim !== "object" || Array.isArray(claim)) {
|
|
676
676
|
return { sub: null, actor: null };
|
|
677
677
|
}
|
|
678
|
-
const
|
|
679
|
-
const sub =
|
|
678
|
+
const record2 = claim;
|
|
679
|
+
const sub = record2["sub"];
|
|
680
680
|
return {
|
|
681
681
|
sub: typeof sub === "string" && sub !== "" ? sub : null,
|
|
682
|
-
actor: depth + 1 < MAX_ACTOR_DEPTH ? parseActor(
|
|
682
|
+
actor: depth + 1 < MAX_ACTOR_DEPTH ? parseActor(record2["act"], depth + 1) : null
|
|
683
683
|
};
|
|
684
684
|
}
|
|
685
685
|
function stringList(claim) {
|
|
@@ -944,9 +944,9 @@ function header(headers, name) {
|
|
|
944
944
|
if (typeof Headers !== "undefined" && headers instanceof Headers) {
|
|
945
945
|
return headers.get(name) ?? void 0;
|
|
946
946
|
}
|
|
947
|
-
const
|
|
948
|
-
const key = Object.keys(
|
|
949
|
-
const value = key === void 0 ? void 0 :
|
|
947
|
+
const record2 = headers;
|
|
948
|
+
const key = Object.keys(record2).find((k) => k.toLowerCase() === name);
|
|
949
|
+
const value = key === void 0 ? void 0 : record2[key];
|
|
950
950
|
return Array.isArray(value) ? value.join(" ") : value;
|
|
951
951
|
}
|
|
952
952
|
function base64ToBytes(value) {
|
|
@@ -1644,11 +1644,11 @@ function parseOrganizations(claim) {
|
|
|
1644
1644
|
if (typeof entry !== "object" || entry === null) {
|
|
1645
1645
|
continue;
|
|
1646
1646
|
}
|
|
1647
|
-
const
|
|
1648
|
-
const id =
|
|
1649
|
-
const name =
|
|
1647
|
+
const record2 = entry;
|
|
1648
|
+
const id = record2["id"];
|
|
1649
|
+
const name = record2["name"];
|
|
1650
1650
|
if (typeof id === "string" && id !== "" && typeof name === "string" && name !== "") {
|
|
1651
|
-
orgs.push({ id, name, role: typeof
|
|
1651
|
+
orgs.push({ id, name, role: typeof record2["role"] === "string" ? record2["role"] : null });
|
|
1652
1652
|
}
|
|
1653
1653
|
}
|
|
1654
1654
|
return orgs;
|
|
@@ -1702,6 +1702,11 @@ async function publishManifest(config, declaration) {
|
|
|
1702
1702
|
throw new ConfigurationError("Publishing a manifest needs issuer, clientId and clientSecret.");
|
|
1703
1703
|
}
|
|
1704
1704
|
const timeoutMs = config.timeoutMs ?? 1e4;
|
|
1705
|
+
assertSecureIssuer(config.issuer);
|
|
1706
|
+
const issuer = new URL(config.issuer);
|
|
1707
|
+
if (issuer.username || issuer.password || issuer.search || issuer.hash) {
|
|
1708
|
+
throw new ConfigurationError("The manifest issuer must not contain credentials, a query or a fragment.");
|
|
1709
|
+
}
|
|
1705
1710
|
const manifest = await buildManifest(declaration);
|
|
1706
1711
|
const discovery = new Discovery(config.issuer, timeoutMs, 36e5);
|
|
1707
1712
|
const token = await mintToken(config, discovery, timeoutMs);
|
|
@@ -1709,6 +1714,7 @@ async function publishManifest(config, declaration) {
|
|
|
1709
1714
|
`${config.issuer.replace(/\/$/, "")}/api/v1/apps/manifest`,
|
|
1710
1715
|
{
|
|
1711
1716
|
method: "POST",
|
|
1717
|
+
redirect: "error",
|
|
1712
1718
|
headers: {
|
|
1713
1719
|
authorization: `Bearer ${token}`,
|
|
1714
1720
|
accept: "application/json",
|
|
@@ -1719,10 +1725,20 @@ async function publishManifest(config, declaration) {
|
|
|
1719
1725
|
timeoutMs
|
|
1720
1726
|
);
|
|
1721
1727
|
if (!response.ok) {
|
|
1722
|
-
throw new AuthenticationError(`Manifest push failed: HTTP ${response.status}
|
|
1728
|
+
throw new AuthenticationError(`Manifest push failed: HTTP ${response.status}`);
|
|
1729
|
+
}
|
|
1730
|
+
const json2 = await response.json().catch(() => {
|
|
1731
|
+
throw new AuthenticationError("Cbox ID returned an invalid manifest acknowledgement.");
|
|
1732
|
+
});
|
|
1733
|
+
const checksum = await sha256Hex(canonicalManifestJson(declaration.permissions ?? [], declaration.roles ?? []));
|
|
1734
|
+
if (json2 === null || typeof json2 !== "object" || Array.isArray(json2)) {
|
|
1735
|
+
throw new AuthenticationError("Cbox ID did not acknowledge the authorization manifest.");
|
|
1723
1736
|
}
|
|
1724
|
-
const
|
|
1725
|
-
|
|
1737
|
+
const summary = json2;
|
|
1738
|
+
if (summary.client_id !== config.clientId || summary.version !== manifest.version || summary.checksum !== checksum || typeof summary.unchanged !== "boolean" || !Number.isInteger(summary.roles_declared) || !Number.isInteger(summary.permissions_declared) || (summary.roles_declared ?? -1) < 0 || (summary.permissions_declared ?? -1) < 0 || !summary.unchanged && (summary.roles_declared !== manifest.roles.length || summary.permissions_declared !== manifest.permissions.length)) {
|
|
1739
|
+
throw new AuthenticationError("Cbox ID did not acknowledge this app and its complete authorization manifest.");
|
|
1740
|
+
}
|
|
1741
|
+
return summary;
|
|
1726
1742
|
}
|
|
1727
1743
|
async function mintToken(config, discovery, timeoutMs) {
|
|
1728
1744
|
const response = await fetchWithTimeout(
|
|
@@ -1730,6 +1746,7 @@ async function mintToken(config, discovery, timeoutMs) {
|
|
|
1730
1746
|
{
|
|
1731
1747
|
method: "POST",
|
|
1732
1748
|
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
1749
|
+
redirect: "error",
|
|
1733
1750
|
body: new URLSearchParams({
|
|
1734
1751
|
grant_type: "client_credentials",
|
|
1735
1752
|
client_id: config.clientId,
|
|
@@ -1740,7 +1757,7 @@ async function mintToken(config, discovery, timeoutMs) {
|
|
|
1740
1757
|
timeoutMs
|
|
1741
1758
|
);
|
|
1742
1759
|
const json2 = await response.json().catch(() => ({}));
|
|
1743
|
-
if (typeof json2.access_token !== "string" || json2.access_token === "") {
|
|
1760
|
+
if (!response.ok || typeof json2.access_token !== "string" || json2.access_token === "") {
|
|
1744
1761
|
throw new AuthenticationError(
|
|
1745
1762
|
"Could not obtain an apps.manifest access token \u2014 check the client credentials and that the client holds the apps.manifest scope."
|
|
1746
1763
|
);
|
|
@@ -1862,6 +1879,8 @@ async function fetchWithTimeout(url, init, timeoutMs) {
|
|
|
1862
1879
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
1863
1880
|
try {
|
|
1864
1881
|
return await fetch(url, { ...init, signal: controller.signal });
|
|
1882
|
+
} catch {
|
|
1883
|
+
throw new AuthenticationError("Cbox ID manifest publication could not be verified.");
|
|
1865
1884
|
} finally {
|
|
1866
1885
|
clearTimeout(timer);
|
|
1867
1886
|
}
|
|
@@ -1875,6 +1894,98 @@ function assertLegacyLogin(legacy) {
|
|
|
1875
1894
|
}
|
|
1876
1895
|
}
|
|
1877
1896
|
|
|
1897
|
+
// src/decisions.ts
|
|
1898
|
+
var PermissionDecision = class {
|
|
1899
|
+
constructor(subject, organization2, clientId, organizationRole, organizationActive, permissions2) {
|
|
1900
|
+
this.subject = subject;
|
|
1901
|
+
this.organization = organization2;
|
|
1902
|
+
this.clientId = clientId;
|
|
1903
|
+
this.organizationRole = organizationRole;
|
|
1904
|
+
this.organizationActive = organizationActive;
|
|
1905
|
+
this.permissions = permissions2;
|
|
1906
|
+
}
|
|
1907
|
+
subject;
|
|
1908
|
+
organization;
|
|
1909
|
+
clientId;
|
|
1910
|
+
organizationRole;
|
|
1911
|
+
organizationActive;
|
|
1912
|
+
permissions;
|
|
1913
|
+
allows(permission) {
|
|
1914
|
+
return this.organizationActive && this.permissions[permission] === true;
|
|
1915
|
+
}
|
|
1916
|
+
allowed() {
|
|
1917
|
+
const values = Object.values(this.permissions);
|
|
1918
|
+
return this.organizationActive && values.length > 0 && values.every((value) => value === true);
|
|
1919
|
+
}
|
|
1920
|
+
};
|
|
1921
|
+
async function checkPermissions(config, accessToken, subject, organization2, permissions2) {
|
|
1922
|
+
assertSecureIssuer(config.issuer);
|
|
1923
|
+
const issuer = new URL(config.issuer);
|
|
1924
|
+
if (issuer.username || issuer.password || issuer.search || issuer.hash || !config.clientId || !subject || organization2 === "" || !accessToken || accessToken.length > 32768 || /[\x00-\x20\x7f]/.test(accessToken) || !permissions2.length || permissions2.length > 50 || permissions2.some((permission) => !permission.trim() || new TextEncoder().encode(permission).length > 255)) {
|
|
1925
|
+
throw new ConfigurationError("Permission decision request is not configured or valid.");
|
|
1926
|
+
}
|
|
1927
|
+
const requested = [...new Set(permissions2)];
|
|
1928
|
+
try {
|
|
1929
|
+
const response = await fetch(`${config.issuer.replace(/\/$/, "")}/oauth/decisions`, {
|
|
1930
|
+
method: "POST",
|
|
1931
|
+
redirect: "error",
|
|
1932
|
+
cache: "no-store",
|
|
1933
|
+
signal: AbortSignal.timeout(Math.max(1, config.timeoutMs ?? 1e4)),
|
|
1934
|
+
headers: { authorization: `Bearer ${accessToken}`, accept: "application/json", "content-type": "application/json" },
|
|
1935
|
+
body: JSON.stringify({ subject, org: organization2, permission: requested })
|
|
1936
|
+
});
|
|
1937
|
+
if (response.status !== 200) {
|
|
1938
|
+
await response.body?.cancel();
|
|
1939
|
+
throw new AuthenticationError("Permission decision could not be verified.", void 0, void 0, response.status);
|
|
1940
|
+
}
|
|
1941
|
+
const reader = response.body?.getReader();
|
|
1942
|
+
if (!reader) throw new AuthenticationError("Permission decision response is invalid.");
|
|
1943
|
+
let size = 0;
|
|
1944
|
+
let raw = "";
|
|
1945
|
+
const decoder = new TextDecoder();
|
|
1946
|
+
try {
|
|
1947
|
+
for (; ; ) {
|
|
1948
|
+
const { value, done } = await reader.read();
|
|
1949
|
+
if (done) break;
|
|
1950
|
+
size += value.byteLength;
|
|
1951
|
+
if (size > 65536) throw new AuthenticationError("Permission decision response exceeded its limit.");
|
|
1952
|
+
raw += decoder.decode(value, { stream: true });
|
|
1953
|
+
}
|
|
1954
|
+
raw += decoder.decode();
|
|
1955
|
+
} finally {
|
|
1956
|
+
await reader.cancel();
|
|
1957
|
+
reader.releaseLock();
|
|
1958
|
+
}
|
|
1959
|
+
const data = JSON.parse(raw);
|
|
1960
|
+
if (!record(data) || !record(data.subject) || data.mode !== "rbac" || data.subject.type !== "user" || data.subject.id !== subject || data.organization !== organization2 || data.client_id !== config.clientId || typeof data.organization_active !== "boolean" || typeof data.allowed !== "boolean" || !(data.org_role === null || typeof data.org_role === "string" && ["owner", "admin", "developer", "member", "viewer"].includes(data.org_role)) || !Array.isArray(data.results) || data.results.length !== requested.length) {
|
|
1961
|
+
throw new AuthenticationError("Permission decision response binding is invalid.");
|
|
1962
|
+
}
|
|
1963
|
+
const checks = /* @__PURE__ */ Object.create(null);
|
|
1964
|
+
for (const result of data.results) {
|
|
1965
|
+
if (!record(result) || typeof result.permission !== "string" || typeof result.allowed !== "boolean" || !requested.includes(result.permission) || Object.hasOwn(checks, result.permission)) {
|
|
1966
|
+
throw new AuthenticationError("Permission decision result is invalid.");
|
|
1967
|
+
}
|
|
1968
|
+
checks[result.permission] = result.allowed;
|
|
1969
|
+
}
|
|
1970
|
+
const decision = new PermissionDecision(
|
|
1971
|
+
subject,
|
|
1972
|
+
organization2,
|
|
1973
|
+
config.clientId,
|
|
1974
|
+
data.org_role,
|
|
1975
|
+
data.organization_active,
|
|
1976
|
+
Object.freeze(checks)
|
|
1977
|
+
);
|
|
1978
|
+
if (data.allowed !== decision.allowed()) throw new AuthenticationError("Permission decision result is inconsistent.");
|
|
1979
|
+
return decision;
|
|
1980
|
+
} catch (error) {
|
|
1981
|
+
if (error instanceof AuthenticationError) throw error;
|
|
1982
|
+
throw new AuthenticationError("Permission decision could not be verified.");
|
|
1983
|
+
}
|
|
1984
|
+
}
|
|
1985
|
+
function record(value) {
|
|
1986
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1987
|
+
}
|
|
1988
|
+
|
|
1878
1989
|
exports.AuthenticationError = AuthenticationError;
|
|
1879
1990
|
exports.CboxIdClient = CboxIdClient;
|
|
1880
1991
|
exports.CboxIdError = CboxIdError;
|
|
@@ -1883,6 +1994,7 @@ exports.ConfigurationError = ConfigurationError;
|
|
|
1883
1994
|
exports.FEATURE_FLAGS_SCOPE = FEATURE_FLAGS_SCOPE;
|
|
1884
1995
|
exports.FrontendApiError = FrontendApiError;
|
|
1885
1996
|
exports.InvalidStateError = InvalidStateError;
|
|
1997
|
+
exports.PermissionDecision = PermissionDecision;
|
|
1886
1998
|
exports.PipeLeaseDeniedError = PipeLeaseDeniedError;
|
|
1887
1999
|
exports.PipeLeaseError = PipeLeaseError;
|
|
1888
2000
|
exports.PipeNotConnectedError = PipeNotConnectedError;
|
|
@@ -1892,6 +2004,7 @@ exports.VaultClient = VaultClient;
|
|
|
1892
2004
|
exports.actor = actor;
|
|
1893
2005
|
exports.buildManifest = buildManifest;
|
|
1894
2006
|
exports.challenge = challenge;
|
|
2007
|
+
exports.checkPermissions = checkPermissions;
|
|
1895
2008
|
exports.createLegacyVerifier = createLegacyVerifier;
|
|
1896
2009
|
exports.createVerifier = createVerifier;
|
|
1897
2010
|
exports.defineAuthz = defineAuthz;
|