@iqauth/sdk 2.0.5 → 2.2.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/README.md +19 -3
- package/dist/browser.d.mts +2 -2
- package/dist/browser.d.ts +2 -2
- package/dist/browser.js +57 -6
- package/dist/browser.mjs +2 -2
- package/dist/{chunk-ZESHDJDU.mjs → chunk-D72UL5HL.mjs} +3 -6
- package/dist/{chunk-JQRTY5MY.mjs → chunk-M4J6BPK7.mjs} +3 -8
- package/dist/chunk-QEJB7WEQ.mjs +119 -0
- package/dist/{chunk-S3M2IXCE.mjs → chunk-QZB745C2.mjs} +3 -8
- package/dist/cli/index.js +21 -0
- package/dist/cli/index.mjs +1 -1
- package/dist/{doctor-OHJRZBBT.mjs → doctor-XCI77BQS.mjs} +2 -1
- package/dist/express.js +54 -25
- package/dist/express.mjs +5 -8
- package/dist/fastify.js +53 -19
- package/dist/fastify.mjs +4 -5
- package/dist/hono.js +53 -19
- package/dist/hono.mjs +4 -5
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +59 -4
- package/dist/index.mjs +4 -2
- package/dist/next.js +66 -34
- package/dist/next.mjs +6 -9
- package/dist/{publishableKey-B5DIK81A.d.mts → publishableKey-BaR0HoAH.d.mts} +10 -1
- package/dist/{publishableKey-B5DIK81A.d.ts → publishableKey-BaR0HoAH.d.ts} +10 -1
- package/dist/react.d.mts +91 -4
- package/dist/react.d.ts +91 -4
- package/dist/react.js +466 -162
- package/dist/react.mjs +411 -147
- package/dist/server/handlers.js +63 -17
- package/dist/server/handlers.mjs +3 -2
- package/dist/server.js +53 -21
- package/dist/server.mjs +3 -3
- package/dist/{signIn-VRNzlNyG.d.ts → signIn-BVDTIA_t.d.ts} +1 -1
- package/dist/{signIn-CEMdUAwd.d.mts → signIn-D_kP3v-c.d.mts} +1 -1
- package/package.json +1 -1
- package/dist/chunk-5WFR6Y33.mjs +0 -59
package/README.md
CHANGED
|
@@ -56,18 +56,34 @@ Create both in one call from the admin Quickstart wizard, or run `npx iqauth ini
|
|
|
56
56
|
### React (browser)
|
|
57
57
|
|
|
58
58
|
```tsx
|
|
59
|
-
import {
|
|
59
|
+
import {
|
|
60
|
+
IQAuthProvider,
|
|
61
|
+
IQAuthLoading,
|
|
62
|
+
IQAuthLoaded,
|
|
63
|
+
SignedIn,
|
|
64
|
+
SignedOut,
|
|
65
|
+
RedirectToSignIn,
|
|
66
|
+
} from "@iqauth/sdk/react";
|
|
60
67
|
|
|
61
68
|
export default function App() {
|
|
62
69
|
return (
|
|
63
70
|
<IQAuthProvider publishableKey={import.meta.env.VITE_IQAUTH_PUBLISHABLE_KEY}>
|
|
64
|
-
<
|
|
65
|
-
<
|
|
71
|
+
<IQAuthLoading><Spinner /></IQAuthLoading>
|
|
72
|
+
<IQAuthLoaded>
|
|
73
|
+
<SignedIn><Dashboard /></SignedIn>
|
|
74
|
+
<SignedOut><RedirectToSignIn /></SignedOut>
|
|
75
|
+
</IQAuthLoaded>
|
|
66
76
|
</IQAuthProvider>
|
|
67
77
|
);
|
|
68
78
|
}
|
|
69
79
|
```
|
|
70
80
|
|
|
81
|
+
Wrapping the gating components in `<IQAuthLoading/>` / `<IQAuthLoaded/>` is
|
|
82
|
+
the slow-network-safe pattern: until `bootstrap()` finishes, both
|
|
83
|
+
`<SignedIn/>` and `<SignedOut/>` render `null`, which on a slow mobile
|
|
84
|
+
connection is several seconds of blank page. The loading slot fills that
|
|
85
|
+
gap, mirroring Clerk's `<ClerkLoading/>` / `<ClerkLoaded/>`.
|
|
86
|
+
|
|
71
87
|
Available hooks: `useUser()`, `useSession()`, `useAuth()`, `useOrganization()`. Each returns `{ data, isLoading, error }`.
|
|
72
88
|
Drop-in components: `<SignIn/>`, `<SignUp/>`, `<UserButton/>`, `<UserProfile/>`, `<OrganizationSwitcher/>`, `<AuthCallback/>`.
|
|
73
89
|
|
package/dist/browser.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { C as CallbackResult, S as SessionManager, d as SessionManagerOptions, a as SessionSnapshot, e as SessionStatus, b as SignInOptions, c as SignOutOptions, f as buildSignInUrl, h as handleAuthCallback, r as redirectToSignIn, s as signIn, g as signOut } from './signIn-
|
|
2
|
-
export { K as KeyMode,
|
|
1
|
+
export { C as CallbackResult, S as SessionManager, d as SessionManagerOptions, a as SessionSnapshot, e as SessionStatus, b as SignInOptions, c as SignOutOptions, f as buildSignInUrl, h as handleAuthCallback, r as redirectToSignIn, s as signIn, g as signOut } from './signIn-D_kP3v-c.mjs';
|
|
2
|
+
export { K as KeyMode, c as ParsedPublishableKey, P as PublishableKeyPayload, e as encodePublishableKey, i as isPublishableKey, b as isSecretKey, p as parsePublishableKey } from './publishableKey-BaR0HoAH.mjs';
|
|
3
3
|
export { a as ErrorCode, E as ErrorCodes, I as IQAuthError } from './errors-CDdl24MP.mjs';
|
|
4
4
|
import './types-Cxl3bQHt.mjs';
|
|
5
5
|
|
package/dist/browser.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { C as CallbackResult, S as SessionManager, d as SessionManagerOptions, a as SessionSnapshot, e as SessionStatus, b as SignInOptions, c as SignOutOptions, f as buildSignInUrl, h as handleAuthCallback, r as redirectToSignIn, s as signIn, g as signOut } from './signIn-
|
|
2
|
-
export { K as KeyMode,
|
|
1
|
+
export { C as CallbackResult, S as SessionManager, d as SessionManagerOptions, a as SessionSnapshot, e as SessionStatus, b as SignInOptions, c as SignOutOptions, f as buildSignInUrl, h as handleAuthCallback, r as redirectToSignIn, s as signIn, g as signOut } from './signIn-BVDTIA_t.js';
|
|
2
|
+
export { K as KeyMode, c as ParsedPublishableKey, P as PublishableKeyPayload, e as encodePublishableKey, i as isPublishableKey, b as isSecretKey, p as parsePublishableKey } from './publishableKey-BaR0HoAH.js';
|
|
3
3
|
export { a as ErrorCode, E as ErrorCodes, I as IQAuthError } from './errors-CDdl24MP.js';
|
|
4
4
|
import './types-Cxl3bQHt.js';
|
|
5
5
|
|
package/dist/browser.js
CHANGED
|
@@ -116,6 +116,18 @@ function encodePublishableKey(mode, payload) {
|
|
|
116
116
|
if (mode !== "test" && mode !== "live") throw new Error(`Invalid mode: ${mode}`);
|
|
117
117
|
return `pk_${mode}_${b64urlEncode(JSON.stringify(payload))}`;
|
|
118
118
|
}
|
|
119
|
+
function isValidIssuerUrl(iss) {
|
|
120
|
+
if (typeof iss !== "string" || iss.length === 0) return false;
|
|
121
|
+
if (!iss.startsWith("http://") && !iss.startsWith("https://")) return false;
|
|
122
|
+
try {
|
|
123
|
+
const u = new URL(iss);
|
|
124
|
+
if (u.protocol !== "http:" && u.protocol !== "https:") return false;
|
|
125
|
+
if (!u.hostname) return false;
|
|
126
|
+
return true;
|
|
127
|
+
} catch {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
119
131
|
function parsePublishableKey(raw) {
|
|
120
132
|
if (typeof raw !== "string") return null;
|
|
121
133
|
const m = raw.match(/^pk_(test|live)_([A-Za-z0-9_-]+)$/);
|
|
@@ -126,11 +138,55 @@ function parsePublishableKey(raw) {
|
|
|
126
138
|
if (typeof json.iss !== "string" || typeof json.appId !== "string" || typeof json.tenantId !== "string" || typeof json.kid !== "string") {
|
|
127
139
|
return null;
|
|
128
140
|
}
|
|
141
|
+
if (!isValidIssuerUrl(json.iss)) return null;
|
|
129
142
|
return { mode: m[1], iss: json.iss, appId: json.appId, tenantId: json.tenantId, kid: json.kid, raw };
|
|
130
143
|
} catch {
|
|
131
144
|
return null;
|
|
132
145
|
}
|
|
133
146
|
}
|
|
147
|
+
function assertPublishableKey(raw, opts) {
|
|
148
|
+
const ctx = opts?.context ? `${opts.context}: ` : "";
|
|
149
|
+
if (typeof raw !== "string" || raw.length === 0) {
|
|
150
|
+
throw new IQAuthError(
|
|
151
|
+
"CONFIG_INVALID",
|
|
152
|
+
`${ctx}IQAuth publishable key is missing. Set IQAUTH_PUBLISHABLE_KEY (or pass publishableKey) to a pk_test_\u2026 or pk_live_\u2026 value from the IQAuth admin console.`
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
const shapeMatch = raw.match(/^pk_(test|live)_([A-Za-z0-9_-]+)$/);
|
|
156
|
+
if (!shapeMatch) {
|
|
157
|
+
throw new IQAuthError(
|
|
158
|
+
"CONFIG_INVALID",
|
|
159
|
+
`${ctx}IQAuth publishable key is malformed (got ${raw.slice(0, 12)}\u2026). Expected pk_test_\u2026 or pk_live_\u2026; regenerate the key from the IQAuth admin console.`
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
let decoded;
|
|
163
|
+
try {
|
|
164
|
+
decoded = JSON.parse(b64urlDecode(shapeMatch[2]));
|
|
165
|
+
} catch {
|
|
166
|
+
throw new IQAuthError(
|
|
167
|
+
"CONFIG_INVALID",
|
|
168
|
+
`${ctx}IQAuth publishable key payload is not valid base64url JSON. Regenerate the key from the IQAuth admin console.`
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
if (!isPublishableKeyPayload(decoded)) {
|
|
172
|
+
throw new IQAuthError(
|
|
173
|
+
"CONFIG_INVALID",
|
|
174
|
+
`${ctx}IQAuth publishable key payload is missing required fields {iss, appId, tenantId, kid}. Regenerate the key from the IQAuth admin console.`
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
if (!isValidIssuerUrl(decoded.iss)) {
|
|
178
|
+
throw new IQAuthError(
|
|
179
|
+
"CONFIG_INVALID",
|
|
180
|
+
`${ctx}IQAuth publishable key encodes an invalid issuer (iss=${JSON.stringify(decoded.iss)}). Expected a fully-qualified URL like "https://auth.example.com" (scheme required). Regenerate the key from the IQAuth admin console, or set IQAUTH_ISSUER to the correct issuer URL as a temporary workaround.`
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
return { mode: shapeMatch[1], iss: decoded.iss, appId: decoded.appId, tenantId: decoded.tenantId, kid: decoded.kid, raw };
|
|
184
|
+
}
|
|
185
|
+
function isPublishableKeyPayload(value) {
|
|
186
|
+
if (!value || typeof value !== "object") return false;
|
|
187
|
+
const v = value;
|
|
188
|
+
return typeof v.iss === "string" && typeof v.appId === "string" && typeof v.tenantId === "string" && typeof v.kid === "string";
|
|
189
|
+
}
|
|
134
190
|
function isPublishableKey(raw) {
|
|
135
191
|
return typeof raw === "string" && /^pk_(test|live)_/.test(raw);
|
|
136
192
|
}
|
|
@@ -258,12 +314,7 @@ var SessionManager = class {
|
|
|
258
314
|
this.remoteRefreshWaiters = [];
|
|
259
315
|
/** Active claims by other tabs (keyed by source tabId). */
|
|
260
316
|
this.foreignClaim = null;
|
|
261
|
-
const parsed =
|
|
262
|
-
if (!parsed) {
|
|
263
|
-
throw new Error(
|
|
264
|
-
`Invalid IQAuth publishable key. Expected pk_test_\u2026 or pk_live_\u2026 (got ${options.publishableKey?.slice(0, 12) ?? "<empty>"}\u2026).`
|
|
265
|
-
);
|
|
266
|
-
}
|
|
317
|
+
const parsed = assertPublishableKey(options.publishableKey, { context: "@iqauth/sdk/browser SessionManager" });
|
|
267
318
|
this.key = parsed;
|
|
268
319
|
const inferred = options.issuer ?? (parsed.iss.startsWith("http") ? parsed.iss : `https://${parsed.iss}`);
|
|
269
320
|
this.issuer = inferred.replace(/\/+$/, "");
|
package/dist/browser.mjs
CHANGED
|
@@ -12,13 +12,13 @@ import {
|
|
|
12
12
|
setCookie,
|
|
13
13
|
signIn,
|
|
14
14
|
signOut
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-QZB745C2.mjs";
|
|
16
16
|
import {
|
|
17
17
|
encodePublishableKey,
|
|
18
18
|
isPublishableKey,
|
|
19
19
|
isSecretKey,
|
|
20
20
|
parsePublishableKey
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-QEJB7WEQ.mjs";
|
|
22
22
|
import {
|
|
23
23
|
ErrorCodes,
|
|
24
24
|
IQAuthError
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
} from "./chunk-
|
|
2
|
+
assertPublishableKey
|
|
3
|
+
} from "./chunk-QEJB7WEQ.mjs";
|
|
4
4
|
import {
|
|
5
5
|
IQAuthClient
|
|
6
6
|
} from "./chunk-MDUHPQMM.mjs";
|
|
@@ -44,10 +44,7 @@ function readCookie(req, name) {
|
|
|
44
44
|
return void 0;
|
|
45
45
|
}
|
|
46
46
|
function clientFromPublishableKey(opts) {
|
|
47
|
-
const parsed =
|
|
48
|
-
if (!parsed) {
|
|
49
|
-
throw new Error("iqAuthMiddleware: invalid publishable key");
|
|
50
|
-
}
|
|
47
|
+
const parsed = assertPublishableKey(opts.publishableKey, { context: "iqAuthMiddleware" });
|
|
51
48
|
const issuer = (opts.issuer ?? (parsed.iss.startsWith("http") ? parsed.iss : `https://${parsed.iss}`)).replace(/\/+$/, "");
|
|
52
49
|
return new IQAuthClient({ baseUrl: issuer, environment: "server" });
|
|
53
50
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
} from "./chunk-
|
|
2
|
+
assertPublishableKey
|
|
3
|
+
} from "./chunk-QEJB7WEQ.mjs";
|
|
4
4
|
|
|
5
5
|
// src/server/handlers.ts
|
|
6
6
|
var TERMINAL_REFRESH_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
@@ -22,12 +22,7 @@ function shouldClearCookiesOnFailure(policy, status, errorCode) {
|
|
|
22
22
|
var ACCESS_TOKEN_TTL_SECONDS = 60 * 15;
|
|
23
23
|
var REFRESH_TOKEN_TTL_SECONDS = 60 * 60 * 24 * 30;
|
|
24
24
|
function resolve(config) {
|
|
25
|
-
const parsed =
|
|
26
|
-
if (!parsed) {
|
|
27
|
-
throw new Error(
|
|
28
|
-
"@iqauth/sdk: invalid publishable key passed to iqAuth helpers (expected pk_test_\u2026 or pk_live_\u2026)"
|
|
29
|
-
);
|
|
30
|
-
}
|
|
25
|
+
const parsed = assertPublishableKey(config.publishableKey, { context: "@iqauth/sdk helpers" });
|
|
31
26
|
const inferredIssuer = parsed.iss.startsWith("http") ? parsed.iss : `https://${parsed.iss}`;
|
|
32
27
|
return {
|
|
33
28
|
publishableKey: config.publishableKey,
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import {
|
|
2
|
+
IQAuthError
|
|
3
|
+
} from "./chunk-6I6RM4MN.mjs";
|
|
4
|
+
import {
|
|
5
|
+
__require
|
|
6
|
+
} from "./chunk-Y6FXYEAI.mjs";
|
|
7
|
+
|
|
8
|
+
// src/publishableKey.ts
|
|
9
|
+
function b64urlEncode(input) {
|
|
10
|
+
if (typeof btoa === "function") {
|
|
11
|
+
const bytes = new TextEncoder().encode(input);
|
|
12
|
+
let bin = "";
|
|
13
|
+
for (let i = 0; i < bytes.byteLength; i++) bin += String.fromCharCode(bytes[i]);
|
|
14
|
+
return btoa(bin).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
|
|
15
|
+
}
|
|
16
|
+
const { Buffer } = __require("buffer");
|
|
17
|
+
return Buffer.from(input, "utf8").toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
|
|
18
|
+
}
|
|
19
|
+
function b64urlDecode(input) {
|
|
20
|
+
const pad = input.length % 4 === 0 ? "" : "=".repeat(4 - input.length % 4);
|
|
21
|
+
const normalized = input.replace(/-/g, "+").replace(/_/g, "/") + pad;
|
|
22
|
+
if (typeof atob === "function") {
|
|
23
|
+
const bin = atob(normalized);
|
|
24
|
+
const bytes = new Uint8Array(bin.length);
|
|
25
|
+
for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);
|
|
26
|
+
return new TextDecoder().decode(bytes);
|
|
27
|
+
}
|
|
28
|
+
const { Buffer } = __require("buffer");
|
|
29
|
+
return Buffer.from(normalized, "base64").toString("utf8");
|
|
30
|
+
}
|
|
31
|
+
function encodePublishableKey(mode, payload) {
|
|
32
|
+
if (mode !== "test" && mode !== "live") throw new Error(`Invalid mode: ${mode}`);
|
|
33
|
+
return `pk_${mode}_${b64urlEncode(JSON.stringify(payload))}`;
|
|
34
|
+
}
|
|
35
|
+
function isValidIssuerUrl(iss) {
|
|
36
|
+
if (typeof iss !== "string" || iss.length === 0) return false;
|
|
37
|
+
if (!iss.startsWith("http://") && !iss.startsWith("https://")) return false;
|
|
38
|
+
try {
|
|
39
|
+
const u = new URL(iss);
|
|
40
|
+
if (u.protocol !== "http:" && u.protocol !== "https:") return false;
|
|
41
|
+
if (!u.hostname) return false;
|
|
42
|
+
return true;
|
|
43
|
+
} catch {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function parsePublishableKey(raw) {
|
|
48
|
+
if (typeof raw !== "string") return null;
|
|
49
|
+
const m = raw.match(/^pk_(test|live)_([A-Za-z0-9_-]+)$/);
|
|
50
|
+
if (!m) return null;
|
|
51
|
+
try {
|
|
52
|
+
const json = JSON.parse(b64urlDecode(m[2]));
|
|
53
|
+
if (!json || typeof json !== "object") return null;
|
|
54
|
+
if (typeof json.iss !== "string" || typeof json.appId !== "string" || typeof json.tenantId !== "string" || typeof json.kid !== "string") {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
if (!isValidIssuerUrl(json.iss)) return null;
|
|
58
|
+
return { mode: m[1], iss: json.iss, appId: json.appId, tenantId: json.tenantId, kid: json.kid, raw };
|
|
59
|
+
} catch {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function assertPublishableKey(raw, opts) {
|
|
64
|
+
const ctx = opts?.context ? `${opts.context}: ` : "";
|
|
65
|
+
if (typeof raw !== "string" || raw.length === 0) {
|
|
66
|
+
throw new IQAuthError(
|
|
67
|
+
"CONFIG_INVALID",
|
|
68
|
+
`${ctx}IQAuth publishable key is missing. Set IQAUTH_PUBLISHABLE_KEY (or pass publishableKey) to a pk_test_\u2026 or pk_live_\u2026 value from the IQAuth admin console.`
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
const shapeMatch = raw.match(/^pk_(test|live)_([A-Za-z0-9_-]+)$/);
|
|
72
|
+
if (!shapeMatch) {
|
|
73
|
+
throw new IQAuthError(
|
|
74
|
+
"CONFIG_INVALID",
|
|
75
|
+
`${ctx}IQAuth publishable key is malformed (got ${raw.slice(0, 12)}\u2026). Expected pk_test_\u2026 or pk_live_\u2026; regenerate the key from the IQAuth admin console.`
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
let decoded;
|
|
79
|
+
try {
|
|
80
|
+
decoded = JSON.parse(b64urlDecode(shapeMatch[2]));
|
|
81
|
+
} catch {
|
|
82
|
+
throw new IQAuthError(
|
|
83
|
+
"CONFIG_INVALID",
|
|
84
|
+
`${ctx}IQAuth publishable key payload is not valid base64url JSON. Regenerate the key from the IQAuth admin console.`
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
if (!isPublishableKeyPayload(decoded)) {
|
|
88
|
+
throw new IQAuthError(
|
|
89
|
+
"CONFIG_INVALID",
|
|
90
|
+
`${ctx}IQAuth publishable key payload is missing required fields {iss, appId, tenantId, kid}. Regenerate the key from the IQAuth admin console.`
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
if (!isValidIssuerUrl(decoded.iss)) {
|
|
94
|
+
throw new IQAuthError(
|
|
95
|
+
"CONFIG_INVALID",
|
|
96
|
+
`${ctx}IQAuth publishable key encodes an invalid issuer (iss=${JSON.stringify(decoded.iss)}). Expected a fully-qualified URL like "https://auth.example.com" (scheme required). Regenerate the key from the IQAuth admin console, or set IQAUTH_ISSUER to the correct issuer URL as a temporary workaround.`
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
return { mode: shapeMatch[1], iss: decoded.iss, appId: decoded.appId, tenantId: decoded.tenantId, kid: decoded.kid, raw };
|
|
100
|
+
}
|
|
101
|
+
function isPublishableKeyPayload(value) {
|
|
102
|
+
if (!value || typeof value !== "object") return false;
|
|
103
|
+
const v = value;
|
|
104
|
+
return typeof v.iss === "string" && typeof v.appId === "string" && typeof v.tenantId === "string" && typeof v.kid === "string";
|
|
105
|
+
}
|
|
106
|
+
function isPublishableKey(raw) {
|
|
107
|
+
return typeof raw === "string" && /^pk_(test|live)_/.test(raw);
|
|
108
|
+
}
|
|
109
|
+
function isSecretKey(raw) {
|
|
110
|
+
return typeof raw === "string" && /^sk_(test|live)_/.test(raw);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export {
|
|
114
|
+
encodePublishableKey,
|
|
115
|
+
parsePublishableKey,
|
|
116
|
+
assertPublishableKey,
|
|
117
|
+
isPublishableKey,
|
|
118
|
+
isSecretKey
|
|
119
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
} from "./chunk-
|
|
2
|
+
assertPublishableKey
|
|
3
|
+
} from "./chunk-QEJB7WEQ.mjs";
|
|
4
4
|
import {
|
|
5
5
|
IQAuthError
|
|
6
6
|
} from "./chunk-6I6RM4MN.mjs";
|
|
@@ -125,12 +125,7 @@ var SessionManager = class {
|
|
|
125
125
|
this.remoteRefreshWaiters = [];
|
|
126
126
|
/** Active claims by other tabs (keyed by source tabId). */
|
|
127
127
|
this.foreignClaim = null;
|
|
128
|
-
const parsed =
|
|
129
|
-
if (!parsed) {
|
|
130
|
-
throw new Error(
|
|
131
|
-
`Invalid IQAuth publishable key. Expected pk_test_\u2026 or pk_live_\u2026 (got ${options.publishableKey?.slice(0, 12) ?? "<empty>"}\u2026).`
|
|
132
|
-
);
|
|
133
|
-
}
|
|
128
|
+
const parsed = assertPublishableKey(options.publishableKey, { context: "@iqauth/sdk/browser SessionManager" });
|
|
134
129
|
this.key = parsed;
|
|
135
130
|
const inferred = options.issuer ?? (parsed.iss.startsWith("http") ? parsed.iss : `https://${parsed.iss}`);
|
|
136
131
|
this.issuer = inferred.replace(/\/+$/, "");
|
package/dist/cli/index.js
CHANGED
|
@@ -278,6 +278,13 @@ var init_util = __esm({
|
|
|
278
278
|
}
|
|
279
279
|
});
|
|
280
280
|
|
|
281
|
+
// src/errors.ts
|
|
282
|
+
var init_errors = __esm({
|
|
283
|
+
"src/errors.ts"() {
|
|
284
|
+
"use strict";
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
|
|
281
288
|
// src/publishableKey.ts
|
|
282
289
|
function b64urlDecode(input) {
|
|
283
290
|
const pad = input.length % 4 === 0 ? "" : "=".repeat(4 - input.length % 4);
|
|
@@ -291,6 +298,18 @@ function b64urlDecode(input) {
|
|
|
291
298
|
const { Buffer: Buffer2 } = require("buffer");
|
|
292
299
|
return Buffer2.from(normalized, "base64").toString("utf8");
|
|
293
300
|
}
|
|
301
|
+
function isValidIssuerUrl(iss) {
|
|
302
|
+
if (typeof iss !== "string" || iss.length === 0) return false;
|
|
303
|
+
if (!iss.startsWith("http://") && !iss.startsWith("https://")) return false;
|
|
304
|
+
try {
|
|
305
|
+
const u = new URL(iss);
|
|
306
|
+
if (u.protocol !== "http:" && u.protocol !== "https:") return false;
|
|
307
|
+
if (!u.hostname) return false;
|
|
308
|
+
return true;
|
|
309
|
+
} catch {
|
|
310
|
+
return false;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
294
313
|
function parsePublishableKey(raw) {
|
|
295
314
|
if (typeof raw !== "string") return null;
|
|
296
315
|
const m = raw.match(/^pk_(test|live)_([A-Za-z0-9_-]+)$/);
|
|
@@ -301,6 +320,7 @@ function parsePublishableKey(raw) {
|
|
|
301
320
|
if (typeof json.iss !== "string" || typeof json.appId !== "string" || typeof json.tenantId !== "string" || typeof json.kid !== "string") {
|
|
302
321
|
return null;
|
|
303
322
|
}
|
|
323
|
+
if (!isValidIssuerUrl(json.iss)) return null;
|
|
304
324
|
return { mode: m[1], iss: json.iss, appId: json.appId, tenantId: json.tenantId, kid: json.kid, raw };
|
|
305
325
|
} catch {
|
|
306
326
|
return null;
|
|
@@ -309,6 +329,7 @@ function parsePublishableKey(raw) {
|
|
|
309
329
|
var init_publishableKey = __esm({
|
|
310
330
|
"src/publishableKey.ts"() {
|
|
311
331
|
"use strict";
|
|
332
|
+
init_errors();
|
|
312
333
|
}
|
|
313
334
|
});
|
|
314
335
|
|
package/dist/cli/index.mjs
CHANGED
package/dist/express.js
CHANGED
|
@@ -1805,21 +1805,61 @@ function b64urlDecode(input) {
|
|
|
1805
1805
|
const { Buffer: Buffer2 } = require("buffer");
|
|
1806
1806
|
return Buffer2.from(normalized, "base64").toString("utf8");
|
|
1807
1807
|
}
|
|
1808
|
-
function
|
|
1809
|
-
if (typeof
|
|
1810
|
-
|
|
1811
|
-
if (!m) return null;
|
|
1808
|
+
function isValidIssuerUrl(iss) {
|
|
1809
|
+
if (typeof iss !== "string" || iss.length === 0) return false;
|
|
1810
|
+
if (!iss.startsWith("http://") && !iss.startsWith("https://")) return false;
|
|
1812
1811
|
try {
|
|
1813
|
-
const
|
|
1814
|
-
if (
|
|
1815
|
-
if (
|
|
1816
|
-
|
|
1817
|
-
}
|
|
1818
|
-
return { mode: m[1], iss: json.iss, appId: json.appId, tenantId: json.tenantId, kid: json.kid, raw };
|
|
1812
|
+
const u = new URL(iss);
|
|
1813
|
+
if (u.protocol !== "http:" && u.protocol !== "https:") return false;
|
|
1814
|
+
if (!u.hostname) return false;
|
|
1815
|
+
return true;
|
|
1819
1816
|
} catch {
|
|
1820
|
-
return
|
|
1817
|
+
return false;
|
|
1821
1818
|
}
|
|
1822
1819
|
}
|
|
1820
|
+
function assertPublishableKey(raw, opts) {
|
|
1821
|
+
const ctx = opts?.context ? `${opts.context}: ` : "";
|
|
1822
|
+
if (typeof raw !== "string" || raw.length === 0) {
|
|
1823
|
+
throw new IQAuthError(
|
|
1824
|
+
"CONFIG_INVALID",
|
|
1825
|
+
`${ctx}IQAuth publishable key is missing. Set IQAUTH_PUBLISHABLE_KEY (or pass publishableKey) to a pk_test_\u2026 or pk_live_\u2026 value from the IQAuth admin console.`
|
|
1826
|
+
);
|
|
1827
|
+
}
|
|
1828
|
+
const shapeMatch = raw.match(/^pk_(test|live)_([A-Za-z0-9_-]+)$/);
|
|
1829
|
+
if (!shapeMatch) {
|
|
1830
|
+
throw new IQAuthError(
|
|
1831
|
+
"CONFIG_INVALID",
|
|
1832
|
+
`${ctx}IQAuth publishable key is malformed (got ${raw.slice(0, 12)}\u2026). Expected pk_test_\u2026 or pk_live_\u2026; regenerate the key from the IQAuth admin console.`
|
|
1833
|
+
);
|
|
1834
|
+
}
|
|
1835
|
+
let decoded;
|
|
1836
|
+
try {
|
|
1837
|
+
decoded = JSON.parse(b64urlDecode(shapeMatch[2]));
|
|
1838
|
+
} catch {
|
|
1839
|
+
throw new IQAuthError(
|
|
1840
|
+
"CONFIG_INVALID",
|
|
1841
|
+
`${ctx}IQAuth publishable key payload is not valid base64url JSON. Regenerate the key from the IQAuth admin console.`
|
|
1842
|
+
);
|
|
1843
|
+
}
|
|
1844
|
+
if (!isPublishableKeyPayload(decoded)) {
|
|
1845
|
+
throw new IQAuthError(
|
|
1846
|
+
"CONFIG_INVALID",
|
|
1847
|
+
`${ctx}IQAuth publishable key payload is missing required fields {iss, appId, tenantId, kid}. Regenerate the key from the IQAuth admin console.`
|
|
1848
|
+
);
|
|
1849
|
+
}
|
|
1850
|
+
if (!isValidIssuerUrl(decoded.iss)) {
|
|
1851
|
+
throw new IQAuthError(
|
|
1852
|
+
"CONFIG_INVALID",
|
|
1853
|
+
`${ctx}IQAuth publishable key encodes an invalid issuer (iss=${JSON.stringify(decoded.iss)}). Expected a fully-qualified URL like "https://auth.example.com" (scheme required). Regenerate the key from the IQAuth admin console, or set IQAUTH_ISSUER to the correct issuer URL as a temporary workaround.`
|
|
1854
|
+
);
|
|
1855
|
+
}
|
|
1856
|
+
return { mode: shapeMatch[1], iss: decoded.iss, appId: decoded.appId, tenantId: decoded.tenantId, kid: decoded.kid, raw };
|
|
1857
|
+
}
|
|
1858
|
+
function isPublishableKeyPayload(value) {
|
|
1859
|
+
if (!value || typeof value !== "object") return false;
|
|
1860
|
+
const v = value;
|
|
1861
|
+
return typeof v.iss === "string" && typeof v.appId === "string" && typeof v.tenantId === "string" && typeof v.kid === "string";
|
|
1862
|
+
}
|
|
1823
1863
|
|
|
1824
1864
|
// src/middleware/express.ts
|
|
1825
1865
|
var KNOWN_AUTH_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
@@ -1857,10 +1897,7 @@ function readCookie(req, name) {
|
|
|
1857
1897
|
return void 0;
|
|
1858
1898
|
}
|
|
1859
1899
|
function clientFromPublishableKey(opts) {
|
|
1860
|
-
const parsed =
|
|
1861
|
-
if (!parsed) {
|
|
1862
|
-
throw new Error("iqAuthMiddleware: invalid publishable key");
|
|
1863
|
-
}
|
|
1900
|
+
const parsed = assertPublishableKey(opts.publishableKey, { context: "iqAuthMiddleware" });
|
|
1864
1901
|
const issuer = (opts.issuer ?? (parsed.iss.startsWith("http") ? parsed.iss : `https://${parsed.iss}`)).replace(/\/+$/, "");
|
|
1865
1902
|
return new IQAuthClient({ baseUrl: issuer, environment: "server" });
|
|
1866
1903
|
}
|
|
@@ -2002,12 +2039,7 @@ function shouldClearCookiesOnFailure(policy, status, errorCode) {
|
|
|
2002
2039
|
var ACCESS_TOKEN_TTL_SECONDS = 60 * 15;
|
|
2003
2040
|
var REFRESH_TOKEN_TTL_SECONDS = 60 * 60 * 24 * 30;
|
|
2004
2041
|
function resolve(config) {
|
|
2005
|
-
const parsed =
|
|
2006
|
-
if (!parsed) {
|
|
2007
|
-
throw new Error(
|
|
2008
|
-
"@iqauth/sdk: invalid publishable key passed to iqAuth helpers (expected pk_test_\u2026 or pk_live_\u2026)"
|
|
2009
|
-
);
|
|
2010
|
-
}
|
|
2042
|
+
const parsed = assertPublishableKey(config.publishableKey, { context: "@iqauth/sdk helpers" });
|
|
2011
2043
|
const inferredIssuer = parsed.iss.startsWith("http") ? parsed.iss : `https://${parsed.iss}`;
|
|
2012
2044
|
return {
|
|
2013
2045
|
publishableKey: config.publishableKey,
|
|
@@ -2222,10 +2254,7 @@ function readCookieFromReq(req, name) {
|
|
|
2222
2254
|
return void 0;
|
|
2223
2255
|
}
|
|
2224
2256
|
function iqAuth(options) {
|
|
2225
|
-
const parsed =
|
|
2226
|
-
if (!parsed) {
|
|
2227
|
-
throw new Error("@iqauth/sdk/express: invalid publishable key");
|
|
2228
|
-
}
|
|
2257
|
+
const parsed = assertPublishableKey(options.publishableKey, { context: "@iqauth/sdk/express" });
|
|
2229
2258
|
const issuer = (options.issuer ?? (parsed.iss.startsWith("http") ? parsed.iss : `https://${parsed.iss}`)).replace(/\/+$/, "");
|
|
2230
2259
|
const client = new IQAuthClient({
|
|
2231
2260
|
baseUrl: issuer,
|
package/dist/express.mjs
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DEFAULT_REFRESH_COOKIE,
|
|
3
3
|
iqAuthMiddleware
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-D72UL5HL.mjs";
|
|
5
5
|
import {
|
|
6
6
|
handleCallback,
|
|
7
7
|
handleRefresh,
|
|
8
8
|
handleSignout
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-M4J6BPK7.mjs";
|
|
10
10
|
import {
|
|
11
|
-
|
|
12
|
-
} from "./chunk-
|
|
11
|
+
assertPublishableKey
|
|
12
|
+
} from "./chunk-QEJB7WEQ.mjs";
|
|
13
13
|
import {
|
|
14
14
|
IQAuthClient
|
|
15
15
|
} from "./chunk-MDUHPQMM.mjs";
|
|
@@ -66,10 +66,7 @@ function readCookieFromReq(req, name) {
|
|
|
66
66
|
return void 0;
|
|
67
67
|
}
|
|
68
68
|
function iqAuth(options) {
|
|
69
|
-
const parsed =
|
|
70
|
-
if (!parsed) {
|
|
71
|
-
throw new Error("@iqauth/sdk/express: invalid publishable key");
|
|
72
|
-
}
|
|
69
|
+
const parsed = assertPublishableKey(options.publishableKey, { context: "@iqauth/sdk/express" });
|
|
73
70
|
const issuer = (options.issuer ?? (parsed.iss.startsWith("http") ? parsed.iss : `https://${parsed.iss}`)).replace(/\/+$/, "");
|
|
74
71
|
const client = new IQAuthClient({
|
|
75
72
|
baseUrl: issuer,
|
package/dist/fastify.js
CHANGED
|
@@ -1767,20 +1767,60 @@ function b64urlDecode(input) {
|
|
|
1767
1767
|
const { Buffer: Buffer2 } = require("buffer");
|
|
1768
1768
|
return Buffer2.from(normalized, "base64").toString("utf8");
|
|
1769
1769
|
}
|
|
1770
|
-
function
|
|
1771
|
-
if (typeof
|
|
1772
|
-
|
|
1773
|
-
if (!m) return null;
|
|
1770
|
+
function isValidIssuerUrl(iss) {
|
|
1771
|
+
if (typeof iss !== "string" || iss.length === 0) return false;
|
|
1772
|
+
if (!iss.startsWith("http://") && !iss.startsWith("https://")) return false;
|
|
1774
1773
|
try {
|
|
1775
|
-
const
|
|
1776
|
-
if (
|
|
1777
|
-
if (
|
|
1778
|
-
|
|
1779
|
-
}
|
|
1780
|
-
return { mode: m[1], iss: json.iss, appId: json.appId, tenantId: json.tenantId, kid: json.kid, raw };
|
|
1774
|
+
const u = new URL(iss);
|
|
1775
|
+
if (u.protocol !== "http:" && u.protocol !== "https:") return false;
|
|
1776
|
+
if (!u.hostname) return false;
|
|
1777
|
+
return true;
|
|
1781
1778
|
} catch {
|
|
1782
|
-
return
|
|
1779
|
+
return false;
|
|
1780
|
+
}
|
|
1781
|
+
}
|
|
1782
|
+
function assertPublishableKey(raw, opts) {
|
|
1783
|
+
const ctx = opts?.context ? `${opts.context}: ` : "";
|
|
1784
|
+
if (typeof raw !== "string" || raw.length === 0) {
|
|
1785
|
+
throw new IQAuthError(
|
|
1786
|
+
"CONFIG_INVALID",
|
|
1787
|
+
`${ctx}IQAuth publishable key is missing. Set IQAUTH_PUBLISHABLE_KEY (or pass publishableKey) to a pk_test_\u2026 or pk_live_\u2026 value from the IQAuth admin console.`
|
|
1788
|
+
);
|
|
1789
|
+
}
|
|
1790
|
+
const shapeMatch = raw.match(/^pk_(test|live)_([A-Za-z0-9_-]+)$/);
|
|
1791
|
+
if (!shapeMatch) {
|
|
1792
|
+
throw new IQAuthError(
|
|
1793
|
+
"CONFIG_INVALID",
|
|
1794
|
+
`${ctx}IQAuth publishable key is malformed (got ${raw.slice(0, 12)}\u2026). Expected pk_test_\u2026 or pk_live_\u2026; regenerate the key from the IQAuth admin console.`
|
|
1795
|
+
);
|
|
1796
|
+
}
|
|
1797
|
+
let decoded;
|
|
1798
|
+
try {
|
|
1799
|
+
decoded = JSON.parse(b64urlDecode(shapeMatch[2]));
|
|
1800
|
+
} catch {
|
|
1801
|
+
throw new IQAuthError(
|
|
1802
|
+
"CONFIG_INVALID",
|
|
1803
|
+
`${ctx}IQAuth publishable key payload is not valid base64url JSON. Regenerate the key from the IQAuth admin console.`
|
|
1804
|
+
);
|
|
1805
|
+
}
|
|
1806
|
+
if (!isPublishableKeyPayload(decoded)) {
|
|
1807
|
+
throw new IQAuthError(
|
|
1808
|
+
"CONFIG_INVALID",
|
|
1809
|
+
`${ctx}IQAuth publishable key payload is missing required fields {iss, appId, tenantId, kid}. Regenerate the key from the IQAuth admin console.`
|
|
1810
|
+
);
|
|
1783
1811
|
}
|
|
1812
|
+
if (!isValidIssuerUrl(decoded.iss)) {
|
|
1813
|
+
throw new IQAuthError(
|
|
1814
|
+
"CONFIG_INVALID",
|
|
1815
|
+
`${ctx}IQAuth publishable key encodes an invalid issuer (iss=${JSON.stringify(decoded.iss)}). Expected a fully-qualified URL like "https://auth.example.com" (scheme required). Regenerate the key from the IQAuth admin console, or set IQAUTH_ISSUER to the correct issuer URL as a temporary workaround.`
|
|
1816
|
+
);
|
|
1817
|
+
}
|
|
1818
|
+
return { mode: shapeMatch[1], iss: decoded.iss, appId: decoded.appId, tenantId: decoded.tenantId, kid: decoded.kid, raw };
|
|
1819
|
+
}
|
|
1820
|
+
function isPublishableKeyPayload(value) {
|
|
1821
|
+
if (!value || typeof value !== "object") return false;
|
|
1822
|
+
const v = value;
|
|
1823
|
+
return typeof v.iss === "string" && typeof v.appId === "string" && typeof v.tenantId === "string" && typeof v.kid === "string";
|
|
1784
1824
|
}
|
|
1785
1825
|
|
|
1786
1826
|
// src/server/handlers.ts
|
|
@@ -1803,12 +1843,7 @@ function shouldClearCookiesOnFailure(policy, status, errorCode) {
|
|
|
1803
1843
|
var ACCESS_TOKEN_TTL_SECONDS = 60 * 15;
|
|
1804
1844
|
var REFRESH_TOKEN_TTL_SECONDS = 60 * 60 * 24 * 30;
|
|
1805
1845
|
function resolve(config) {
|
|
1806
|
-
const parsed =
|
|
1807
|
-
if (!parsed) {
|
|
1808
|
-
throw new Error(
|
|
1809
|
-
"@iqauth/sdk: invalid publishable key passed to iqAuth helpers (expected pk_test_\u2026 or pk_live_\u2026)"
|
|
1810
|
-
);
|
|
1811
|
-
}
|
|
1846
|
+
const parsed = assertPublishableKey(config.publishableKey, { context: "@iqauth/sdk helpers" });
|
|
1812
1847
|
const inferredIssuer = parsed.iss.startsWith("http") ? parsed.iss : `https://${parsed.iss}`;
|
|
1813
1848
|
return {
|
|
1814
1849
|
publishableKey: config.publishableKey,
|
|
@@ -2023,8 +2058,7 @@ function readCookie(req, name) {
|
|
|
2023
2058
|
return void 0;
|
|
2024
2059
|
}
|
|
2025
2060
|
async function iqAuth(fastify, options) {
|
|
2026
|
-
const parsed =
|
|
2027
|
-
if (!parsed) throw new Error("@iqauth/sdk/fastify: invalid publishable key");
|
|
2061
|
+
const parsed = assertPublishableKey(options.publishableKey, { context: "@iqauth/sdk/fastify" });
|
|
2028
2062
|
const issuer = (options.issuer ?? (parsed.iss.startsWith("http") ? parsed.iss : `https://${parsed.iss}`)).replace(/\/+$/, "");
|
|
2029
2063
|
const helperConfig = { ...options, issuer };
|
|
2030
2064
|
const client = new IQAuthClient({
|