@iqauth/sdk 2.6.4 → 2.8.1
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/README.md +173 -1
- package/dist/browser-session.d.mts +4 -4
- package/dist/browser-session.d.ts +4 -4
- package/dist/browser-session.js +212 -46
- package/dist/browser-session.mjs +3 -3
- package/dist/browser.d.mts +5 -5
- package/dist/browser.d.ts +5 -5
- package/dist/browser.js +293 -34
- package/dist/browser.mjs +5 -5
- package/dist/{chunk-BVV54LPI.mjs → chunk-25SSYDIP.mjs} +10 -4
- package/dist/{chunk-XAWYUPMO.mjs → chunk-4V7FKOTG.mjs} +242 -22
- package/dist/{chunk-6I6RM4MN.mjs → chunk-6PJRLRB4.mjs} +33 -3
- package/dist/{chunk-SL3KRS4W.mjs → chunk-CIJORODR.mjs} +23 -1
- package/dist/{chunk-LIZYFXH7.mjs → chunk-DFWHSDYQ.mjs} +1 -1
- package/dist/chunk-GLXSIGVS.mjs +66 -0
- package/dist/{chunk-DJIBN2N7.mjs → chunk-GN37E64I.mjs} +29 -7
- package/dist/{chunk-WQWBJSSS.mjs → chunk-HVHNYPDC.mjs} +6 -6
- package/dist/chunk-JRDVUWAL.mjs +46 -0
- package/dist/{chunk-UNYDG2L4.mjs → chunk-NUO2I65G.mjs} +56 -23
- package/dist/{chunk-5T7GHBX6.mjs → chunk-TLET552H.mjs} +36 -0
- package/dist/chunk-VYQ3ETCK.mjs +244 -0
- package/dist/{chunk-3JULWS6F.mjs → chunk-WCELYTJ3.mjs} +3 -3
- package/dist/chunk-WHT6WKTY.mjs +3180 -0
- package/dist/{chunk-MKKZULZR.mjs → chunk-WIFG74IK.mjs} +1 -1
- package/dist/chunk-WSH4SW7F.mjs +490 -0
- package/dist/{chunk-W3F4JYGP.mjs → chunk-ZLJPABB7.mjs} +139 -23
- package/dist/cli/index.js +2 -2
- package/dist/cli/index.mjs +2 -2
- package/dist/{client-BNQe3AgF.d.ts → client-D8L-PaWr.d.mts} +59 -6
- package/dist/{client-kYlJFgPv.d.mts → client-DkPL0EPZ.d.ts} +59 -6
- package/dist/{doctor-YYNHNMLD.mjs → doctor-JAFXWU3X.mjs} +2 -2
- package/dist/errors-Jl1Jtm-6.d.mts +107 -0
- package/dist/errors-Jl1Jtm-6.d.ts +107 -0
- package/dist/{express-CHpfa7D_.d.ts → express-Budysq4h.d.ts} +2 -2
- package/dist/{express-B6_1vBYZ.d.mts → express-DDTA3qV1.d.mts} +2 -2
- package/dist/express.d.mts +7 -6
- package/dist/express.d.ts +7 -6
- package/dist/express.js +563 -85
- package/dist/express.mjs +73 -34
- package/dist/fastify.d.mts +10 -0
- package/dist/fastify.d.ts +10 -0
- package/dist/fastify.js +589 -65
- package/dist/fastify.mjs +101 -11
- package/dist/hono.d.mts +10 -0
- package/dist/hono.d.ts +10 -0
- package/dist/hono.js +566 -65
- package/dist/hono.mjs +78 -11
- package/dist/index-Cko-d5po.d.mts +1848 -0
- package/dist/index-RNqwEcmY.d.ts +1848 -0
- package/dist/index.d.mts +56 -8
- package/dist/index.d.ts +56 -8
- package/dist/index.js +694 -75
- package/dist/index.mjs +30 -10
- package/dist/{keys-NLWFAOEM.mjs → keys-6Y776TG2.mjs} +2 -2
- package/dist/locales.d.mts +1 -1
- package/dist/locales.d.ts +1 -1
- package/dist/locales.js +36 -0
- package/dist/locales.mjs +1 -1
- package/dist/mobile.d.mts +77 -7
- package/dist/mobile.d.ts +77 -7
- package/dist/mobile.js +307 -46
- package/dist/mobile.mjs +98 -3
- package/dist/next.d.mts +10 -1
- package/dist/next.d.ts +10 -1
- package/dist/next.js +596 -205
- package/dist/next.mjs +83 -10
- package/dist/{provisioningBridge-88xjOS2n.d.mts → provisioningBridge-BXPMZCLe.d.ts} +30 -2
- package/dist/{provisioningBridge-DnTfzdZK.d.ts → provisioningBridge-IEycmsgb.d.mts} +30 -2
- package/dist/{publishableKey-BaR0HoAH.d.ts → publishableKey-f2kq-rKw.d.mts} +1 -1
- package/dist/{publishableKey-BaR0HoAH.d.mts → publishableKey-f2kq-rKw.d.ts} +1 -1
- package/dist/react-permissions.d.mts +52 -0
- package/dist/react-permissions.d.ts +52 -0
- package/dist/react-permissions.js +239 -0
- package/dist/react-permissions.mjs +98 -0
- package/dist/react.d.mts +9 -1624
- package/dist/react.d.ts +9 -1624
- package/dist/react.js +882 -73
- package/dist/react.mjs +71 -2631
- package/dist/{reverify-4UEJXUS6.mjs → reverify-C64QXKJO.mjs} +2 -2
- package/dist/server/handlers.d.mts +200 -4
- package/dist/server/handlers.d.ts +200 -4
- package/dist/server/handlers.js +530 -16
- package/dist/server/handlers.mjs +14 -3
- package/dist/server.d.mts +171 -8
- package/dist/server.d.ts +171 -8
- package/dist/server.js +579 -61
- package/dist/server.mjs +99 -12
- package/dist/service.d.mts +4 -4
- package/dist/service.d.ts +4 -4
- package/dist/service.js +212 -46
- package/dist/service.mjs +3 -3
- package/dist/{signIn-CiIBTJIh.d.mts → signIn-CReqfXsh.d.mts} +95 -3
- package/dist/{signIn-OCr88Zf8.d.ts → signIn-Cfa1GTpO.d.ts} +95 -3
- package/dist/{signIn-4OKLDEIH.mjs → signIn-SHBW6Z4T.mjs} +1 -1
- package/dist/test.mjs +3 -3
- package/dist/{tokens-DCyzzn8L.d.mts → tokens-9F6ETrzk.d.ts} +9 -2
- package/dist/{tokens-aHiGFr_E.d.ts → tokens-B06VtvUi.d.mts} +9 -2
- package/dist/{types-DZAflmmq.d.mts → types-Bn8O-OEd.d.mts} +164 -11
- package/dist/{types-DZAflmmq.d.ts → types-Bn8O-OEd.d.ts} +164 -11
- package/dist/{types-6bNdxesb.d.ts → types-DnU2LhXR.d.mts} +7 -1
- package/dist/{types-6bNdxesb.d.mts → types-DnU2LhXR.d.ts} +7 -1
- package/dist/webhooks.d.mts +113 -17
- package/dist/webhooks.d.ts +113 -17
- package/dist/webhooks.js +179 -15
- package/dist/webhooks.mjs +7 -1
- package/dist/ws.d.mts +2 -2
- package/dist/ws.d.ts +2 -2
- package/dist/ws.js +80 -30
- package/dist/ws.mjs +4 -4
- package/docs/error-handling.md +101 -0
- package/docs/guides/effective-permissions.md +171 -0
- package/docs/guides/invitations.md +65 -0
- package/package.json +19 -4
- package/dist/chunk-6TDJJER7.mjs +0 -217
- package/dist/chunk-UKZLOHZG.mjs +0 -83
- package/dist/errors-CDdl24MP.d.mts +0 -52
- package/dist/errors-CDdl24MP.d.ts +0 -52
package/dist/next.mjs
CHANGED
|
@@ -1,19 +1,24 @@
|
|
|
1
|
+
import {
|
|
2
|
+
sanitizeReturnTo
|
|
3
|
+
} from "./chunk-JRDVUWAL.mjs";
|
|
1
4
|
import {
|
|
2
5
|
handleCallback,
|
|
3
6
|
handleRefresh,
|
|
4
7
|
handleSignout,
|
|
8
|
+
handleUserinfo,
|
|
5
9
|
serializeCookie
|
|
6
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-WSH4SW7F.mjs";
|
|
7
11
|
import {
|
|
8
12
|
assertPublishableKey
|
|
9
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-HVHNYPDC.mjs";
|
|
10
14
|
import {
|
|
11
15
|
TokensModule
|
|
12
|
-
} from "./chunk-
|
|
13
|
-
import "./chunk-
|
|
16
|
+
} from "./chunk-NUO2I65G.mjs";
|
|
17
|
+
import "./chunk-6PJRLRB4.mjs";
|
|
14
18
|
import "./chunk-Y6FXYEAI.mjs";
|
|
15
19
|
|
|
16
20
|
// src/next.ts
|
|
21
|
+
var PKCE_COOKIE = "iqauth_pkce";
|
|
17
22
|
function readCookieFromHeader(header, name) {
|
|
18
23
|
if (!header) return void 0;
|
|
19
24
|
const target = `${name}=`;
|
|
@@ -34,32 +39,100 @@ function toResponse(hr) {
|
|
|
34
39
|
for (const c of hr.cookies) headers.append("set-cookie", serializeCookie(c));
|
|
35
40
|
return new Response(JSON.stringify(hr.body), { status: hr.status, headers });
|
|
36
41
|
}
|
|
42
|
+
function callbackResponse(hr, requestOrigin, returnToCookieValue, returnToCookieName) {
|
|
43
|
+
const returnTo = sanitizeReturnTo(
|
|
44
|
+
returnToCookieValue || hr.body?.returnTo,
|
|
45
|
+
{ currentOrigin: requestOrigin, fallback: "/" }
|
|
46
|
+
);
|
|
47
|
+
const headers = new Headers({ "Content-Type": "application/json" });
|
|
48
|
+
for (const c of hr.cookies) headers.append("set-cookie", serializeCookie(c));
|
|
49
|
+
if (hr.status < 400) {
|
|
50
|
+
headers.append("set-cookie", `${returnToCookieName}=; Path=/; Max-Age=0; SameSite=Lax`);
|
|
51
|
+
}
|
|
52
|
+
const body = { ...hr.body, returnTo };
|
|
53
|
+
return new Response(JSON.stringify(body), { status: hr.status, headers });
|
|
54
|
+
}
|
|
55
|
+
function callbackRedirectResponse(hr, requestOrigin, returnToCookieValue, cookieNames) {
|
|
56
|
+
const headers = new Headers();
|
|
57
|
+
for (const c of hr.cookies) headers.append("set-cookie", serializeCookie(c));
|
|
58
|
+
headers.append("set-cookie", `${cookieNames.state}=; Path=/; Max-Age=0; SameSite=Lax`);
|
|
59
|
+
headers.append("set-cookie", `${cookieNames.pkce}=; Path=/; Max-Age=0; SameSite=Lax`);
|
|
60
|
+
if (hr.status >= 400) {
|
|
61
|
+
headers.set("Location", "/");
|
|
62
|
+
return new Response(null, { status: 302, headers });
|
|
63
|
+
}
|
|
64
|
+
const dest = sanitizeReturnTo(returnToCookieValue, {
|
|
65
|
+
currentOrigin: requestOrigin,
|
|
66
|
+
fallback: "/"
|
|
67
|
+
});
|
|
68
|
+
headers.append("set-cookie", `${cookieNames.returnTo}=; Path=/; Max-Age=0; SameSite=Lax`);
|
|
69
|
+
headers.set("Location", dest);
|
|
70
|
+
return new Response(null, { status: 302, headers });
|
|
71
|
+
}
|
|
37
72
|
function handler(options) {
|
|
38
73
|
const parsed = assertPublishableKey(options.publishableKey, { context: "@iqauth/sdk/next handler" });
|
|
39
74
|
const issuer = (options.issuer ?? (parsed.iss.startsWith("http") ? parsed.iss : `https://${parsed.iss}`)).replace(/\/+$/, "");
|
|
40
75
|
const helperConfig = { ...options, issuer };
|
|
41
76
|
const accessCookie = options.accessCookieName ?? "iqauth_at";
|
|
42
77
|
const refreshCookie = options.refreshCookieName ?? "iqauth_rt";
|
|
78
|
+
const returnToCookie = options.returnToCookieName ?? "iqauth_return_to";
|
|
43
79
|
return async (req) => {
|
|
44
80
|
const url = new URL(req.url);
|
|
45
81
|
const action = url.pathname.split("/").pop();
|
|
46
|
-
const body = await req.json().catch(() => ({}));
|
|
47
82
|
const cookieHeader = req.headers.get("cookie");
|
|
83
|
+
if (action === "me" && req.method === "GET") {
|
|
84
|
+
if (!options.mountUserinfo) {
|
|
85
|
+
return new Response(JSON.stringify({ success: false, error: { code: "NOT_FOUND", message: "userinfo route not enabled" } }), {
|
|
86
|
+
status: 404,
|
|
87
|
+
headers: { "Content-Type": "application/json" }
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
const auth = req.headers.get("authorization");
|
|
91
|
+
const accessToken = auth && auth.replace(/^Bearer /i, "") || readCookieFromHeader(cookieHeader, accessCookie);
|
|
92
|
+
return toResponse(await handleUserinfo(helperConfig, { accessToken, req }));
|
|
93
|
+
}
|
|
94
|
+
const stateCookie = helperConfig.stateCookieName ?? "iqauth_state";
|
|
95
|
+
if (action === "callback" && req.method === "GET") {
|
|
96
|
+
const code = url.searchParams.get("code") ?? void 0;
|
|
97
|
+
const state = url.searchParams.get("state") ?? void 0;
|
|
98
|
+
const redirectUri = `${url.origin}${url.pathname}`;
|
|
99
|
+
const hr = await handleCallback(helperConfig, {
|
|
100
|
+
code,
|
|
101
|
+
codeVerifier: readCookieFromHeader(cookieHeader, PKCE_COOKIE),
|
|
102
|
+
redirectUri,
|
|
103
|
+
state,
|
|
104
|
+
expectedState: readCookieFromHeader(cookieHeader, stateCookie)
|
|
105
|
+
});
|
|
106
|
+
return callbackRedirectResponse(
|
|
107
|
+
hr,
|
|
108
|
+
url.origin,
|
|
109
|
+
readCookieFromHeader(cookieHeader, returnToCookie),
|
|
110
|
+
{ returnTo: returnToCookie, state: stateCookie, pkce: PKCE_COOKIE }
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
const body = await req.json().catch(() => ({}));
|
|
48
114
|
if (action === "callback") {
|
|
49
|
-
|
|
115
|
+
const hr = await handleCallback(helperConfig, {
|
|
50
116
|
code: body.code,
|
|
51
117
|
codeVerifier: body.codeVerifier,
|
|
52
|
-
redirectUri: body.redirectUri
|
|
53
|
-
|
|
118
|
+
redirectUri: body.redirectUri,
|
|
119
|
+
// M-2: bind callback to this browser; handleCallback fails closed.
|
|
120
|
+
state: body.state,
|
|
121
|
+
expectedState: readCookieFromHeader(cookieHeader, helperConfig.stateCookieName ?? "iqauth_state")
|
|
122
|
+
});
|
|
123
|
+
return callbackResponse(hr, url.origin, readCookieFromHeader(cookieHeader, returnToCookie), returnToCookie);
|
|
54
124
|
}
|
|
55
125
|
if (action === "refresh") {
|
|
56
126
|
const refreshToken = body.refreshToken || readCookieFromHeader(cookieHeader, refreshCookie);
|
|
57
|
-
|
|
127
|
+
const idempotencyToken = req.headers.get("x-iqauth-idempotency") || body.idempotencyToken;
|
|
128
|
+
return toResponse(await handleRefresh(helperConfig, { refreshToken, idempotencyToken: idempotencyToken ?? void 0 }));
|
|
58
129
|
}
|
|
59
130
|
if (action === "signout") {
|
|
60
131
|
const auth = req.headers.get("authorization");
|
|
61
132
|
const accessToken = auth && auth.replace(/^Bearer /i, "") || readCookieFromHeader(cookieHeader, accessCookie);
|
|
62
|
-
|
|
133
|
+
const refreshToken = readCookieFromHeader(cookieHeader, refreshCookie);
|
|
134
|
+
const idempotencyToken = req.headers.get("x-iqauth-idempotency") ?? void 0;
|
|
135
|
+
return toResponse(await handleSignout(helperConfig, { accessToken, refreshToken, idempotencyToken, ssoCookieHeader: cookieHeader ?? void 0 }));
|
|
63
136
|
}
|
|
64
137
|
return new Response(JSON.stringify({ success: false, error: { code: "NOT_FOUND", message: `Unknown action: ${action}` } }), {
|
|
65
138
|
status: 404,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { J as JwtClaims } from './types-
|
|
1
|
+
import { J as JwtClaims } from './types-Bn8O-OEd.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* createProvisioningBridge — server-side helper that lifts the
|
|
@@ -21,6 +21,17 @@ import { J as JwtClaims } from './types-DZAflmmq.mjs';
|
|
|
21
21
|
* your local user table. See the JSDoc on each adapter for the contract.
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Thrown when the bridge refuses to adopt a pre-existing local account because
|
|
26
|
+
* the IQAuth claims do not assert a verified email (`email_verified !== true`)
|
|
27
|
+
* and the integrator has not opted in via `allowUnverifiedEmailAdopt`. Carries
|
|
28
|
+
* a stable `.code` so callers can branch (e.g. provision a brand-new row under
|
|
29
|
+
* a different identity instead of adopting the email-matched one).
|
|
30
|
+
*/
|
|
31
|
+
declare class ProvisioningError extends Error {
|
|
32
|
+
code: string;
|
|
33
|
+
constructor(code: string, message: string);
|
|
34
|
+
}
|
|
24
35
|
interface ProvisioningContext<TUser> {
|
|
25
36
|
claims: JwtClaims;
|
|
26
37
|
/** The local user record, looked up or freshly inserted. */
|
|
@@ -59,6 +70,23 @@ interface ProvisioningBridgeOptions<TUser> {
|
|
|
59
70
|
* Defaults to checking for Postgres `23505` and SQLite `SQLITE_CONSTRAINT_UNIQUE`.
|
|
60
71
|
*/
|
|
61
72
|
isUniqueViolation?: (err: unknown) => boolean;
|
|
73
|
+
/**
|
|
74
|
+
* Security gate (H-4): adopting a pre-existing local row matched by email is
|
|
75
|
+
* only performed when the IQAuth claims assert a verified email
|
|
76
|
+
* (`claims.email_verified === true`). When the claim is absent/false and a
|
|
77
|
+
* local row matches the email, the bridge **refuses to adopt** and throws
|
|
78
|
+
* `ProvisioningError("UNVERIFIED_EMAIL_ADOPT_REFUSED")` (fail closed) so an
|
|
79
|
+
* IdP that does not assert email verification cannot silently take over an
|
|
80
|
+
* existing account.
|
|
81
|
+
*
|
|
82
|
+
* Brand-new users (no email-matched row) are unaffected — there is nothing
|
|
83
|
+
* to take over, so they are inserted regardless of verification status.
|
|
84
|
+
*
|
|
85
|
+
* Set `true` ONLY when your issuer is trusted to never emit an unverified
|
|
86
|
+
* email for adoption (or you have a compensating control). Defaults to
|
|
87
|
+
* `false` (secure).
|
|
88
|
+
*/
|
|
89
|
+
allowUnverifiedEmailAdopt?: boolean;
|
|
62
90
|
}
|
|
63
91
|
interface ProvisioningBridge<TUser> {
|
|
64
92
|
/**
|
|
@@ -83,4 +111,4 @@ interface ProvisioningBridge<TUser> {
|
|
|
83
111
|
*/
|
|
84
112
|
declare function createProvisioningBridge<TUser>(options: ProvisioningBridgeOptions<TUser>): ProvisioningBridge<TUser>;
|
|
85
113
|
|
|
86
|
-
export { type ProvisioningBridge as P, type ProvisioningBridgeOptions as a, type ProvisioningStorage as b, createProvisioningBridge as c, type ProvisioningContext as d };
|
|
114
|
+
export { type ProvisioningBridge as P, type ProvisioningBridgeOptions as a, type ProvisioningStorage as b, createProvisioningBridge as c, type ProvisioningContext as d, ProvisioningError as e };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { J as JwtClaims } from './types-
|
|
1
|
+
import { J as JwtClaims } from './types-Bn8O-OEd.mjs';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* createProvisioningBridge — server-side helper that lifts the
|
|
@@ -21,6 +21,17 @@ import { J as JwtClaims } from './types-DZAflmmq.js';
|
|
|
21
21
|
* your local user table. See the JSDoc on each adapter for the contract.
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Thrown when the bridge refuses to adopt a pre-existing local account because
|
|
26
|
+
* the IQAuth claims do not assert a verified email (`email_verified !== true`)
|
|
27
|
+
* and the integrator has not opted in via `allowUnverifiedEmailAdopt`. Carries
|
|
28
|
+
* a stable `.code` so callers can branch (e.g. provision a brand-new row under
|
|
29
|
+
* a different identity instead of adopting the email-matched one).
|
|
30
|
+
*/
|
|
31
|
+
declare class ProvisioningError extends Error {
|
|
32
|
+
code: string;
|
|
33
|
+
constructor(code: string, message: string);
|
|
34
|
+
}
|
|
24
35
|
interface ProvisioningContext<TUser> {
|
|
25
36
|
claims: JwtClaims;
|
|
26
37
|
/** The local user record, looked up or freshly inserted. */
|
|
@@ -59,6 +70,23 @@ interface ProvisioningBridgeOptions<TUser> {
|
|
|
59
70
|
* Defaults to checking for Postgres `23505` and SQLite `SQLITE_CONSTRAINT_UNIQUE`.
|
|
60
71
|
*/
|
|
61
72
|
isUniqueViolation?: (err: unknown) => boolean;
|
|
73
|
+
/**
|
|
74
|
+
* Security gate (H-4): adopting a pre-existing local row matched by email is
|
|
75
|
+
* only performed when the IQAuth claims assert a verified email
|
|
76
|
+
* (`claims.email_verified === true`). When the claim is absent/false and a
|
|
77
|
+
* local row matches the email, the bridge **refuses to adopt** and throws
|
|
78
|
+
* `ProvisioningError("UNVERIFIED_EMAIL_ADOPT_REFUSED")` (fail closed) so an
|
|
79
|
+
* IdP that does not assert email verification cannot silently take over an
|
|
80
|
+
* existing account.
|
|
81
|
+
*
|
|
82
|
+
* Brand-new users (no email-matched row) are unaffected — there is nothing
|
|
83
|
+
* to take over, so they are inserted regardless of verification status.
|
|
84
|
+
*
|
|
85
|
+
* Set `true` ONLY when your issuer is trusted to never emit an unverified
|
|
86
|
+
* email for adoption (or you have a compensating control). Defaults to
|
|
87
|
+
* `false` (secure).
|
|
88
|
+
*/
|
|
89
|
+
allowUnverifiedEmailAdopt?: boolean;
|
|
62
90
|
}
|
|
63
91
|
interface ProvisioningBridge<TUser> {
|
|
64
92
|
/**
|
|
@@ -83,4 +111,4 @@ interface ProvisioningBridge<TUser> {
|
|
|
83
111
|
*/
|
|
84
112
|
declare function createProvisioningBridge<TUser>(options: ProvisioningBridgeOptions<TUser>): ProvisioningBridge<TUser>;
|
|
85
113
|
|
|
86
|
-
export { type ProvisioningBridge as P, type ProvisioningBridgeOptions as a, type ProvisioningStorage as b, createProvisioningBridge as c, type ProvisioningContext as d };
|
|
114
|
+
export { type ProvisioningBridge as P, type ProvisioningBridgeOptions as a, type ProvisioningStorage as b, createProvisioningBridge as c, type ProvisioningContext as d, ProvisioningError as e };
|
|
@@ -20,7 +20,7 @@ declare function encodePublishableKey(mode: KeyMode, payload: PublishableKeyPayl
|
|
|
20
20
|
declare function parsePublishableKey(raw: string): ParsedPublishableKey | null;
|
|
21
21
|
/**
|
|
22
22
|
* Strict counterpart to `parsePublishableKey` — throws a typed `IQAuthError`
|
|
23
|
-
* (`code: "
|
|
23
|
+
* (`code: "config_invalid"`) with an actionable message when the key is
|
|
24
24
|
* missing, malformed, or encodes a non-URL `iss`. Use this at SDK init so a
|
|
25
25
|
* bad key fails loudly at boot instead of cryptically at first verify.
|
|
26
26
|
*/
|
|
@@ -20,7 +20,7 @@ declare function encodePublishableKey(mode: KeyMode, payload: PublishableKeyPayl
|
|
|
20
20
|
declare function parsePublishableKey(raw: string): ParsedPublishableKey | null;
|
|
21
21
|
/**
|
|
22
22
|
* Strict counterpart to `parsePublishableKey` — throws a typed `IQAuthError`
|
|
23
|
-
* (`code: "
|
|
23
|
+
* (`code: "config_invalid"`) with an actionable message when the key is
|
|
24
24
|
* missing, malformed, or encodes a non-URL `iss`. Use this at SDK init so a
|
|
25
25
|
* bad key fails loudly at boot instead of cryptically at first verify.
|
|
26
26
|
*/
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { S as SessionError } from './index-Cko-d5po.mjs';
|
|
2
|
+
import 'csstype';
|
|
3
|
+
import 'react/jsx-runtime';
|
|
4
|
+
import 'react';
|
|
5
|
+
import './signIn-CReqfXsh.mjs';
|
|
6
|
+
import './publishableKey-f2kq-rKw.mjs';
|
|
7
|
+
import './types-Bn8O-OEd.mjs';
|
|
8
|
+
import './types-DnU2LhXR.mjs';
|
|
9
|
+
|
|
10
|
+
interface UseEffectivePermissionsOptions {
|
|
11
|
+
/**
|
|
12
|
+
* App key (OIDC client_id / manifest key) the permissions should be
|
|
13
|
+
* resolved against. Required — permissions in IQAuth are app-scoped.
|
|
14
|
+
*/
|
|
15
|
+
appKey: string;
|
|
16
|
+
/** Disable the network fetch (claims fallback still applies). */
|
|
17
|
+
enabled?: boolean;
|
|
18
|
+
/** Stale window in ms. Default 5min. */
|
|
19
|
+
staleTime?: number;
|
|
20
|
+
/**
|
|
21
|
+
* Override the issuer URL the hook calls. Defaults to the issuer the
|
|
22
|
+
* provider was booted with.
|
|
23
|
+
*/
|
|
24
|
+
issuer?: string;
|
|
25
|
+
}
|
|
26
|
+
interface UseEffectivePermissionsResult {
|
|
27
|
+
/** Normalized, wildcard-collapsed set of granted permission ids. */
|
|
28
|
+
permissions: string[];
|
|
29
|
+
/** Wildcard-aware membership check. Identical semantics on server. */
|
|
30
|
+
hasPermission: (id: string) => boolean;
|
|
31
|
+
/** True only while a fetch is actively in flight (and no cached data yet). */
|
|
32
|
+
isLoading: boolean;
|
|
33
|
+
/** Last fetch error, if any. Cleared on next successful refetch. */
|
|
34
|
+
error: SessionError | null;
|
|
35
|
+
/** Force a refetch, bypassing the staleTime window. */
|
|
36
|
+
refetch: () => Promise<void>;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Canonical hook for resolving the *full* effective permission set of the
|
|
40
|
+
* signed-in user against a single app. Use this whenever the JWT
|
|
41
|
+
* `entitlements` claim is too small (apps with hundreds of nodes can't fit
|
|
42
|
+
* them in the token).
|
|
43
|
+
*
|
|
44
|
+
* Requires a `<QueryClientProvider>` (`@tanstack/react-query`) somewhere
|
|
45
|
+
* above this hook in the tree.
|
|
46
|
+
*
|
|
47
|
+
* Returns `{ permissions, hasPermission, isLoading, error, refetch }`.
|
|
48
|
+
* `hasPermission` honours wildcard semantics (`*`, `metrics.*`).
|
|
49
|
+
*/
|
|
50
|
+
declare function useEffectivePermissions(opts: UseEffectivePermissionsOptions): UseEffectivePermissionsResult;
|
|
51
|
+
|
|
52
|
+
export { type UseEffectivePermissionsOptions, type UseEffectivePermissionsResult, useEffectivePermissions };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { S as SessionError } from './index-RNqwEcmY.js';
|
|
2
|
+
import 'csstype';
|
|
3
|
+
import 'react/jsx-runtime';
|
|
4
|
+
import 'react';
|
|
5
|
+
import './signIn-Cfa1GTpO.js';
|
|
6
|
+
import './publishableKey-f2kq-rKw.js';
|
|
7
|
+
import './types-Bn8O-OEd.js';
|
|
8
|
+
import './types-DnU2LhXR.js';
|
|
9
|
+
|
|
10
|
+
interface UseEffectivePermissionsOptions {
|
|
11
|
+
/**
|
|
12
|
+
* App key (OIDC client_id / manifest key) the permissions should be
|
|
13
|
+
* resolved against. Required — permissions in IQAuth are app-scoped.
|
|
14
|
+
*/
|
|
15
|
+
appKey: string;
|
|
16
|
+
/** Disable the network fetch (claims fallback still applies). */
|
|
17
|
+
enabled?: boolean;
|
|
18
|
+
/** Stale window in ms. Default 5min. */
|
|
19
|
+
staleTime?: number;
|
|
20
|
+
/**
|
|
21
|
+
* Override the issuer URL the hook calls. Defaults to the issuer the
|
|
22
|
+
* provider was booted with.
|
|
23
|
+
*/
|
|
24
|
+
issuer?: string;
|
|
25
|
+
}
|
|
26
|
+
interface UseEffectivePermissionsResult {
|
|
27
|
+
/** Normalized, wildcard-collapsed set of granted permission ids. */
|
|
28
|
+
permissions: string[];
|
|
29
|
+
/** Wildcard-aware membership check. Identical semantics on server. */
|
|
30
|
+
hasPermission: (id: string) => boolean;
|
|
31
|
+
/** True only while a fetch is actively in flight (and no cached data yet). */
|
|
32
|
+
isLoading: boolean;
|
|
33
|
+
/** Last fetch error, if any. Cleared on next successful refetch. */
|
|
34
|
+
error: SessionError | null;
|
|
35
|
+
/** Force a refetch, bypassing the staleTime window. */
|
|
36
|
+
refetch: () => Promise<void>;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Canonical hook for resolving the *full* effective permission set of the
|
|
40
|
+
* signed-in user against a single app. Use this whenever the JWT
|
|
41
|
+
* `entitlements` claim is too small (apps with hundreds of nodes can't fit
|
|
42
|
+
* them in the token).
|
|
43
|
+
*
|
|
44
|
+
* Requires a `<QueryClientProvider>` (`@tanstack/react-query`) somewhere
|
|
45
|
+
* above this hook in the tree.
|
|
46
|
+
*
|
|
47
|
+
* Returns `{ permissions, hasPermission, isLoading, error, refetch }`.
|
|
48
|
+
* `hasPermission` honours wildcard semantics (`*`, `metrics.*`).
|
|
49
|
+
*/
|
|
50
|
+
declare function useEffectivePermissions(opts: UseEffectivePermissionsOptions): UseEffectivePermissionsResult;
|
|
51
|
+
|
|
52
|
+
export { type UseEffectivePermissionsOptions, type UseEffectivePermissionsResult, useEffectivePermissions };
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __esm = (fn, res) => function __init() {
|
|
7
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
8
|
+
};
|
|
9
|
+
var __export = (target, all) => {
|
|
10
|
+
for (var name in all)
|
|
11
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
12
|
+
};
|
|
13
|
+
var __copyProps = (to, from, except, desc) => {
|
|
14
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
15
|
+
for (let key of __getOwnPropNames(from))
|
|
16
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
17
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
18
|
+
}
|
|
19
|
+
return to;
|
|
20
|
+
};
|
|
21
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
22
|
+
|
|
23
|
+
// src/errors.ts
|
|
24
|
+
var init_errors = __esm({
|
|
25
|
+
"src/errors.ts"() {
|
|
26
|
+
"use strict";
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// src/browser/storage.ts
|
|
31
|
+
var init_storage = __esm({
|
|
32
|
+
"src/browser/storage.ts"() {
|
|
33
|
+
"use strict";
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// src/browser/pkce.ts
|
|
38
|
+
var init_pkce = __esm({
|
|
39
|
+
"src/browser/pkce.ts"() {
|
|
40
|
+
"use strict";
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// src/browser/signIn.ts
|
|
45
|
+
var init_signIn = __esm({
|
|
46
|
+
"src/browser/signIn.ts"() {
|
|
47
|
+
"use strict";
|
|
48
|
+
init_pkce();
|
|
49
|
+
init_storage();
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// src/react-permissions.ts
|
|
54
|
+
var react_permissions_exports = {};
|
|
55
|
+
__export(react_permissions_exports, {
|
|
56
|
+
useEffectivePermissions: () => useEffectivePermissions
|
|
57
|
+
});
|
|
58
|
+
module.exports = __toCommonJS(react_permissions_exports);
|
|
59
|
+
|
|
60
|
+
// src/react/permissions.tsx
|
|
61
|
+
var import_react2 = require("react");
|
|
62
|
+
var import_react_query = require("@tanstack/react-query");
|
|
63
|
+
|
|
64
|
+
// src/permissions/wildcard.ts
|
|
65
|
+
var SUFFIX = ".*";
|
|
66
|
+
function wildcardPrefix(pattern) {
|
|
67
|
+
return pattern.slice(0, -SUFFIX.length);
|
|
68
|
+
}
|
|
69
|
+
function hasPermission(set, id) {
|
|
70
|
+
if (!id) return false;
|
|
71
|
+
if (!set) return false;
|
|
72
|
+
if (id === "*") {
|
|
73
|
+
for (const entry of set) if (entry === "*") return true;
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
const queryIsWildcard = id.endsWith(SUFFIX);
|
|
77
|
+
const queryPrefix = queryIsWildcard ? wildcardPrefix(id) : null;
|
|
78
|
+
for (const entry of set) {
|
|
79
|
+
if (!entry) continue;
|
|
80
|
+
if (entry === "*") return true;
|
|
81
|
+
if (entry === id) return true;
|
|
82
|
+
if (entry.endsWith(SUFFIX)) {
|
|
83
|
+
const prefix = wildcardPrefix(entry);
|
|
84
|
+
if (!queryIsWildcard) {
|
|
85
|
+
if (id === prefix) return true;
|
|
86
|
+
if (id.startsWith(prefix + ".")) return true;
|
|
87
|
+
} else {
|
|
88
|
+
if (queryPrefix === prefix) return true;
|
|
89
|
+
if (queryPrefix !== null && queryPrefix.startsWith(prefix + ".")) return true;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
function expandPermissions(set) {
|
|
96
|
+
if (!set) return [];
|
|
97
|
+
const seen = /* @__PURE__ */ new Set();
|
|
98
|
+
for (const raw of set) {
|
|
99
|
+
if (typeof raw !== "string" || raw.length === 0) continue;
|
|
100
|
+
seen.add(raw);
|
|
101
|
+
}
|
|
102
|
+
if (seen.has("*")) return ["*"];
|
|
103
|
+
const wildcards = [];
|
|
104
|
+
for (const entry of seen) if (entry.endsWith(SUFFIX)) wildcards.push(entry);
|
|
105
|
+
const out = [];
|
|
106
|
+
for (const entry of seen) {
|
|
107
|
+
let covered = false;
|
|
108
|
+
for (const w of wildcards) {
|
|
109
|
+
if (w === entry) continue;
|
|
110
|
+
const prefix = wildcardPrefix(w);
|
|
111
|
+
if (entry === prefix) {
|
|
112
|
+
covered = true;
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
if (entry.startsWith(prefix + ".")) {
|
|
116
|
+
covered = true;
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (!covered) out.push(entry);
|
|
121
|
+
}
|
|
122
|
+
out.sort();
|
|
123
|
+
return out;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// src/react/index.tsx
|
|
127
|
+
var import_react = require("react");
|
|
128
|
+
|
|
129
|
+
// src/browser/sessionManager.ts
|
|
130
|
+
init_errors();
|
|
131
|
+
|
|
132
|
+
// src/publishableKey.ts
|
|
133
|
+
init_errors();
|
|
134
|
+
|
|
135
|
+
// src/browser/sessionManager.ts
|
|
136
|
+
init_storage();
|
|
137
|
+
|
|
138
|
+
// src/react/index.tsx
|
|
139
|
+
init_signIn();
|
|
140
|
+
|
|
141
|
+
// src/browser/accountRegistry.ts
|
|
142
|
+
init_storage();
|
|
143
|
+
var COOKIE_MAX_AGE = 60 * 60 * 24 * 30;
|
|
144
|
+
|
|
145
|
+
// src/react/index.tsx
|
|
146
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
147
|
+
var IQAuthContext = (0, import_react.createContext)(null);
|
|
148
|
+
function __useIQAuthInternal() {
|
|
149
|
+
return useCtx();
|
|
150
|
+
}
|
|
151
|
+
function useCtx() {
|
|
152
|
+
const ctx = (0, import_react.useContext)(IQAuthContext);
|
|
153
|
+
if (!ctx) throw new Error("IQAuth hooks must be used inside <IQAuthProvider>");
|
|
154
|
+
return ctx;
|
|
155
|
+
}
|
|
156
|
+
var MultisessionContext = (0, import_react.createContext)(null);
|
|
157
|
+
var SDK_CSS_MAX_LEN = 50 * 1024;
|
|
158
|
+
|
|
159
|
+
// src/react/permissions.tsx
|
|
160
|
+
var DEFAULT_PERMS_STALE_MS = 5 * 60 * 1e3;
|
|
161
|
+
function projectAllowedScopes(rows) {
|
|
162
|
+
if (!Array.isArray(rows)) return [];
|
|
163
|
+
const allowed = [];
|
|
164
|
+
const denied = /* @__PURE__ */ new Set();
|
|
165
|
+
for (const r of rows) {
|
|
166
|
+
if (!r || typeof r.scope !== "string" || !r.scope) continue;
|
|
167
|
+
if (r.effect === "deny") denied.add(r.scope);
|
|
168
|
+
else allowed.push(r.scope);
|
|
169
|
+
}
|
|
170
|
+
return expandPermissions(allowed.filter((s) => !denied.has(s)));
|
|
171
|
+
}
|
|
172
|
+
function useEffectivePermissions(opts) {
|
|
173
|
+
const { manager, snapshot } = __useIQAuthInternal();
|
|
174
|
+
const { appKey, enabled = true, staleTime = DEFAULT_PERMS_STALE_MS, issuer } = opts;
|
|
175
|
+
const claims = snapshot.claims;
|
|
176
|
+
const isPlatformAdmin = Array.isArray(claims?.roles) && claims.roles.includes("platform_admin");
|
|
177
|
+
const userId = snapshot.user?.sub ?? null;
|
|
178
|
+
const tenantId = snapshot.tenantId ?? claims?.tenantId ?? null;
|
|
179
|
+
const issuerUrl = (issuer ?? manager.issuerUrl).replace(/\/$/, "");
|
|
180
|
+
const queryEnabled = enabled && !!userId && !!tenantId && !!appKey && !isPlatformAdmin;
|
|
181
|
+
const query = (0, import_react_query.useQuery)({
|
|
182
|
+
queryKey: ["iqauth", "effective-permissions", issuerUrl, tenantId, userId, appKey],
|
|
183
|
+
queryFn: async () => {
|
|
184
|
+
const url = `${issuerUrl}/api/v1/tenants/${encodeURIComponent(tenantId)}/users/${encodeURIComponent(userId)}/permissions/effective?appKey=${encodeURIComponent(appKey)}`;
|
|
185
|
+
const res = await manager.fetch(url);
|
|
186
|
+
const json = await res.json().catch(() => ({}));
|
|
187
|
+
if (!res.ok) {
|
|
188
|
+
const code = json?.error?.code || `HTTP_${res.status}`;
|
|
189
|
+
const message = json?.error?.message || `HTTP ${res.status}`;
|
|
190
|
+
const e = { code, message };
|
|
191
|
+
throw e;
|
|
192
|
+
}
|
|
193
|
+
const rows = Array.isArray(json) ? json : json?.data ?? [];
|
|
194
|
+
return projectAllowedScopes(rows);
|
|
195
|
+
},
|
|
196
|
+
enabled: queryEnabled,
|
|
197
|
+
staleTime,
|
|
198
|
+
refetchOnWindowFocus: false,
|
|
199
|
+
retry: false
|
|
200
|
+
});
|
|
201
|
+
const refetch = (0, import_react2.useCallback)(async () => {
|
|
202
|
+
if (!queryEnabled) return;
|
|
203
|
+
await query.refetch();
|
|
204
|
+
}, [query, queryEnabled]);
|
|
205
|
+
return (0, import_react2.useMemo)(() => {
|
|
206
|
+
if (isPlatformAdmin) {
|
|
207
|
+
return {
|
|
208
|
+
permissions: ["*"],
|
|
209
|
+
hasPermission: () => true,
|
|
210
|
+
isLoading: false,
|
|
211
|
+
error: null,
|
|
212
|
+
refetch
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
const fetched = query.data;
|
|
216
|
+
const perms = fetched ?? expandPermissions(claims?.entitlements ?? []);
|
|
217
|
+
const isLoading = queryEnabled && query.isLoading;
|
|
218
|
+
const error = query.error ? "code" in query.error && typeof query.error.code === "string" ? query.error : { code: "PERMISSIONS_FETCH_FAILED", message: query.error.message || "Failed to fetch permissions" } : null;
|
|
219
|
+
return {
|
|
220
|
+
permissions: perms,
|
|
221
|
+
hasPermission: (id) => hasPermission(perms, id),
|
|
222
|
+
isLoading,
|
|
223
|
+
error,
|
|
224
|
+
refetch
|
|
225
|
+
};
|
|
226
|
+
}, [
|
|
227
|
+
isPlatformAdmin,
|
|
228
|
+
queryEnabled,
|
|
229
|
+
query.data,
|
|
230
|
+
query.isLoading,
|
|
231
|
+
query.error,
|
|
232
|
+
claims?.entitlements,
|
|
233
|
+
refetch
|
|
234
|
+
]);
|
|
235
|
+
}
|
|
236
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
237
|
+
0 && (module.exports = {
|
|
238
|
+
useEffectivePermissions
|
|
239
|
+
});
|