@iqauth/sdk 2.2.0 → 2.5.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.
Files changed (96) hide show
  1. package/README.md +134 -0
  2. package/dist/browser-session.d.mts +3 -3
  3. package/dist/browser-session.d.ts +3 -3
  4. package/dist/browser-session.js +89 -68
  5. package/dist/browser-session.mjs +2 -1
  6. package/dist/browser.d.mts +64 -29
  7. package/dist/browser.d.ts +64 -29
  8. package/dist/browser.js +794 -39
  9. package/dist/browser.mjs +44 -4
  10. package/dist/bundle-LUKDQYVQ.mjs +374 -0
  11. package/dist/chunk-3JULWS6F.mjs +106 -0
  12. package/dist/chunk-5T7GHBX6.mjs +1165 -0
  13. package/dist/{chunk-M4J6BPK7.mjs → chunk-6TDJJER7.mjs} +12 -3
  14. package/dist/{chunk-QZB745C2.mjs → chunk-76W5TLQQ.mjs} +264 -211
  15. package/dist/{chunk-D72UL5HL.mjs → chunk-BVV54LPI.mjs} +36 -4
  16. package/dist/chunk-LIZYFXH7.mjs +90 -0
  17. package/dist/chunk-MKKZULZR.mjs +241 -0
  18. package/dist/chunk-SL3KRS4W.mjs +54 -0
  19. package/dist/chunk-TKZTCPEK.mjs +232 -0
  20. package/dist/chunk-UKZLOHZG.mjs +83 -0
  21. package/dist/chunk-UNYDG2L4.mjs +209 -0
  22. package/dist/{chunk-MDUHPQMM.mjs → chunk-W3F4JYGP.mjs} +8 -180
  23. package/dist/{chunk-QEJB7WEQ.mjs → chunk-WQWBJSSS.mjs} +1 -1
  24. package/dist/cli/index.js +144 -36
  25. package/dist/cli/index.mjs +1 -1
  26. package/dist/{client-DXbHb2ul.d.ts → client-BNQe3AgF.d.ts} +3 -67
  27. package/dist/{client-Dv4v92Mj.d.mts → client-kYlJFgPv.d.mts} +3 -67
  28. package/dist/doctor-YYNHNMLD.mjs +198 -0
  29. package/dist/{express-BZmF1llh.d.mts → express-B6_1vBYZ.d.mts} +23 -2
  30. package/dist/{express-B4o3P8vK.d.ts → express-CHpfa7D_.d.ts} +23 -2
  31. package/dist/express.d.mts +77 -6
  32. package/dist/express.d.ts +77 -6
  33. package/dist/express.js +336 -74
  34. package/dist/express.mjs +209 -8
  35. package/dist/fastify.js +103 -72
  36. package/dist/fastify.mjs +6 -4
  37. package/dist/hono.js +102 -72
  38. package/dist/hono.mjs +5 -4
  39. package/dist/index.d.mts +8 -4
  40. package/dist/index.d.ts +8 -4
  41. package/dist/index.js +590 -73
  42. package/dist/index.mjs +30 -8
  43. package/dist/locales.d.mts +53 -0
  44. package/dist/locales.d.ts +53 -0
  45. package/dist/locales.js +1202 -0
  46. package/dist/locales.mjs +29 -0
  47. package/dist/mobile.d.mts +3 -3
  48. package/dist/mobile.d.ts +3 -3
  49. package/dist/mobile.js +89 -68
  50. package/dist/mobile.mjs +2 -1
  51. package/dist/next.d.mts +10 -1
  52. package/dist/next.d.ts +10 -1
  53. package/dist/next.js +101 -1618
  54. package/dist/next.mjs +9 -9
  55. package/dist/provisioningBridge-88xjOS2n.d.mts +86 -0
  56. package/dist/provisioningBridge-DnTfzdZK.d.ts +86 -0
  57. package/dist/react.d.mts +1349 -10
  58. package/dist/react.d.ts +1349 -10
  59. package/dist/react.js +2998 -569
  60. package/dist/react.mjs +1518 -95
  61. package/dist/reverify-4UEJXUS6.mjs +16 -0
  62. package/dist/server/handlers.d.mts +12 -1
  63. package/dist/server/handlers.d.ts +12 -1
  64. package/dist/server/handlers.js +12 -3
  65. package/dist/server/handlers.mjs +2 -2
  66. package/dist/server.d.mts +5 -4
  67. package/dist/server.d.ts +5 -4
  68. package/dist/server.js +188 -73
  69. package/dist/server.mjs +13 -8
  70. package/dist/service.d.mts +3 -3
  71. package/dist/service.d.ts +3 -3
  72. package/dist/service.js +89 -68
  73. package/dist/service.mjs +2 -1
  74. package/dist/signIn-CCY4JE5G.mjs +15 -0
  75. package/dist/{signIn-D_kP3v-c.d.mts → signIn-CiIBTJIh.d.mts} +232 -4
  76. package/dist/{signIn-BVDTIA_t.d.ts → signIn-OCr88Zf8.d.ts} +232 -4
  77. package/dist/test.d.mts +86 -0
  78. package/dist/test.d.ts +86 -0
  79. package/dist/test.js +289 -0
  80. package/dist/test.mjs +9 -0
  81. package/dist/tokens-DCyzzn8L.d.mts +63 -0
  82. package/dist/tokens-aHiGFr_E.d.ts +63 -0
  83. package/dist/types-6bNdxesb.d.mts +196 -0
  84. package/dist/types-6bNdxesb.d.ts +196 -0
  85. package/dist/{types-Cxl3bQHt.d.ts → types-DZAflmmq.d.mts} +6 -0
  86. package/dist/{types-Cxl3bQHt.d.mts → types-DZAflmmq.d.ts} +6 -0
  87. package/dist/webhooks.d.mts +61 -0
  88. package/dist/webhooks.d.ts +61 -0
  89. package/dist/webhooks.js +119 -0
  90. package/dist/webhooks.mjs +11 -0
  91. package/dist/ws.d.mts +73 -0
  92. package/dist/ws.d.ts +73 -0
  93. package/dist/ws.js +397 -0
  94. package/dist/ws.mjs +12 -0
  95. package/package.json +24 -3
  96. package/dist/doctor-XCI77BQS.mjs +0 -90
@@ -1,5 +1,5 @@
1
1
  import { c as ParsedPublishableKey } from './publishableKey-BaR0HoAH.js';
2
- import { d as SessionUser, J as JwtClaims } from './types-Cxl3bQHt.js';
2
+ import { J as JwtClaims, d as SessionUser } from './types-DZAflmmq.js';
3
3
 
4
4
  /**
5
5
  * SessionManager — core browser-side session state.
@@ -28,9 +28,17 @@ type SessionStatus = "loading" | "authenticated" | "unauthenticated";
28
28
  * first-party cookie — convenient for browser-only apps but not as resistant
29
29
  * to XSS exfiltration.
30
30
  */
31
+ /**
32
+ * Optional context handed to `RefreshTokenStore.write`. Multi-account stores
33
+ * use it to route the token to the correct per-account cookie when no
34
+ * "active" account is set yet (i.e. immediately after `addAccount()`).
35
+ */
36
+ interface RefreshTokenWriteContext {
37
+ claims?: JwtClaims | null;
38
+ }
31
39
  interface RefreshTokenStore {
32
40
  read(): string | null | Promise<string | null>;
33
- write(token: string): void | Promise<void>;
41
+ write(token: string, ctx?: RefreshTokenWriteContext): void | Promise<void>;
34
42
  clear(): void | Promise<void>;
35
43
  }
36
44
  interface SessionSnapshot {
@@ -101,6 +109,17 @@ interface SessionManagerOptions {
101
109
  * Defaults to `false` to preserve pre-2.0.3 behavior.
102
110
  */
103
111
  serverManagedSession?: boolean;
112
+ /**
113
+ * F14 — Override the names of the cookies the SDK reads/writes. Useful when
114
+ * a host app already uses `iqauth_rt`/`iqauth_at` for an unrelated purpose
115
+ * or wants to prefix all SDK cookies. Only the `refresh` cookie is written
116
+ * by the browser SDK directly; `access` is for parity with the backend
117
+ * helpers/middleware which read it via the same umbrella option.
118
+ */
119
+ cookieNames?: {
120
+ refresh?: string;
121
+ access?: string;
122
+ };
104
123
  }
105
124
  declare class SessionManager {
106
125
  private snapshot;
@@ -115,9 +134,10 @@ declare class SessionManager {
115
134
  private readonly userinfoPath;
116
135
  private readonly useCookies;
117
136
  private readonly proactiveRefresh;
118
- private readonly tokenStore;
137
+ private tokenStore;
119
138
  private readonly crossTabLockTimeoutMs;
120
139
  private readonly serverManagedSession;
140
+ private readonly refreshCookieName;
121
141
  private proactiveTimer;
122
142
  private bootstrapped;
123
143
  /** Pending refresh awaited by other tabs after a `refresh:claim` from us. */
@@ -129,6 +149,8 @@ declare class SessionManager {
129
149
  get appKey(): string;
130
150
  get tenantIdFromKey(): string;
131
151
  get issuerUrl(): string;
152
+ /** Cookie name the SDK uses for the refresh token (overridable via `cookieNames.refresh`). */
153
+ get refreshCookie(): string;
132
154
  getSnapshot(): SessionSnapshot;
133
155
  subscribe(listener: (s: SessionSnapshot) => void): () => void;
134
156
  /**
@@ -175,6 +197,12 @@ declare class SessionManager {
175
197
  * the server-side logout request.
176
198
  */
177
199
  signOutLocal(status?: SessionStatus): void;
200
+ /**
201
+ * Replace the refresh-token store at runtime. Used by the F22
202
+ * `<MultisessionAppSupport>` wrapper to swap in a `MultiAccountTokenStore`
203
+ * after the manager has already been constructed by `<IQAuthProvider>`.
204
+ */
205
+ setTokenStore(store: RefreshTokenStore): void;
178
206
  destroy(): void;
179
207
  private setStatus;
180
208
  private setError;
@@ -186,6 +214,180 @@ declare class SessionManager {
186
214
  private onBroadcast;
187
215
  }
188
216
 
217
+ /**
218
+ * Passwordless helpers (Task #91 / F24/F25/F31). Browser-only — these wrap
219
+ * the issuer's `/api/v1/auth/{magic-link,passkeys,identities}` routes so
220
+ * non-React consumers (vanilla JS, Vue, Svelte) can integrate without
221
+ * pulling in framework code. React consumers should prefer the hooks
222
+ * exported from `@iqauth/sdk/react`.
223
+ *
224
+ * All calls use `credentials: "include"` so the issuer can promote the
225
+ * minted session to httpOnly cookies (`iqauth_at` / `iqauth_rt`) when
226
+ * the caller forwards the `x-iqauth-session: cookie` header.
227
+ *
228
+ * `iqAuthBaseUrl` is the issuer URL (e.g. https://auth.example.com); it
229
+ * must NOT end with a slash.
230
+ */
231
+ type PasswordlessOptions = {
232
+ iqAuthBaseUrl: string;
233
+ /** When true (default), tells the issuer to set httpOnly cookies on success. */
234
+ cookieSession?: boolean;
235
+ };
236
+ type MagicLinkRequestInput = {
237
+ email: string;
238
+ /** App identifier — accepts the app id, key, or omitted for global links. */
239
+ appId?: string;
240
+ redirectUri: string;
241
+ };
242
+ declare function requestMagicLink(opts: PasswordlessOptions, input: MagicLinkRequestInput): Promise<{
243
+ ok: true;
244
+ }>;
245
+ declare function verifyMagicLink(opts: PasswordlessOptions, token: string): Promise<{
246
+ accessToken?: string;
247
+ user?: unknown;
248
+ redirectUri?: string;
249
+ }>;
250
+ type PasskeyAuthInput = {
251
+ email?: string;
252
+ };
253
+ declare function beginPasskeyAuthentication(opts: PasswordlessOptions, input?: PasskeyAuthInput): Promise<unknown>;
254
+ declare function finishPasskeyAuthentication(opts: PasswordlessOptions, response: unknown): Promise<unknown>;
255
+ declare function beginPasskeyRegistration(opts: PasswordlessOptions): Promise<unknown>;
256
+ declare function finishPasskeyRegistration(opts: PasswordlessOptions, response: unknown, name?: string): Promise<unknown>;
257
+ /**
258
+ * End-to-end passkey sign-in convenience. Loads `@simplewebauthn/browser` on
259
+ * demand (peer dependency) so callers that never use passkeys don't pay the
260
+ * bundle cost.
261
+ */
262
+ declare function signInWithPasskey(opts: PasswordlessOptions, input?: PasskeyAuthInput): Promise<unknown>;
263
+ declare function enrollPasskey(opts: PasswordlessOptions, name?: string): Promise<unknown>;
264
+ type LinkedIdentity = {
265
+ id: string;
266
+ provider: string;
267
+ providerUserId: string | null;
268
+ linkedAt: string;
269
+ label?: string | null;
270
+ name?: string | null;
271
+ canUnlink: boolean;
272
+ };
273
+ declare function listLinkedIdentities(opts: PasswordlessOptions): Promise<LinkedIdentity[]>;
274
+ type LinkProviderInput = {
275
+ provider: "google";
276
+ idToken: string;
277
+ reauth: {
278
+ password?: string;
279
+ };
280
+ } | {
281
+ provider: string;
282
+ opaque: {
283
+ providerUserId: string;
284
+ verifiedBy: string;
285
+ };
286
+ reauth: {
287
+ password?: string;
288
+ };
289
+ };
290
+ declare function linkProvider(opts: PasswordlessOptions, input: LinkProviderInput): Promise<{
291
+ ok: true;
292
+ }>;
293
+ type UnlinkProviderInput = {
294
+ provider: string;
295
+ reauth: {
296
+ password?: string;
297
+ };
298
+ };
299
+ declare function unlinkProvider(opts: PasswordlessOptions, input: UnlinkProviderInput): Promise<{
300
+ ok: true;
301
+ }>;
302
+
303
+ /**
304
+ * Browser-only storage helpers used by the SessionManager.
305
+ *
306
+ * Storage strategy (Phase B):
307
+ * - Access token: in memory only (held by SessionManager).
308
+ * - Refresh token: first-party cookie on the app's own domain. The cookie
309
+ * is set by the SDK callback handler and cleared on signOut.
310
+ *
311
+ * NOTE: Cookies set from JS cannot be httpOnly. Phase D (cookie-aware
312
+ * middleware) will move refresh-token cookie management into the app's
313
+ * backend so it can be httpOnly. Until then, the SDK uses a `Secure`,
314
+ * `SameSite=Lax` first-party cookie as a pragmatic stopgap. Nothing
315
+ * privileged ever lives in localStorage.
316
+ */
317
+ declare const REFRESH_COOKIE = "iqauth_rt";
318
+ interface CookieOptions {
319
+ maxAgeSeconds?: number;
320
+ path?: string;
321
+ domain?: string;
322
+ secure?: boolean;
323
+ sameSite?: "lax" | "strict" | "none";
324
+ }
325
+ declare function setCookie(name: string, value: string, opts?: CookieOptions): void;
326
+ declare function getCookie(name: string): string | null;
327
+ declare function clearCookie(name: string, opts?: CookieOptions): void;
328
+
329
+ /**
330
+ * F22 — Multi-account registry.
331
+ *
332
+ * Persists the set of accounts currently signed in to a given app (keyed by
333
+ * the app's publishableKey appId) plus which one is "active". Each account
334
+ * stores a single per-account refresh cookie named `iqauth_rt_<accountId>`
335
+ * (where `accountId` is the user's `sub` claim), so SessionManager can
336
+ * pivot to any account by reading a different cookie + calling refresh().
337
+ *
338
+ * NOTE: We deliberately do NOT store refresh tokens in localStorage — they
339
+ * stay in cookies (same security posture as the single-account default).
340
+ * localStorage holds only public profile data + ordering metadata.
341
+ */
342
+
343
+ interface AccountRecord {
344
+ accountId: string;
345
+ userId: string;
346
+ email: string;
347
+ name: string;
348
+ tenantId: string | null;
349
+ addedAt: number;
350
+ }
351
+ declare class AccountRegistry {
352
+ private readonly appId;
353
+ private listeners;
354
+ private storageHandler;
355
+ constructor(appId: string);
356
+ destroy(): void;
357
+ list(): AccountRecord[];
358
+ active(): string | null;
359
+ get(accountId: string): AccountRecord | null;
360
+ upsert(rec: AccountRecord): void;
361
+ setActive(accountId: string | null): void;
362
+ remove(accountId: string): void;
363
+ subscribe(listener: () => void): () => void;
364
+ /**
365
+ * Read the refresh token for a specific account from its per-account
366
+ * cookie. Used by `MultiAccountTokenStore.read()`.
367
+ */
368
+ readRefreshToken(accountId: string): string | null;
369
+ /**
370
+ * Persist a refresh token to the per-account cookie. Caller is the
371
+ * SessionManager via `MultiAccountTokenStore.write()`.
372
+ */
373
+ writeRefreshToken(accountId: string, token: string, opts?: CookieOptions): void;
374
+ clearRefreshToken(accountId: string): void;
375
+ private notify;
376
+ }
377
+ /**
378
+ * RefreshTokenStore that reads/writes the active account's per-account
379
+ * cookie. Falls back to a configurable single-account store when no active
380
+ * account is set (e.g. before the first sign-in completes).
381
+ */
382
+ declare class MultiAccountTokenStore implements RefreshTokenStore {
383
+ private readonly registry;
384
+ private readonly fallback;
385
+ constructor(registry: AccountRegistry, fallback: RefreshTokenStore);
386
+ read(): string | null | Promise<string | null>;
387
+ write(token: string, ctx?: RefreshTokenWriteContext): void | Promise<void>;
388
+ clear(): void | Promise<void>;
389
+ }
390
+
189
391
  /**
190
392
  * Sign-in / sign-out / callback helpers that build on top of the
191
393
  * SessionManager. These are usable both from React (via the hook helpers in
@@ -208,6 +410,20 @@ interface SignInOptions {
208
410
  redirectUri?: string;
209
411
  /** Extra OIDC scopes beyond `openid` to request. */
210
412
  scope?: string;
413
+ /**
414
+ * F14 — Override SDK cookie names. `refresh` controls where the JS
415
+ * fallback writes the refresh token after the callback exchange.
416
+ */
417
+ cookieNames?: {
418
+ refresh?: string;
419
+ };
420
+ /**
421
+ * OIDC `prompt` parameter. `"login"` forces the IQAuth host to show the
422
+ * sign-in form even if a single-sign-on session already exists — used by
423
+ * the multi-account `addAccount()` helper to add a second account in
424
+ * parallel rather than re-using the active session.
425
+ */
426
+ prompt?: "login" | "none" | "consent" | "select_account";
211
427
  }
212
428
  interface SignOutOptions {
213
429
  /** Where to send the user after sign out. Defaults to the current page. */
@@ -216,6 +432,14 @@ interface SignOutOptions {
216
432
  logoutPath?: string;
217
433
  /** Skip the network call to IQAuth and just clear the local session. */
218
434
  localOnly?: boolean;
435
+ /**
436
+ * Also clear the IQAuth single-sign-on session (the `iq_sso` cookie on the
437
+ * issuer host). When `true` (the default), the next visit to `/sign-in`
438
+ * will show the login form instead of silently resuming the prior session.
439
+ * Set to `false` to preserve SSO across other apps in the same IQAuth
440
+ * tenant (e.g. log out of one app but stay signed in to others).
441
+ */
442
+ endSsoSession?: boolean;
219
443
  }
220
444
  interface CallbackResult {
221
445
  ok: boolean;
@@ -250,6 +474,10 @@ declare function handleAuthCallback(manager: SessionManager, options?: {
250
474
  url?: string;
251
475
  tokenPath?: string;
252
476
  fetchImpl?: typeof fetch;
477
+ /** F14 — refresh-cookie name override (defaults to `manager.refreshCookie`). */
478
+ cookieNames?: {
479
+ refresh?: string;
480
+ };
253
481
  }): Promise<CallbackResult>;
254
482
  /**
255
483
  * Clear the local session, ask IQAuth to revoke the refresh token, and
@@ -257,4 +485,4 @@ declare function handleAuthCallback(manager: SessionManager, options?: {
257
485
  */
258
486
  declare function signOut(manager: SessionManager, opts?: SignOutOptions): Promise<void>;
259
487
 
260
- export { type CallbackResult as C, SessionManager as S, type SessionSnapshot as a, type SignInOptions as b, type SignOutOptions as c, type SessionManagerOptions as d, type SessionStatus as e, buildSignInUrl as f, signOut as g, handleAuthCallback as h, redirectToSignIn as r, signIn as s };
488
+ export { AccountRegistry as A, clearCookie as B, type CallbackResult as C, getCookie as D, setCookie as E, type LinkedIdentity as L, type MagicLinkRequestInput as M, type PasswordlessOptions as P, type RefreshTokenStore as R, SessionManager as S, type UnlinkProviderInput as U, type SessionManagerOptions as a, type SessionSnapshot as b, type SessionStatus as c, beginPasskeyAuthentication as d, beginPasskeyRegistration as e, finishPasskeyAuthentication as f, finishPasskeyRegistration as g, enrollPasskey as h, linkProvider as i, type PasskeyAuthInput as j, type LinkProviderInput as k, listLinkedIdentities as l, MultiAccountTokenStore as m, type AccountRecord as n, buildSignInUrl as o, handleAuthCallback as p, redirectToSignIn as q, requestMagicLink as r, signInWithPasskey as s, signIn as t, unlinkProvider as u, verifyMagicLink as v, signOut as w, type SignInOptions as x, type SignOutOptions as y, REFRESH_COOKIE as z };
@@ -0,0 +1,86 @@
1
+ /**
2
+ * @iqauth/sdk/test — in-process test issuer.
3
+ *
4
+ * Spawns an http.Server that exposes a JWKS endpoint, an OIDC discovery
5
+ * doc, a token endpoint that accepts a code-exchange call, and a userinfo
6
+ * endpoint. Mints valid RS256 JWTs against a freshly-generated keypair so
7
+ * integration tests can write `<IQAuthProvider iqAuthBaseUrl={baseUrl}>`
8
+ * without a live IQAuth dependency.
9
+ *
10
+ * import { createTestIssuer } from "@iqauth/sdk/test";
11
+ * const issuer = await createTestIssuer({ port: 0 });
12
+ * const token = issuer.mintToken({ sub: "u1", roles: ["tenant_admin"] });
13
+ * // …point your SDK / fetch / supertest at issuer.baseUrl
14
+ * await issuer.close();
15
+ *
16
+ * Out of scope: this does NOT mock SSO providers (Google, Microsoft) or
17
+ * the full DispositionIQ admin/permissions API surface. It implements
18
+ * exactly enough of the issuer to satisfy the SDK's verify path and the
19
+ * common code-exchange flow used by integration tests.
20
+ */
21
+ interface MintTokenOptions {
22
+ sub?: string;
23
+ email?: string;
24
+ name?: string;
25
+ tenantId?: string;
26
+ vendorId?: string | null;
27
+ roles?: string[];
28
+ entitlements?: string[];
29
+ sessionId?: string;
30
+ jti?: string;
31
+ scopeContext?: unknown;
32
+ loginMethod?: string;
33
+ /** Override the audience array. Defaults to `["dispositioniq"]`. */
34
+ audience?: string | string[];
35
+ /** Override `iss` for negative tests. Defaults to the issuer's baseUrl. */
36
+ issuer?: string;
37
+ /** Token lifetime in seconds. Defaults to 900 (15min). */
38
+ expiresInSeconds?: number;
39
+ /** Backdate `iat` (and consequently `exp`) for expired-token tests. */
40
+ iat?: number;
41
+ /** Free-form extra claims. */
42
+ [key: string]: unknown;
43
+ }
44
+ interface MintAuthCodeOptions extends MintTokenOptions {
45
+ /** Refresh token returned alongside the access token at exchange time. */
46
+ refreshToken?: string;
47
+ }
48
+ interface CreateTestIssuerOptions {
49
+ /** Port to bind. Use `0` (default) for an OS-assigned free port. */
50
+ port?: number;
51
+ /** Hostname to bind. Defaults to `127.0.0.1`. */
52
+ host?: string;
53
+ /** Tenant id baked into minted publishable keys. Defaults to `tenant-test`. */
54
+ tenantId?: string;
55
+ /** App id baked into minted publishable keys. Defaults to `app-test`. */
56
+ appId?: string;
57
+ /**
58
+ * Key id used in the JWT header and JWKS entry. Defaults to a
59
+ * crypto-random value so concurrent test issuers don't collide caches.
60
+ */
61
+ kid?: string;
62
+ /** Default audience used when callers don't override it. Defaults to `["dispositioniq"]`. */
63
+ defaultAudience?: string[];
64
+ }
65
+ interface TestIssuer {
66
+ /** Base URL of the running issuer, e.g. `http://127.0.0.1:54321`. */
67
+ baseUrl: string;
68
+ /** A `pk_test_…` publishable key encoding `{iss=baseUrl, appId, tenantId, kid}`. */
69
+ publishableKey: string;
70
+ /** Key id used to sign tokens — useful when tests want to assert headers. */
71
+ kid: string;
72
+ /** PEM-encoded RSA public key. */
73
+ publicKey: string;
74
+ /** Sign a JWT and return it. */
75
+ mintToken: (opts?: MintTokenOptions) => string;
76
+ /**
77
+ * Pre-register an authorization code that, when later exchanged at
78
+ * `POST /oidc/token`, returns the supplied claims as a fresh token pair.
79
+ */
80
+ mintAuthCode: (opts?: MintAuthCodeOptions) => string;
81
+ /** Stop accepting connections and free the port. */
82
+ close: () => Promise<void>;
83
+ }
84
+ declare function createTestIssuer(options?: CreateTestIssuerOptions): Promise<TestIssuer>;
85
+
86
+ export { type CreateTestIssuerOptions, type MintAuthCodeOptions, type MintTokenOptions, type TestIssuer, createTestIssuer };
package/dist/test.d.ts ADDED
@@ -0,0 +1,86 @@
1
+ /**
2
+ * @iqauth/sdk/test — in-process test issuer.
3
+ *
4
+ * Spawns an http.Server that exposes a JWKS endpoint, an OIDC discovery
5
+ * doc, a token endpoint that accepts a code-exchange call, and a userinfo
6
+ * endpoint. Mints valid RS256 JWTs against a freshly-generated keypair so
7
+ * integration tests can write `<IQAuthProvider iqAuthBaseUrl={baseUrl}>`
8
+ * without a live IQAuth dependency.
9
+ *
10
+ * import { createTestIssuer } from "@iqauth/sdk/test";
11
+ * const issuer = await createTestIssuer({ port: 0 });
12
+ * const token = issuer.mintToken({ sub: "u1", roles: ["tenant_admin"] });
13
+ * // …point your SDK / fetch / supertest at issuer.baseUrl
14
+ * await issuer.close();
15
+ *
16
+ * Out of scope: this does NOT mock SSO providers (Google, Microsoft) or
17
+ * the full DispositionIQ admin/permissions API surface. It implements
18
+ * exactly enough of the issuer to satisfy the SDK's verify path and the
19
+ * common code-exchange flow used by integration tests.
20
+ */
21
+ interface MintTokenOptions {
22
+ sub?: string;
23
+ email?: string;
24
+ name?: string;
25
+ tenantId?: string;
26
+ vendorId?: string | null;
27
+ roles?: string[];
28
+ entitlements?: string[];
29
+ sessionId?: string;
30
+ jti?: string;
31
+ scopeContext?: unknown;
32
+ loginMethod?: string;
33
+ /** Override the audience array. Defaults to `["dispositioniq"]`. */
34
+ audience?: string | string[];
35
+ /** Override `iss` for negative tests. Defaults to the issuer's baseUrl. */
36
+ issuer?: string;
37
+ /** Token lifetime in seconds. Defaults to 900 (15min). */
38
+ expiresInSeconds?: number;
39
+ /** Backdate `iat` (and consequently `exp`) for expired-token tests. */
40
+ iat?: number;
41
+ /** Free-form extra claims. */
42
+ [key: string]: unknown;
43
+ }
44
+ interface MintAuthCodeOptions extends MintTokenOptions {
45
+ /** Refresh token returned alongside the access token at exchange time. */
46
+ refreshToken?: string;
47
+ }
48
+ interface CreateTestIssuerOptions {
49
+ /** Port to bind. Use `0` (default) for an OS-assigned free port. */
50
+ port?: number;
51
+ /** Hostname to bind. Defaults to `127.0.0.1`. */
52
+ host?: string;
53
+ /** Tenant id baked into minted publishable keys. Defaults to `tenant-test`. */
54
+ tenantId?: string;
55
+ /** App id baked into minted publishable keys. Defaults to `app-test`. */
56
+ appId?: string;
57
+ /**
58
+ * Key id used in the JWT header and JWKS entry. Defaults to a
59
+ * crypto-random value so concurrent test issuers don't collide caches.
60
+ */
61
+ kid?: string;
62
+ /** Default audience used when callers don't override it. Defaults to `["dispositioniq"]`. */
63
+ defaultAudience?: string[];
64
+ }
65
+ interface TestIssuer {
66
+ /** Base URL of the running issuer, e.g. `http://127.0.0.1:54321`. */
67
+ baseUrl: string;
68
+ /** A `pk_test_…` publishable key encoding `{iss=baseUrl, appId, tenantId, kid}`. */
69
+ publishableKey: string;
70
+ /** Key id used to sign tokens — useful when tests want to assert headers. */
71
+ kid: string;
72
+ /** PEM-encoded RSA public key. */
73
+ publicKey: string;
74
+ /** Sign a JWT and return it. */
75
+ mintToken: (opts?: MintTokenOptions) => string;
76
+ /**
77
+ * Pre-register an authorization code that, when later exchanged at
78
+ * `POST /oidc/token`, returns the supplied claims as a fresh token pair.
79
+ */
80
+ mintAuthCode: (opts?: MintAuthCodeOptions) => string;
81
+ /** Stop accepting connections and free the port. */
82
+ close: () => Promise<void>;
83
+ }
84
+ declare function createTestIssuer(options?: CreateTestIssuerOptions): Promise<TestIssuer>;
85
+
86
+ export { type CreateTestIssuerOptions, type MintAuthCodeOptions, type MintTokenOptions, type TestIssuer, createTestIssuer };