@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
@@ -0,0 +1,16 @@
1
+ import {
2
+ PRIOR_SESSION_STORAGE_KEY,
3
+ enterImpersonation,
4
+ exitImpersonation,
5
+ reverify,
6
+ withReverification
7
+ } from "./chunk-LIZYFXH7.mjs";
8
+ import "./chunk-6I6RM4MN.mjs";
9
+ import "./chunk-Y6FXYEAI.mjs";
10
+ export {
11
+ PRIOR_SESSION_STORAGE_KEY,
12
+ enterImpersonation,
13
+ exitImpersonation,
14
+ reverify,
15
+ withReverification
16
+ };
@@ -47,6 +47,11 @@ interface IQAuthHelperConfig {
47
47
  accessCookieName?: string;
48
48
  /** Override the refresh token cookie name. Defaults to `iqauth_rt`. */
49
49
  refreshCookieName?: string;
50
+ /** F14 — Umbrella shorthand for `accessCookieName` / `refreshCookieName`. */
51
+ cookieNames?: {
52
+ access?: string;
53
+ refresh?: string;
54
+ };
50
55
  /** Cookie domain (e.g. `.example.com`). Defaults to host-only. */
51
56
  cookieDomain?: string;
52
57
  /** Cookie sameSite policy. Defaults to `lax`. */
@@ -82,7 +87,11 @@ interface IQAuthHelperConfig {
82
87
  */
83
88
  clearCookiesOnRefreshFailure?: "terminal-only" | "always" | "never";
84
89
  }
85
- interface ResolvedConfig extends Required<Omit<IQAuthHelperConfig, "secretKey" | "cookieDomain" | "issuer" | "fetchImpl" | "clearCookiesOnRefreshFailure">> {
90
+ interface ResolvedConfig extends Required<Omit<IQAuthHelperConfig, "secretKey" | "cookieDomain" | "issuer" | "fetchImpl" | "clearCookiesOnRefreshFailure" | "cookieNames">> {
91
+ cookieNames?: {
92
+ access?: string;
93
+ refresh?: string;
94
+ };
86
95
  secretKey?: string;
87
96
  cookieDomain?: string;
88
97
  issuer: string;
@@ -118,6 +127,8 @@ declare function handleRefresh(config: IQAuthHelperConfig, input: {
118
127
  /** POST /api/iqauth/signout — clear cookies and best-effort revoke at issuer. */
119
128
  declare function handleSignout(config: IQAuthHelperConfig, input: {
120
129
  accessToken?: string;
130
+ ssoCookieHeader?: string;
131
+ endSsoSession?: boolean;
121
132
  }): Promise<HandlerResponse>;
122
133
 
123
134
  export { type HandlerResponse, type IQAuthHelperConfig, type ResolvedConfig as ResolvedIQAuthHelperConfig, type SetCookieDirective, handleCallback, handleRefresh, handleSignout, serializeCookie };
@@ -47,6 +47,11 @@ interface IQAuthHelperConfig {
47
47
  accessCookieName?: string;
48
48
  /** Override the refresh token cookie name. Defaults to `iqauth_rt`. */
49
49
  refreshCookieName?: string;
50
+ /** F14 — Umbrella shorthand for `accessCookieName` / `refreshCookieName`. */
51
+ cookieNames?: {
52
+ access?: string;
53
+ refresh?: string;
54
+ };
50
55
  /** Cookie domain (e.g. `.example.com`). Defaults to host-only. */
51
56
  cookieDomain?: string;
52
57
  /** Cookie sameSite policy. Defaults to `lax`. */
@@ -82,7 +87,11 @@ interface IQAuthHelperConfig {
82
87
  */
83
88
  clearCookiesOnRefreshFailure?: "terminal-only" | "always" | "never";
84
89
  }
85
- interface ResolvedConfig extends Required<Omit<IQAuthHelperConfig, "secretKey" | "cookieDomain" | "issuer" | "fetchImpl" | "clearCookiesOnRefreshFailure">> {
90
+ interface ResolvedConfig extends Required<Omit<IQAuthHelperConfig, "secretKey" | "cookieDomain" | "issuer" | "fetchImpl" | "clearCookiesOnRefreshFailure" | "cookieNames">> {
91
+ cookieNames?: {
92
+ access?: string;
93
+ refresh?: string;
94
+ };
86
95
  secretKey?: string;
87
96
  cookieDomain?: string;
88
97
  issuer: string;
@@ -118,6 +127,8 @@ declare function handleRefresh(config: IQAuthHelperConfig, input: {
118
127
  /** POST /api/iqauth/signout — clear cookies and best-effort revoke at issuer. */
119
128
  declare function handleSignout(config: IQAuthHelperConfig, input: {
120
129
  accessToken?: string;
130
+ ssoCookieHeader?: string;
131
+ endSsoSession?: boolean;
121
132
  }): Promise<HandlerResponse>;
122
133
 
123
134
  export { type HandlerResponse, type IQAuthHelperConfig, type ResolvedConfig as ResolvedIQAuthHelperConfig, type SetCookieDirective, handleCallback, handleRefresh, handleSignout, serializeCookie };
@@ -96,7 +96,7 @@ function assertPublishableKey(raw, opts) {
96
96
  if (!isValidIssuerUrl(decoded.iss)) {
97
97
  throw new IQAuthError(
98
98
  "CONFIG_INVALID",
99
- `${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.`
99
+ `${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 \u2014 the new key will encode a valid issuer URL.`
100
100
  );
101
101
  }
102
102
  return { mode: shapeMatch[1], iss: decoded.iss, appId: decoded.appId, tenantId: decoded.tenantId, kid: decoded.kid, raw };
@@ -133,8 +133,8 @@ function resolve(config) {
133
133
  publishableKey: config.publishableKey,
134
134
  secretKey: config.secretKey,
135
135
  issuer: (config.issuer ?? inferredIssuer).replace(/\/+$/, ""),
136
- accessCookieName: config.accessCookieName ?? "iqauth_at",
137
- refreshCookieName: config.refreshCookieName ?? "iqauth_rt",
136
+ accessCookieName: config.accessCookieName ?? config.cookieNames?.access ?? "iqauth_at",
137
+ refreshCookieName: config.refreshCookieName ?? config.cookieNames?.refresh ?? "iqauth_rt",
138
138
  cookieDomain: config.cookieDomain,
139
139
  sameSite: config.sameSite ?? "lax",
140
140
  secure: config.secure ?? true,
@@ -298,6 +298,15 @@ async function handleSignout(config, input) {
298
298
  } catch {
299
299
  }
300
300
  }
301
+ if (input.endSsoSession !== false && input.ssoCookieHeader) {
302
+ try {
303
+ await cfg.fetchImpl(`${cfg.issuer}/oidc/sso-logout`, {
304
+ method: "POST",
305
+ headers: { Cookie: input.ssoCookieHeader }
306
+ });
307
+ } catch {
308
+ }
309
+ }
301
310
  return {
302
311
  status: 200,
303
312
  body: { success: true, data: { signedOut: true } },
@@ -3,8 +3,8 @@ import {
3
3
  handleRefresh,
4
4
  handleSignout,
5
5
  serializeCookie
6
- } from "../chunk-M4J6BPK7.mjs";
7
- import "../chunk-QEJB7WEQ.mjs";
6
+ } from "../chunk-6TDJJER7.mjs";
7
+ import "../chunk-WQWBJSSS.mjs";
8
8
  import "../chunk-6I6RM4MN.mjs";
9
9
  import "../chunk-Y6FXYEAI.mjs";
10
10
  export {
package/dist/server.d.mts CHANGED
@@ -1,9 +1,10 @@
1
- import { b as IQAuthTokenClientConfig, N as ExpressMiddlewareOptions, Q as IQAuthRequestLike, R as IQAuthResponseLike, V as IQAuthNextFunction } from './types-Cxl3bQHt.mjs';
2
- import { I as IQAuthClient } from './client-Dv4v92Mj.mjs';
1
+ import { b as IQAuthTokenClientConfig, N as ExpressMiddlewareOptions, Q as IQAuthRequestLike, R as IQAuthResponseLike, V as IQAuthNextFunction } from './types-DZAflmmq.mjs';
2
+ import { I as IQAuthClient } from './client-kYlJFgPv.mjs';
3
3
  export { E as ErrorCodes, I as IQAuthError } from './errors-CDdl24MP.mjs';
4
- export { C as CookieAwareMiddlewareOptions, D as DEFAULT_ACCESS_COOKIE, a as DEFAULT_REFRESH_COOKIE, i as iqAuthMiddleware } from './express-BZmF1llh.mjs';
4
+ export { C as CookieAwareMiddlewareOptions, D as DEFAULT_ACCESS_COOKIE, a as DEFAULT_REFRESH_COOKIE, i as iqAuthMiddleware } from './express-B6_1vBYZ.mjs';
5
5
  export { HandlerResponse, IQAuthHelperConfig, SetCookieDirective, handleCallback, handleRefresh, handleSignout, serializeCookie } from './server/handlers.mjs';
6
- import 'jsonwebtoken';
6
+ export { P as ProvisioningBridge, a as ProvisioningBridgeOptions, d as ProvisioningContext, b as ProvisioningStorage, c as createProvisioningBridge } from './provisioningBridge-88xjOS2n.mjs';
7
+ import './tokens-DCyzzn8L.mjs';
7
8
 
8
9
  declare class ServerIQAuthClient extends IQAuthClient {
9
10
  constructor(config: IQAuthTokenClientConfig);
package/dist/server.d.ts CHANGED
@@ -1,9 +1,10 @@
1
- import { b as IQAuthTokenClientConfig, N as ExpressMiddlewareOptions, Q as IQAuthRequestLike, R as IQAuthResponseLike, V as IQAuthNextFunction } from './types-Cxl3bQHt.js';
2
- import { I as IQAuthClient } from './client-DXbHb2ul.js';
1
+ import { b as IQAuthTokenClientConfig, N as ExpressMiddlewareOptions, Q as IQAuthRequestLike, R as IQAuthResponseLike, V as IQAuthNextFunction } from './types-DZAflmmq.js';
2
+ import { I as IQAuthClient } from './client-BNQe3AgF.js';
3
3
  export { E as ErrorCodes, I as IQAuthError } from './errors-CDdl24MP.js';
4
- export { C as CookieAwareMiddlewareOptions, D as DEFAULT_ACCESS_COOKIE, a as DEFAULT_REFRESH_COOKIE, i as iqAuthMiddleware } from './express-B4o3P8vK.js';
4
+ export { C as CookieAwareMiddlewareOptions, D as DEFAULT_ACCESS_COOKIE, a as DEFAULT_REFRESH_COOKIE, i as iqAuthMiddleware } from './express-CHpfa7D_.js';
5
5
  export { HandlerResponse, IQAuthHelperConfig, SetCookieDirective, handleCallback, handleRefresh, handleSignout, serializeCookie } from './server/handlers.js';
6
- import 'jsonwebtoken';
6
+ export { P as ProvisioningBridge, a as ProvisioningBridgeOptions, d as ProvisioningContext, b as ProvisioningStorage, c as createProvisioningBridge } from './provisioningBridge-DnTfzdZK.js';
7
+ import './tokens-aHiGFr_E.js';
7
8
 
8
9
  declare class ServerIQAuthClient extends IQAuthClient {
9
10
  constructor(config: IQAuthTokenClientConfig);
package/dist/server.js CHANGED
@@ -36,6 +36,7 @@ __export(server_exports, {
36
36
  IQAuthClient: () => IQAuthClient,
37
37
  IQAuthError: () => IQAuthError,
38
38
  ServerIQAuthClient: () => ServerIQAuthClient,
39
+ createProvisioningBridge: () => createProvisioningBridge,
39
40
  createServerClient: () => createServerClient,
40
41
  handleCallback: () => handleCallback,
41
42
  handleRefresh: () => handleRefresh,
@@ -453,8 +454,7 @@ function parseMfaResponse(data, browserSessionMode) {
453
454
  }
454
455
 
455
456
  // src/modules/tokens.ts
456
- var import_crypto = __toESM(require("crypto"));
457
- var import_jsonwebtoken = __toESM(require("jsonwebtoken"));
457
+ var import_jose = require("jose");
458
458
  var JWKS_CACHE_TTL_MS = 60 * 60 * 1e3;
459
459
  var DEFAULT_TOKEN_ISSUER = [
460
460
  "https://auth.dispositioniq.com",
@@ -467,6 +467,24 @@ var DEFAULT_TOKEN_AUDIENCE = [
467
467
  "iqvalidate"
468
468
  ];
469
469
  var DEFAULT_CLOCK_TOLERANCE_SECONDS = 30;
470
+ function decodeProtectedHeader(token) {
471
+ const parts = token.split(".");
472
+ if (parts.length < 2) return null;
473
+ try {
474
+ const padded = parts[0] + "=".repeat((4 - parts[0].length % 4) % 4);
475
+ const b64 = padded.replace(/-/g, "+").replace(/_/g, "/");
476
+ let json;
477
+ if (typeof atob === "function") {
478
+ json = atob(b64);
479
+ } else {
480
+ const { Buffer: Buffer2 } = require("buffer");
481
+ json = Buffer2.from(b64, "base64").toString("utf8");
482
+ }
483
+ return JSON.parse(json);
484
+ } catch {
485
+ return null;
486
+ }
487
+ }
470
488
  var TokensModule = class {
471
489
  constructor(baseUrl, options = {}) {
472
490
  this.jwksCache = null;
@@ -477,49 +495,49 @@ var TokensModule = class {
477
495
  this.defaultClockTolerance = options.clockTolerance ?? DEFAULT_CLOCK_TOLERANCE_SECONDS;
478
496
  }
479
497
  /**
480
- * Verify a JWT access token using RS256 via JWKS from /.well-known/jwks.json.
481
- * Caches JWKS keys for 1 hour. Retries once on unknown `kid`.
482
- *
483
- * @remarks Validates against /.well-known/jwks.json. Issuer, audience, and
484
- * clock tolerance default to client config but can be overridden per call.
498
+ * Verify a JWT access token using RS256/ES256 via JWKS from
499
+ * `/.well-known/jwks.json`. Backed by `jose` (Web Crypto) so it runs on
500
+ * Node, browser, and edge runtimes alike — no `node:crypto` dependency.
501
+ * Caches JWKS for 1 hour and refetches once on unknown `kid`.
485
502
  */
486
503
  async verify(token, options = {}) {
487
- const decoded = import_jsonwebtoken.default.decode(token, { complete: true });
488
- if (!decoded || typeof decoded === "string") {
504
+ const header = decodeProtectedHeader(token);
505
+ if (!header) {
489
506
  throw new IQAuthError("TOKEN_INVALID", "Unable to decode token");
490
507
  }
491
- const kid = decoded.header.kid;
508
+ const kid = header.kid;
492
509
  if (!kid) {
493
510
  throw new IQAuthError("TOKEN_INVALID", "Token missing kid header");
494
511
  }
495
- let publicKey = await this.getPublicKey(kid);
496
- if (!publicKey) {
497
- await this.refreshJwks();
498
- publicKey = await this.getPublicKey(kid);
512
+ let cache = await this.ensureCache();
513
+ if (!cache.byKid.has(kid)) {
514
+ this.jwksCache = null;
515
+ cache = await this.ensureCache();
499
516
  }
500
- if (!publicKey) {
517
+ if (!cache.byKid.has(kid)) {
501
518
  throw new IQAuthError("TOKEN_INVALID", `Unknown key ID: ${kid}`);
502
519
  }
503
520
  const issuer = options.issuer ?? this.defaultIssuer;
504
521
  const audience = options.audience ?? this.defaultAudience;
505
522
  const clockTolerance = options.clockTolerance ?? this.defaultClockTolerance;
506
- const algorithms = options.algorithms ?? ["RS256"];
523
+ const algorithms = options.algorithms ?? ["RS256", "ES256"];
524
+ const verifyOptions = {
525
+ algorithms,
526
+ clockTolerance,
527
+ issuer,
528
+ audience
529
+ };
507
530
  try {
508
- const verifyOptions = {
509
- algorithms,
510
- clockTolerance,
511
- // The jsonwebtoken types insist on tuple types for arrays; runtime
512
- // accepts plain string[] so we cast to satisfy the compiler.
513
- issuer,
514
- audience
515
- };
516
- const verified = import_jsonwebtoken.default.verify(token, publicKey, verifyOptions);
517
- return verified;
531
+ const { payload } = await (0, import_jose.jwtVerify)(token, cache.verifier, verifyOptions);
532
+ return payload;
518
533
  } catch (err) {
534
+ if (err instanceof import_jose.errors.JWTExpired) {
535
+ throw new IQAuthError("TOKEN_EXPIRED", "Token has expired");
536
+ }
537
+ if (err instanceof import_jose.errors.JOSEError) {
538
+ throw new IQAuthError("TOKEN_INVALID", err.message);
539
+ }
519
540
  if (err instanceof Error) {
520
- if (err.name === "TokenExpiredError") {
521
- throw new IQAuthError("TOKEN_EXPIRED", "Token has expired");
522
- }
523
541
  throw new IQAuthError("TOKEN_INVALID", err.message);
524
542
  }
525
543
  throw new IQAuthError("TOKEN_INVALID", "Token verification failed");
@@ -527,29 +545,40 @@ var TokensModule = class {
527
545
  }
528
546
  /**
529
547
  * Decode a JWT without verification. Returns null if malformed.
530
- *
531
- * @remarks Local decode only — no network call
532
548
  */
533
549
  decode(token) {
534
- const decoded = import_jsonwebtoken.default.decode(token);
535
- return decoded;
550
+ try {
551
+ const parts = token.split(".");
552
+ if (parts.length < 2) return null;
553
+ const payload = parts[1];
554
+ const padded = payload + "=".repeat((4 - payload.length % 4) % 4);
555
+ const b64 = padded.replace(/-/g, "+").replace(/_/g, "/");
556
+ let json;
557
+ if (typeof atob === "function") {
558
+ json = atob(b64);
559
+ } else {
560
+ const { Buffer: Buffer2 } = require("buffer");
561
+ json = Buffer2.from(b64, "base64").toString("utf8");
562
+ }
563
+ try {
564
+ json = decodeURIComponent(escape(json));
565
+ } catch {
566
+ }
567
+ const claims = JSON.parse(json);
568
+ if (!claims || typeof claims !== "object") return null;
569
+ return claims;
570
+ } catch {
571
+ return null;
572
+ }
536
573
  }
537
- /**
538
- * Check if a token is expired based on the `exp` claim.
539
- *
540
- * @remarks Local check only — no network call
541
- */
574
+ /** Check if a token is expired based on the `exp` claim. */
542
575
  isExpired(token) {
543
576
  const claims = this.decode(token);
544
577
  if (!claims?.exp) return true;
545
578
  const now = Math.floor(Date.now() / 1e3);
546
579
  return claims.exp <= now;
547
580
  }
548
- /**
549
- * Get the claims from a token without verification.
550
- *
551
- * @remarks Local decode only — no network call
552
- */
581
+ /** Get the claims from a token without verification. */
553
582
  getClaims(token) {
554
583
  const claims = this.decode(token);
555
584
  if (!claims) {
@@ -557,11 +586,15 @@ var TokensModule = class {
557
586
  }
558
587
  return claims;
559
588
  }
560
- async getPublicKey(kid) {
561
- if (!this.jwksCache || Date.now() - this.jwksCache.fetchedAt > JWKS_CACHE_TTL_MS) {
562
- await this.refreshJwks();
589
+ async ensureCache() {
590
+ if (this.jwksCache && Date.now() - this.jwksCache.fetchedAt <= JWKS_CACHE_TTL_MS) {
591
+ return this.jwksCache;
592
+ }
593
+ await this.refreshJwks();
594
+ if (!this.jwksCache) {
595
+ throw new IQAuthError("INTERNAL_ERROR", "JWKS cache unavailable after refresh");
563
596
  }
564
- return this.jwksCache?.keys.get(kid) ?? null;
597
+ return this.jwksCache;
565
598
  }
566
599
  async refreshJwks() {
567
600
  if (this.inFlightRefresh) {
@@ -588,35 +621,24 @@ var TokensModule = class {
588
621
  "Malformed JWKS response: expected { keys: [...] }"
589
622
  );
590
623
  }
591
- const keys = /* @__PURE__ */ new Map();
624
+ const byKid = /* @__PURE__ */ new Set();
592
625
  for (const key of jwks.keys) {
593
- if (!key || typeof key.kid !== "string" || typeof key.n !== "string" || typeof key.e !== "string") {
626
+ if (!key || typeof key.kid !== "string" || typeof key.n !== "string" && typeof key.x !== "string" || key.kty === "RSA" && (typeof key.n !== "string" || typeof key.e !== "string")) {
594
627
  throw new IQAuthError(
595
628
  "INTERNAL_ERROR",
596
629
  "Malformed JWKS response: key missing required fields"
597
630
  );
598
631
  }
599
- const pem = this.jwkToPem(key);
600
- keys.set(key.kid, pem);
632
+ byKid.add(key.kid);
601
633
  }
602
- this.jwksCache = { keys, fetchedAt: Date.now() };
634
+ const verifier = (0, import_jose.createLocalJWKSet)({ keys: jwks.keys });
635
+ this.jwksCache = { raw: jwks.keys, byKid, verifier, fetchedAt: Date.now() };
603
636
  } finally {
604
637
  this.inFlightRefresh = null;
605
638
  }
606
639
  })();
607
640
  return this.inFlightRefresh;
608
641
  }
609
- jwkToPem(jwk) {
610
- const keyObject = import_crypto.default.createPublicKey({
611
- key: {
612
- kty: jwk.kty,
613
- n: jwk.n,
614
- e: jwk.e
615
- },
616
- format: "jwk"
617
- });
618
- return keyObject.export({ type: "spki", format: "pem" });
619
- }
620
642
  /** @internal Exposed for testing — clears JWKS cache */
621
643
  clearCache() {
622
644
  this.jwksCache = null;
@@ -824,7 +846,7 @@ var PermissionsModule = class {
824
846
  };
825
847
 
826
848
  // src/modules/oidc.ts
827
- var import_crypto2 = __toESM(require("crypto"));
849
+ var import_crypto = __toESM(require("crypto"));
828
850
  var InMemoryOidcStateStore = class {
829
851
  constructor() {
830
852
  this.map = /* @__PURE__ */ new Map();
@@ -905,12 +927,12 @@ var OidcModule = class {
905
927
  * ready to redirect the user to.
906
928
  */
907
929
  async createAuthRequest(params) {
908
- const codeVerifier = base64UrlEncode(import_crypto2.default.randomBytes(32));
930
+ const codeVerifier = base64UrlEncode(import_crypto.default.randomBytes(32));
909
931
  const codeChallenge = base64UrlEncode(
910
- import_crypto2.default.createHash("sha256").update(codeVerifier).digest()
932
+ import_crypto.default.createHash("sha256").update(codeVerifier).digest()
911
933
  );
912
- const state = base64UrlEncode(import_crypto2.default.randomBytes(16));
913
- const nonce = base64UrlEncode(import_crypto2.default.randomBytes(16));
934
+ const state = base64UrlEncode(import_crypto.default.randomBytes(16));
935
+ const nonce = base64UrlEncode(import_crypto.default.randomBytes(16));
914
936
  await this.stateStore.set(state, {
915
937
  codeVerifier,
916
938
  state,
@@ -1858,7 +1880,7 @@ function assertPublishableKey(raw, opts) {
1858
1880
  if (!isValidIssuerUrl(decoded.iss)) {
1859
1881
  throw new IQAuthError(
1860
1882
  "CONFIG_INVALID",
1861
- `${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.`
1883
+ `${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 \u2014 the new key will encode a valid issuer URL.`
1862
1884
  );
1863
1885
  }
1864
1886
  return { mode: shapeMatch[1], iss: decoded.iss, appId: decoded.appId, tenantId: decoded.tenantId, kid: decoded.kid, raw };
@@ -1904,6 +1926,22 @@ function readCookie(req, name) {
1904
1926
  }
1905
1927
  return void 0;
1906
1928
  }
1929
+ function compileMatcher(pat) {
1930
+ if (pat instanceof RegExp) return (p) => pat.test(p);
1931
+ const re = new RegExp(
1932
+ "^" + pat.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "::DOUBLE::").replace(/\*/g, "[^/]*").replace(/::DOUBLE::/g, ".*") + "$"
1933
+ );
1934
+ return (p) => re.test(p);
1935
+ }
1936
+ function compileMatchers(pats) {
1937
+ return (pats ?? []).map(compileMatcher);
1938
+ }
1939
+ function pathOf(req) {
1940
+ const r = req;
1941
+ const raw = r.path || r.originalUrl || r.url || "/";
1942
+ const q = raw.indexOf("?");
1943
+ return q >= 0 ? raw.slice(0, q) : raw;
1944
+ }
1907
1945
  function clientFromPublishableKey(opts) {
1908
1946
  const parsed = assertPublishableKey(opts.publishableKey, { context: "iqAuthMiddleware" });
1909
1947
  const issuer = (opts.issuer ?? (parsed.iss.startsWith("http") ? parsed.iss : `https://${parsed.iss}`)).replace(/\/+$/, "");
@@ -1931,10 +1969,26 @@ function iqAuthMiddleware(clientOrOptions, options = {}) {
1931
1969
  onUnauthorized,
1932
1970
  onForbidden,
1933
1971
  onError,
1934
- accessCookieName = DEFAULT_ACCESS_COOKIE,
1935
- cookieAware = true
1972
+ cookieAware = true,
1973
+ cookieNames,
1974
+ protect,
1975
+ publicRoutes
1936
1976
  } = resolvedOptions;
1977
+ const accessCookieName = resolvedOptions.accessCookieName ?? cookieNames?.access ?? DEFAULT_ACCESS_COOKIE;
1978
+ const protectMatchers = compileMatchers(protect);
1979
+ const publicMatchers = compileMatchers(publicRoutes);
1980
+ const hasProtect = protectMatchers.length > 0;
1981
+ const hasPublic = publicMatchers.length > 0;
1937
1982
  return async (req, res, next) => {
1983
+ if (hasProtect || hasPublic) {
1984
+ const path = pathOf(req);
1985
+ if (hasPublic && publicMatchers.some((m) => m(path))) {
1986
+ return next();
1987
+ }
1988
+ if (hasProtect && !protectMatchers.some((m) => m(path))) {
1989
+ return next();
1990
+ }
1991
+ }
1938
1992
  let token;
1939
1993
  const authHeader = getAuthorizationHeader(req);
1940
1994
  if (authHeader && authHeader.startsWith("Bearer ")) {
@@ -2053,8 +2107,8 @@ function resolve(config) {
2053
2107
  publishableKey: config.publishableKey,
2054
2108
  secretKey: config.secretKey,
2055
2109
  issuer: (config.issuer ?? inferredIssuer).replace(/\/+$/, ""),
2056
- accessCookieName: config.accessCookieName ?? "iqauth_at",
2057
- refreshCookieName: config.refreshCookieName ?? "iqauth_rt",
2110
+ accessCookieName: config.accessCookieName ?? config.cookieNames?.access ?? "iqauth_at",
2111
+ refreshCookieName: config.refreshCookieName ?? config.cookieNames?.refresh ?? "iqauth_rt",
2058
2112
  cookieDomain: config.cookieDomain,
2059
2113
  sameSite: config.sameSite ?? "lax",
2060
2114
  secure: config.secure ?? true,
@@ -2218,6 +2272,15 @@ async function handleSignout(config, input) {
2218
2272
  } catch {
2219
2273
  }
2220
2274
  }
2275
+ if (input.endSsoSession !== false && input.ssoCookieHeader) {
2276
+ try {
2277
+ await cfg.fetchImpl(`${cfg.issuer}/oidc/sso-logout`, {
2278
+ method: "POST",
2279
+ headers: { Cookie: input.ssoCookieHeader }
2280
+ });
2281
+ } catch {
2282
+ }
2283
+ }
2221
2284
  return {
2222
2285
  status: 200,
2223
2286
  body: { success: true, data: { signedOut: true } },
@@ -2225,6 +2288,57 @@ async function handleSignout(config, input) {
2225
2288
  };
2226
2289
  }
2227
2290
 
2291
+ // src/server/provisioningBridge.ts
2292
+ function defaultIsUniqueViolation(err) {
2293
+ if (!err || typeof err !== "object") return false;
2294
+ const e = err;
2295
+ if (e.code === "23505") return true;
2296
+ if (typeof e.code === "string" && e.code.startsWith("SQLITE_CONSTRAINT")) return true;
2297
+ if (typeof e.message === "string" && /unique constraint|duplicate key/i.test(e.message)) return true;
2298
+ return false;
2299
+ }
2300
+ function createProvisioningBridge(options) {
2301
+ const { storage } = options;
2302
+ const isUniqueViolation = options.isUniqueViolation ?? defaultIsUniqueViolation;
2303
+ const roleOf = (claims) => {
2304
+ try {
2305
+ return options.roleMapper?.(claims) ?? null;
2306
+ } catch {
2307
+ return null;
2308
+ }
2309
+ };
2310
+ const ensureUser = async (claims) => {
2311
+ if (!claims?.sub) {
2312
+ throw new Error("createProvisioningBridge: claims.sub is required");
2313
+ }
2314
+ const byId = await storage.findByIqAuthUserId(claims.sub);
2315
+ if (byId) return { user: byId, claims, created: false, adopted: false };
2316
+ if (claims.email) {
2317
+ const byEmail = await storage.findByEmail(claims.email);
2318
+ if (byEmail) {
2319
+ if (storage.adoptByEmail) {
2320
+ const adopted = await storage.adoptByEmail(byEmail, claims, roleOf(claims));
2321
+ return { user: adopted, claims, created: false, adopted: true };
2322
+ }
2323
+ }
2324
+ }
2325
+ try {
2326
+ const created = await storage.insertFromClaims(claims, roleOf(claims));
2327
+ return { user: created, claims, created: true, adopted: false };
2328
+ } catch (err) {
2329
+ if (!isUniqueViolation(err)) throw err;
2330
+ const after = await storage.findByIqAuthUserId(claims.sub);
2331
+ if (after) return { user: after, claims, created: false, adopted: false };
2332
+ if (claims.email) {
2333
+ const byEmail = await storage.findByEmail(claims.email);
2334
+ if (byEmail) return { user: byEmail, claims, created: false, adopted: true };
2335
+ }
2336
+ throw err;
2337
+ }
2338
+ };
2339
+ return { ensureUser };
2340
+ }
2341
+
2228
2342
  // src/server.ts
2229
2343
  var ServerIQAuthClient = class extends IQAuthClient {
2230
2344
  constructor(config) {
@@ -2245,6 +2359,7 @@ function createServerClient(config) {
2245
2359
  IQAuthClient,
2246
2360
  IQAuthError,
2247
2361
  ServerIQAuthClient,
2362
+ createProvisioningBridge,
2248
2363
  createServerClient,
2249
2364
  handleCallback,
2250
2365
  handleRefresh,
package/dist/server.mjs CHANGED
@@ -1,18 +1,22 @@
1
- import {
2
- DEFAULT_ACCESS_COOKIE,
3
- DEFAULT_REFRESH_COOKIE,
4
- iqAuthMiddleware
5
- } from "./chunk-D72UL5HL.mjs";
6
1
  import {
7
2
  handleCallback,
8
3
  handleRefresh,
9
4
  handleSignout,
10
5
  serializeCookie
11
- } from "./chunk-M4J6BPK7.mjs";
12
- import "./chunk-QEJB7WEQ.mjs";
6
+ } from "./chunk-6TDJJER7.mjs";
7
+ import {
8
+ createProvisioningBridge
9
+ } from "./chunk-SL3KRS4W.mjs";
10
+ import {
11
+ DEFAULT_ACCESS_COOKIE,
12
+ DEFAULT_REFRESH_COOKIE,
13
+ iqAuthMiddleware
14
+ } from "./chunk-BVV54LPI.mjs";
15
+ import "./chunk-WQWBJSSS.mjs";
13
16
  import {
14
17
  IQAuthClient
15
- } from "./chunk-MDUHPQMM.mjs";
18
+ } from "./chunk-W3F4JYGP.mjs";
19
+ import "./chunk-UNYDG2L4.mjs";
16
20
  import {
17
21
  ErrorCodes,
18
22
  IQAuthError
@@ -38,6 +42,7 @@ export {
38
42
  IQAuthClient,
39
43
  IQAuthError,
40
44
  ServerIQAuthClient,
45
+ createProvisioningBridge,
41
46
  createServerClient,
42
47
  handleCallback,
43
48
  handleRefresh,
@@ -1,7 +1,7 @@
1
- import { I as IQAuthClient } from './client-Dv4v92Mj.mjs';
2
- import { b as IQAuthTokenClientConfig } from './types-Cxl3bQHt.mjs';
1
+ import { I as IQAuthClient } from './client-kYlJFgPv.mjs';
2
+ import { b as IQAuthTokenClientConfig } from './types-DZAflmmq.mjs';
3
3
  export { E as ErrorCodes, I as IQAuthError } from './errors-CDdl24MP.mjs';
4
- import 'jsonwebtoken';
4
+ import './tokens-DCyzzn8L.mjs';
5
5
 
6
6
  declare class ServiceIQAuthClient extends IQAuthClient {
7
7
  constructor(config: IQAuthTokenClientConfig);
package/dist/service.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { I as IQAuthClient } from './client-DXbHb2ul.js';
2
- import { b as IQAuthTokenClientConfig } from './types-Cxl3bQHt.js';
1
+ import { I as IQAuthClient } from './client-BNQe3AgF.js';
2
+ import { b as IQAuthTokenClientConfig } from './types-DZAflmmq.js';
3
3
  export { E as ErrorCodes, I as IQAuthError } from './errors-CDdl24MP.js';
4
- import 'jsonwebtoken';
4
+ import './tokens-aHiGFr_E.js';
5
5
 
6
6
  declare class ServiceIQAuthClient extends IQAuthClient {
7
7
  constructor(config: IQAuthTokenClientConfig);