@iqauth/sdk 2.2.0 → 2.3.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 (60) hide show
  1. package/README.md +24 -0
  2. package/dist/browser-session.d.mts +1 -2
  3. package/dist/browser-session.d.ts +1 -2
  4. package/dist/browser-session.js +89 -68
  5. package/dist/browser-session.mjs +2 -1
  6. package/dist/browser.d.mts +1 -1
  7. package/dist/browser.d.ts +1 -1
  8. package/dist/browser.js +13 -2
  9. package/dist/browser.mjs +2 -2
  10. package/dist/{chunk-D72UL5HL.mjs → chunk-EKTNEZIH.mjs} +4 -4
  11. package/dist/{chunk-M4J6BPK7.mjs → chunk-KGEPDXHU.mjs} +10 -1
  12. package/dist/{chunk-QZB745C2.mjs → chunk-RACIPVLD.mjs} +13 -2
  13. package/dist/chunk-UNYDG2L4.mjs +209 -0
  14. package/dist/{chunk-MDUHPQMM.mjs → chunk-W3F4JYGP.mjs} +8 -180
  15. package/dist/{chunk-QEJB7WEQ.mjs → chunk-WQWBJSSS.mjs} +1 -1
  16. package/dist/cli/index.mjs +1 -1
  17. package/dist/{client-DXbHb2ul.d.ts → client-DTX4hNdS.d.ts} +16 -21
  18. package/dist/{client-Dv4v92Mj.d.mts → client-vdh2a9fJ.d.mts} +16 -21
  19. package/dist/{doctor-XCI77BQS.mjs → doctor-A5E7LSFW.mjs} +1 -1
  20. package/dist/{express-BZmF1llh.d.mts → express-A0-dWEMy.d.mts} +1 -1
  21. package/dist/{express-B4o3P8vK.d.ts → express-Bo_pJKHN.d.ts} +1 -1
  22. package/dist/express.d.mts +75 -5
  23. package/dist/express.d.ts +75 -5
  24. package/dist/express.js +300 -70
  25. package/dist/express.mjs +208 -7
  26. package/dist/fastify.js +101 -70
  27. package/dist/fastify.mjs +8 -6
  28. package/dist/hono.js +100 -70
  29. package/dist/hono.mjs +7 -6
  30. package/dist/index.d.mts +2 -3
  31. package/dist/index.d.ts +2 -3
  32. package/dist/index.js +90 -69
  33. package/dist/index.mjs +15 -13
  34. package/dist/mobile.d.mts +1 -2
  35. package/dist/mobile.d.ts +1 -2
  36. package/dist/mobile.js +89 -68
  37. package/dist/mobile.mjs +2 -1
  38. package/dist/next.d.mts +9 -0
  39. package/dist/next.d.ts +9 -0
  40. package/dist/next.js +99 -1616
  41. package/dist/next.mjs +9 -9
  42. package/dist/react.d.mts +1 -1
  43. package/dist/react.d.ts +1 -1
  44. package/dist/react.js +13 -2
  45. package/dist/react.mjs +2 -2
  46. package/dist/server/handlers.d.mts +2 -0
  47. package/dist/server/handlers.d.ts +2 -0
  48. package/dist/server/handlers.js +10 -1
  49. package/dist/server/handlers.mjs +2 -2
  50. package/dist/server.d.mts +2 -3
  51. package/dist/server.d.ts +2 -3
  52. package/dist/server.js +99 -69
  53. package/dist/server.mjs +7 -6
  54. package/dist/service.d.mts +1 -2
  55. package/dist/service.d.ts +1 -2
  56. package/dist/service.js +89 -68
  57. package/dist/service.mjs +2 -1
  58. package/dist/{signIn-D_kP3v-c.d.mts → signIn-Cd0P4y9d.d.mts} +8 -0
  59. package/dist/{signIn-BVDTIA_t.d.ts → signIn-DKakyzeu.d.ts} +8 -0
  60. package/package.json +3 -2
@@ -1,3 +1,6 @@
1
+ import {
2
+ TokensModule
3
+ } from "./chunk-UNYDG2L4.mjs";
1
4
  import {
2
5
  IQAuthError
3
6
  } from "./chunk-6I6RM4MN.mjs";
@@ -160,177 +163,6 @@ function parseMfaResponse(data, browserSessionMode) {
160
163
  throw new Error("Unexpected MFA response shape");
161
164
  }
162
165
 
163
- // src/modules/tokens.ts
164
- import crypto from "crypto";
165
- import jwt from "jsonwebtoken";
166
- var JWKS_CACHE_TTL_MS = 60 * 60 * 1e3;
167
- var DEFAULT_TOKEN_ISSUER = [
168
- "https://auth.dispositioniq.com",
169
- "auth.dispositioniq.com"
170
- ];
171
- var DEFAULT_TOKEN_AUDIENCE = [
172
- "dispositioniq",
173
- "iqcapture",
174
- "iqreuse",
175
- "iqvalidate"
176
- ];
177
- var DEFAULT_CLOCK_TOLERANCE_SECONDS = 30;
178
- var TokensModule = class {
179
- constructor(baseUrl, options = {}) {
180
- this.jwksCache = null;
181
- this.inFlightRefresh = null;
182
- this.baseUrl = baseUrl;
183
- this.defaultIssuer = options.issuer ?? DEFAULT_TOKEN_ISSUER;
184
- this.defaultAudience = options.audience ?? DEFAULT_TOKEN_AUDIENCE;
185
- this.defaultClockTolerance = options.clockTolerance ?? DEFAULT_CLOCK_TOLERANCE_SECONDS;
186
- }
187
- /**
188
- * Verify a JWT access token using RS256 via JWKS from /.well-known/jwks.json.
189
- * Caches JWKS keys for 1 hour. Retries once on unknown `kid`.
190
- *
191
- * @remarks Validates against /.well-known/jwks.json. Issuer, audience, and
192
- * clock tolerance default to client config but can be overridden per call.
193
- */
194
- async verify(token, options = {}) {
195
- const decoded = jwt.decode(token, { complete: true });
196
- if (!decoded || typeof decoded === "string") {
197
- throw new IQAuthError("TOKEN_INVALID", "Unable to decode token");
198
- }
199
- const kid = decoded.header.kid;
200
- if (!kid) {
201
- throw new IQAuthError("TOKEN_INVALID", "Token missing kid header");
202
- }
203
- let publicKey = await this.getPublicKey(kid);
204
- if (!publicKey) {
205
- await this.refreshJwks();
206
- publicKey = await this.getPublicKey(kid);
207
- }
208
- if (!publicKey) {
209
- throw new IQAuthError("TOKEN_INVALID", `Unknown key ID: ${kid}`);
210
- }
211
- const issuer = options.issuer ?? this.defaultIssuer;
212
- const audience = options.audience ?? this.defaultAudience;
213
- const clockTolerance = options.clockTolerance ?? this.defaultClockTolerance;
214
- const algorithms = options.algorithms ?? ["RS256"];
215
- try {
216
- const verifyOptions = {
217
- algorithms,
218
- clockTolerance,
219
- // The jsonwebtoken types insist on tuple types for arrays; runtime
220
- // accepts plain string[] so we cast to satisfy the compiler.
221
- issuer,
222
- audience
223
- };
224
- const verified = jwt.verify(token, publicKey, verifyOptions);
225
- return verified;
226
- } catch (err) {
227
- if (err instanceof Error) {
228
- if (err.name === "TokenExpiredError") {
229
- throw new IQAuthError("TOKEN_EXPIRED", "Token has expired");
230
- }
231
- throw new IQAuthError("TOKEN_INVALID", err.message);
232
- }
233
- throw new IQAuthError("TOKEN_INVALID", "Token verification failed");
234
- }
235
- }
236
- /**
237
- * Decode a JWT without verification. Returns null if malformed.
238
- *
239
- * @remarks Local decode only — no network call
240
- */
241
- decode(token) {
242
- const decoded = jwt.decode(token);
243
- return decoded;
244
- }
245
- /**
246
- * Check if a token is expired based on the `exp` claim.
247
- *
248
- * @remarks Local check only — no network call
249
- */
250
- isExpired(token) {
251
- const claims = this.decode(token);
252
- if (!claims?.exp) return true;
253
- const now = Math.floor(Date.now() / 1e3);
254
- return claims.exp <= now;
255
- }
256
- /**
257
- * Get the claims from a token without verification.
258
- *
259
- * @remarks Local decode only — no network call
260
- */
261
- getClaims(token) {
262
- const claims = this.decode(token);
263
- if (!claims) {
264
- throw new IQAuthError("TOKEN_INVALID", "Unable to decode token claims");
265
- }
266
- return claims;
267
- }
268
- async getPublicKey(kid) {
269
- if (!this.jwksCache || Date.now() - this.jwksCache.fetchedAt > JWKS_CACHE_TTL_MS) {
270
- await this.refreshJwks();
271
- }
272
- return this.jwksCache?.keys.get(kid) ?? null;
273
- }
274
- async refreshJwks() {
275
- if (this.inFlightRefresh) {
276
- return this.inFlightRefresh;
277
- }
278
- this.inFlightRefresh = (async () => {
279
- try {
280
- const res = await fetch(`${this.baseUrl}/.well-known/jwks.json`);
281
- if (!res.ok) {
282
- throw new IQAuthError(
283
- "INTERNAL_ERROR",
284
- `Failed to fetch JWKS: ${res.status}`
285
- );
286
- }
287
- let jwks;
288
- try {
289
- jwks = await res.json();
290
- } catch {
291
- throw new IQAuthError("INTERNAL_ERROR", "Malformed JWKS response: invalid JSON");
292
- }
293
- if (!jwks || !Array.isArray(jwks.keys)) {
294
- throw new IQAuthError(
295
- "INTERNAL_ERROR",
296
- "Malformed JWKS response: expected { keys: [...] }"
297
- );
298
- }
299
- const keys = /* @__PURE__ */ new Map();
300
- for (const key of jwks.keys) {
301
- if (!key || typeof key.kid !== "string" || typeof key.n !== "string" || typeof key.e !== "string") {
302
- throw new IQAuthError(
303
- "INTERNAL_ERROR",
304
- "Malformed JWKS response: key missing required fields"
305
- );
306
- }
307
- const pem = this.jwkToPem(key);
308
- keys.set(key.kid, pem);
309
- }
310
- this.jwksCache = { keys, fetchedAt: Date.now() };
311
- } finally {
312
- this.inFlightRefresh = null;
313
- }
314
- })();
315
- return this.inFlightRefresh;
316
- }
317
- jwkToPem(jwk) {
318
- const keyObject = crypto.createPublicKey({
319
- key: {
320
- kty: jwk.kty,
321
- n: jwk.n,
322
- e: jwk.e
323
- },
324
- format: "jwk"
325
- });
326
- return keyObject.export({ type: "spki", format: "pem" });
327
- }
328
- /** @internal Exposed for testing — clears JWKS cache */
329
- clearCache() {
330
- this.jwksCache = null;
331
- }
332
- };
333
-
334
166
  // src/modules/sessions.ts
335
167
  var SessionsModule = class {
336
168
  constructor(http) {
@@ -532,7 +364,7 @@ var PermissionsModule = class {
532
364
  };
533
365
 
534
366
  // src/modules/oidc.ts
535
- import crypto2 from "crypto";
367
+ import crypto from "crypto";
536
368
  var InMemoryOidcStateStore = class {
537
369
  constructor() {
538
370
  this.map = /* @__PURE__ */ new Map();
@@ -613,12 +445,12 @@ var OidcModule = class {
613
445
  * ready to redirect the user to.
614
446
  */
615
447
  async createAuthRequest(params) {
616
- const codeVerifier = base64UrlEncode(crypto2.randomBytes(32));
448
+ const codeVerifier = base64UrlEncode(crypto.randomBytes(32));
617
449
  const codeChallenge = base64UrlEncode(
618
- crypto2.createHash("sha256").update(codeVerifier).digest()
450
+ crypto.createHash("sha256").update(codeVerifier).digest()
619
451
  );
620
- const state = base64UrlEncode(crypto2.randomBytes(16));
621
- const nonce = base64UrlEncode(crypto2.randomBytes(16));
452
+ const state = base64UrlEncode(crypto.randomBytes(16));
453
+ const nonce = base64UrlEncode(crypto.randomBytes(16));
622
454
  await this.stateStore.set(state, {
623
455
  codeVerifier,
624
456
  state,
@@ -1712,10 +1544,6 @@ var IQAuthClient = class _IQAuthClient {
1712
1544
 
1713
1545
  export {
1714
1546
  AuthModule,
1715
- DEFAULT_TOKEN_ISSUER,
1716
- DEFAULT_TOKEN_AUDIENCE,
1717
- DEFAULT_CLOCK_TOLERANCE_SECONDS,
1718
- TokensModule,
1719
1547
  SessionsModule,
1720
1548
  UsersModule,
1721
1549
  PermissionsModule,
@@ -93,7 +93,7 @@ function assertPublishableKey(raw, opts) {
93
93
  if (!isValidIssuerUrl(decoded.iss)) {
94
94
  throw new IQAuthError(
95
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.`
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 \u2014 the new key will encode a valid issuer URL.`
97
97
  );
98
98
  }
99
99
  return { mode: shapeMatch[1], iss: decoded.iss, appId: decoded.appId, tenantId: decoded.tenantId, kid: decoded.kid, raw };
@@ -17,7 +17,7 @@ async function run() {
17
17
  return;
18
18
  }
19
19
  case "doctor": {
20
- const { runDoctor } = await import("../doctor-XCI77BQS.mjs");
20
+ const { runDoctor } = await import("../doctor-A5E7LSFW.mjs");
21
21
  await runDoctor(rest);
22
22
  return;
23
23
  }
@@ -1,5 +1,4 @@
1
1
  import { I as IQAuthEnvironment, T as TokenPair, W as IQAuthRetryConfig, L as LoginResult, a$ as SignupRequest, D as MfaVerifyResult, d as SessionUser, J as JwtClaims, h as Session, U as UserProfile, H as ProvisionUserRequest, K as ProvisionUserResponse, G as UserPermissions, O as OidcDiscovery, t as JwksResponse, u as OidcTokenResponse, b0 as HostedClientContext, i as TenantInfo, C as CreateTenantRequest, j as UpdateTenantRequest, P as PromoteToVendorRequest, k as PromoteToVendorResult, a7 as TenantUser, l as InviteTenantUserRequest, m as InviteTenantUserResult, n as TenantUserRoleUpdate, M as MigrateUserRequest, E as PasswordPolicy, F as MfaPolicy, B as BrandingConfig, _ as AppInfo, $ as PermissionNodeInfo, Z as AppManifest, a0 as AppSyncResult, a1 as Role, a2 as CreateRoleRequest, a3 as UpdateRoleRequest, a4 as AssignRoleRequest, a5 as UserRoleAssignment, a8 as PermissionGroup, a9 as GroupPermission, aa as AddGroupPermissionRequest, ab as InheritanceRelation, a6 as UserGroupAssignment, ac as UserPermissionOverride, ad as AddUserOverrideRequest, ae as EffectivePermission, af as PermissionCheckResult, ah as CreateApiKeyRequest, ai as CreateApiKeyResult, ag as ApiKeyInfo, aj as ApiKeyIntrospection, al as CreateInviteRequest, ak as Invitation, am as InviteValidation, an as AcceptInviteRequest, ap as CreateWebhookRequest, aq as CreateWebhookResult, ao as WebhookEndpoint, ar as WebhookDelivery, as as WebhookTestResult, at as Entitlement, au as GrantEntitlementRequest, av as Vendor, aw as CreateVendorRequest, ax as UpdateVendorRequest, az as CreateSourceRequest, ay as Source, aA as UpdateSourceRequest, aC as CreateClientRequest, aB as Client, aD as UpdateClientRequest, aE as HierarchyVendor, aH as HierarchyLink, aL as MembershipWithDetails, aJ as CreateMembershipRequest, aI as Membership, aK as UpdateMembershipRequest, aM as AvailableScopesTree, aQ as ScopeSwitchResult, aR as GdprExportData, aS as PinStatus, aU as MfaAvailableMethods, aV as TotpEnrollResult, aW as TotpVerifyResult, aX as SmsEnrollResult, y as MfaEnrollment, aY as EmailEnrollResult, aZ as BackupCodesResult, a_ as BackupCodeCountResult, o as UpdateBrandingRequest, q as UploadAssetRequest, p as BrandingAsset, r as BrandingDomainMapping, a as IQAuthClientConfig, c as IQAuthBrowserSessionClientConfig, b as IQAuthTokenClientConfig } from './types-Cxl3bQHt.js';
2
- import jwt from 'jsonwebtoken';
3
2
 
4
3
  /**
5
4
  * SOURCE REFS:
@@ -90,6 +89,14 @@ declare class AuthModule {
90
89
  * - Route file: src/lib/crypto.ts (key rotation with kid)
91
90
  * - Verified claims: sub, email, name, tenantId, vendorId, roles, entitlements, sessionId, jti, iss, aud, exp, iat, scopeContext, loginMethod
92
91
  * - Last verified: Phase 0 Research Summary
92
+ *
93
+ * 2.3.0: Verify path swapped from `jsonwebtoken` (which depends on
94
+ * `node:crypto`) to `jose` so the SDK works on Next.js / Vercel / Cloudflare
95
+ * edge runtimes. Edge has only Web Crypto, so every call from a Next
96
+ * middleware previously threw and was wrapped as `TOKEN_INVALID`,
97
+ * indistinguishable from a real bad token. We keep our own JWKS fetch +
98
+ * cache to preserve INTERNAL_ERROR mapping for malformed JWKS payloads and
99
+ * to keep the kid-aware "Unknown key ID" diagnostic.
93
100
  */
94
101
 
95
102
  declare const DEFAULT_TOKEN_ISSUER: string[];
@@ -99,7 +106,7 @@ interface TokenVerifyOptions {
99
106
  issuer?: string | string[];
100
107
  audience?: string | string[];
101
108
  clockTolerance?: number;
102
- algorithms?: jwt.Algorithm[];
109
+ algorithms?: string[];
103
110
  }
104
111
  interface TokensModuleOptions {
105
112
  issuer?: string | string[];
@@ -115,34 +122,22 @@ declare class TokensModule {
115
122
  private defaultClockTolerance;
116
123
  constructor(baseUrl: string, options?: TokensModuleOptions);
117
124
  /**
118
- * Verify a JWT access token using RS256 via JWKS from /.well-known/jwks.json.
119
- * Caches JWKS keys for 1 hour. Retries once on unknown `kid`.
120
- *
121
- * @remarks Validates against /.well-known/jwks.json. Issuer, audience, and
122
- * clock tolerance default to client config but can be overridden per call.
125
+ * Verify a JWT access token using RS256/ES256 via JWKS from
126
+ * `/.well-known/jwks.json`. Backed by `jose` (Web Crypto) so it runs on
127
+ * Node, browser, and edge runtimes alike — no `node:crypto` dependency.
128
+ * Caches JWKS for 1 hour and refetches once on unknown `kid`.
123
129
  */
124
130
  verify(token: string, options?: TokenVerifyOptions): Promise<JwtClaims>;
125
131
  /**
126
132
  * Decode a JWT without verification. Returns null if malformed.
127
- *
128
- * @remarks Local decode only — no network call
129
133
  */
130
134
  decode(token: string): JwtClaims | null;
131
- /**
132
- * Check if a token is expired based on the `exp` claim.
133
- *
134
- * @remarks Local check only — no network call
135
- */
135
+ /** Check if a token is expired based on the `exp` claim. */
136
136
  isExpired(token: string): boolean;
137
- /**
138
- * Get the claims from a token without verification.
139
- *
140
- * @remarks Local decode only — no network call
141
- */
137
+ /** Get the claims from a token without verification. */
142
138
  getClaims(token: string): JwtClaims;
143
- private getPublicKey;
139
+ private ensureCache;
144
140
  private refreshJwks;
145
- private jwkToPem;
146
141
  /** @internal Exposed for testing — clears JWKS cache */
147
142
  clearCache(): void;
148
143
  }
@@ -1,5 +1,4 @@
1
1
  import { I as IQAuthEnvironment, T as TokenPair, W as IQAuthRetryConfig, L as LoginResult, a$ as SignupRequest, D as MfaVerifyResult, d as SessionUser, J as JwtClaims, h as Session, U as UserProfile, H as ProvisionUserRequest, K as ProvisionUserResponse, G as UserPermissions, O as OidcDiscovery, t as JwksResponse, u as OidcTokenResponse, b0 as HostedClientContext, i as TenantInfo, C as CreateTenantRequest, j as UpdateTenantRequest, P as PromoteToVendorRequest, k as PromoteToVendorResult, a7 as TenantUser, l as InviteTenantUserRequest, m as InviteTenantUserResult, n as TenantUserRoleUpdate, M as MigrateUserRequest, E as PasswordPolicy, F as MfaPolicy, B as BrandingConfig, _ as AppInfo, $ as PermissionNodeInfo, Z as AppManifest, a0 as AppSyncResult, a1 as Role, a2 as CreateRoleRequest, a3 as UpdateRoleRequest, a4 as AssignRoleRequest, a5 as UserRoleAssignment, a8 as PermissionGroup, a9 as GroupPermission, aa as AddGroupPermissionRequest, ab as InheritanceRelation, a6 as UserGroupAssignment, ac as UserPermissionOverride, ad as AddUserOverrideRequest, ae as EffectivePermission, af as PermissionCheckResult, ah as CreateApiKeyRequest, ai as CreateApiKeyResult, ag as ApiKeyInfo, aj as ApiKeyIntrospection, al as CreateInviteRequest, ak as Invitation, am as InviteValidation, an as AcceptInviteRequest, ap as CreateWebhookRequest, aq as CreateWebhookResult, ao as WebhookEndpoint, ar as WebhookDelivery, as as WebhookTestResult, at as Entitlement, au as GrantEntitlementRequest, av as Vendor, aw as CreateVendorRequest, ax as UpdateVendorRequest, az as CreateSourceRequest, ay as Source, aA as UpdateSourceRequest, aC as CreateClientRequest, aB as Client, aD as UpdateClientRequest, aE as HierarchyVendor, aH as HierarchyLink, aL as MembershipWithDetails, aJ as CreateMembershipRequest, aI as Membership, aK as UpdateMembershipRequest, aM as AvailableScopesTree, aQ as ScopeSwitchResult, aR as GdprExportData, aS as PinStatus, aU as MfaAvailableMethods, aV as TotpEnrollResult, aW as TotpVerifyResult, aX as SmsEnrollResult, y as MfaEnrollment, aY as EmailEnrollResult, aZ as BackupCodesResult, a_ as BackupCodeCountResult, o as UpdateBrandingRequest, q as UploadAssetRequest, p as BrandingAsset, r as BrandingDomainMapping, a as IQAuthClientConfig, c as IQAuthBrowserSessionClientConfig, b as IQAuthTokenClientConfig } from './types-Cxl3bQHt.mjs';
2
- import jwt from 'jsonwebtoken';
3
2
 
4
3
  /**
5
4
  * SOURCE REFS:
@@ -90,6 +89,14 @@ declare class AuthModule {
90
89
  * - Route file: src/lib/crypto.ts (key rotation with kid)
91
90
  * - Verified claims: sub, email, name, tenantId, vendorId, roles, entitlements, sessionId, jti, iss, aud, exp, iat, scopeContext, loginMethod
92
91
  * - Last verified: Phase 0 Research Summary
92
+ *
93
+ * 2.3.0: Verify path swapped from `jsonwebtoken` (which depends on
94
+ * `node:crypto`) to `jose` so the SDK works on Next.js / Vercel / Cloudflare
95
+ * edge runtimes. Edge has only Web Crypto, so every call from a Next
96
+ * middleware previously threw and was wrapped as `TOKEN_INVALID`,
97
+ * indistinguishable from a real bad token. We keep our own JWKS fetch +
98
+ * cache to preserve INTERNAL_ERROR mapping for malformed JWKS payloads and
99
+ * to keep the kid-aware "Unknown key ID" diagnostic.
93
100
  */
94
101
 
95
102
  declare const DEFAULT_TOKEN_ISSUER: string[];
@@ -99,7 +106,7 @@ interface TokenVerifyOptions {
99
106
  issuer?: string | string[];
100
107
  audience?: string | string[];
101
108
  clockTolerance?: number;
102
- algorithms?: jwt.Algorithm[];
109
+ algorithms?: string[];
103
110
  }
104
111
  interface TokensModuleOptions {
105
112
  issuer?: string | string[];
@@ -115,34 +122,22 @@ declare class TokensModule {
115
122
  private defaultClockTolerance;
116
123
  constructor(baseUrl: string, options?: TokensModuleOptions);
117
124
  /**
118
- * Verify a JWT access token using RS256 via JWKS from /.well-known/jwks.json.
119
- * Caches JWKS keys for 1 hour. Retries once on unknown `kid`.
120
- *
121
- * @remarks Validates against /.well-known/jwks.json. Issuer, audience, and
122
- * clock tolerance default to client config but can be overridden per call.
125
+ * Verify a JWT access token using RS256/ES256 via JWKS from
126
+ * `/.well-known/jwks.json`. Backed by `jose` (Web Crypto) so it runs on
127
+ * Node, browser, and edge runtimes alike — no `node:crypto` dependency.
128
+ * Caches JWKS for 1 hour and refetches once on unknown `kid`.
123
129
  */
124
130
  verify(token: string, options?: TokenVerifyOptions): Promise<JwtClaims>;
125
131
  /**
126
132
  * Decode a JWT without verification. Returns null if malformed.
127
- *
128
- * @remarks Local decode only — no network call
129
133
  */
130
134
  decode(token: string): JwtClaims | null;
131
- /**
132
- * Check if a token is expired based on the `exp` claim.
133
- *
134
- * @remarks Local check only — no network call
135
- */
135
+ /** Check if a token is expired based on the `exp` claim. */
136
136
  isExpired(token: string): boolean;
137
- /**
138
- * Get the claims from a token without verification.
139
- *
140
- * @remarks Local decode only — no network call
141
- */
137
+ /** Get the claims from a token without verification. */
142
138
  getClaims(token: string): JwtClaims;
143
- private getPublicKey;
139
+ private ensureCache;
144
140
  private refreshJwks;
145
- private jwkToPem;
146
141
  /** @internal Exposed for testing — clears JWKS cache */
147
142
  clearCache(): void;
148
143
  }
@@ -5,7 +5,7 @@ import {
5
5
  } from "./chunk-X3K3WOBR.mjs";
6
6
  import {
7
7
  parsePublishableKey
8
- } from "./chunk-QEJB7WEQ.mjs";
8
+ } from "./chunk-WQWBJSSS.mjs";
9
9
  import "./chunk-6I6RM4MN.mjs";
10
10
  import "./chunk-Y6FXYEAI.mjs";
11
11
 
@@ -1,4 +1,4 @@
1
- import { I as IQAuthClient } from './client-Dv4v92Mj.mjs';
1
+ import { I as IQAuthClient } from './client-vdh2a9fJ.mjs';
2
2
  import { J as JwtClaims, N as ExpressMiddlewareOptions, Q as IQAuthRequestLike, R as IQAuthResponseLike, V as IQAuthNextFunction } from './types-Cxl3bQHt.mjs';
3
3
 
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { I as IQAuthClient } from './client-DXbHb2ul.js';
1
+ import { I as IQAuthClient } from './client-DTX4hNdS.js';
2
2
  import { J as JwtClaims, N as ExpressMiddlewareOptions, Q as IQAuthRequestLike, R as IQAuthResponseLike, V as IQAuthNextFunction } from './types-Cxl3bQHt.js';
3
3
 
4
4
  /**
@@ -1,10 +1,9 @@
1
- import { I as IQAuthClient } from './client-Dv4v92Mj.mjs';
2
- import { C as CookieAwareMiddlewareOptions } from './express-BZmF1llh.mjs';
3
- export { i as iqAuthMiddleware } from './express-BZmF1llh.mjs';
1
+ import { I as IQAuthClient } from './client-vdh2a9fJ.mjs';
2
+ import { C as CookieAwareMiddlewareOptions } from './express-A0-dWEMy.mjs';
3
+ export { i as iqAuthMiddleware } from './express-A0-dWEMy.mjs';
4
4
  import { IQAuthHelperConfig } from './server/handlers.mjs';
5
5
  import { Q as IQAuthRequestLike, R as IQAuthResponseLike, V as IQAuthNextFunction } from './types-Cxl3bQHt.mjs';
6
6
  export { E as ErrorCodes, I as IQAuthError } from './errors-CDdl24MP.mjs';
7
- import 'jsonwebtoken';
8
7
 
9
8
  /**
10
9
  * @iqauth/sdk/express — drop-in Express adapter.
@@ -22,18 +21,89 @@ import 'jsonwebtoken';
22
21
  * app.use(iqAuth({ publishableKey: process.env.IQAUTH_PUBLISHABLE_KEY!, secretKey: process.env.IQAUTH_SECRET_KEY! }));
23
22
  */
24
23
 
24
+ interface InlineCallbackBrandedRenderArgs {
25
+ /** Issuer URL the SDK will use to mint the authorization code (publishable key origin). */
26
+ issuer: string;
27
+ /** Path of the JSON exchange endpoint to POST to (e.g. `/api/iqauth/callback/exchange`). */
28
+ exchangePath: string;
29
+ /** The raw `?code=` value from the OAuth redirect (already escaped for HTML). */
30
+ code: string;
31
+ /** The raw `?state=` value from the OAuth redirect (already escaped for HTML). */
32
+ state: string;
33
+ /** If `errorPath` is configured on the inline-callback options, it's threaded
34
+ * here so a custom render function can reuse it for its own catch handler.
35
+ * `""` when unset. */
36
+ errorPath?: string;
37
+ }
38
+ interface InlineCallbackBrandedConfig {
39
+ /**
40
+ * Optional override for the spinner page HTML. Receives the issuer URL, the
41
+ * exchange endpoint path, and the (HTML-escaped) `code` + `state` from the
42
+ * OAuth redirect. Returns a full HTML document. When omitted, a minimal
43
+ * neutral spinner is rendered.
44
+ */
45
+ render?: (args: InlineCallbackBrandedRenderArgs) => string;
46
+ }
47
+ interface InlineCallbackConfig {
48
+ /**
49
+ * When truthy, mount a GET-method handler on the same path as the POST
50
+ * callback so the OAuth redirect lands on a server-rendered page (no
51
+ * blank-tab while waiting for client JS). When `false`, only `POST` is
52
+ * mounted (the browser SDK posts the code + verifier itself).
53
+ *
54
+ * - `inlineCallback: true` — GET exchanges the code synchronously
55
+ * (PKCE verifier read from the `iqauth_pkce` first-party cookie set by
56
+ * the browser SDK before redirect) and 302s to the final URL.
57
+ *
58
+ * - `inlineCallback: { branded: true }` — GET returns a small spinner HTML
59
+ * document; the exchange happens via a sibling JSON endpoint at
60
+ * `${callbackPath}/exchange`.
61
+ *
62
+ * - `inlineCallback: { branded: { render } }` — same as above but lets
63
+ * you supply your own HTML (logo, copy, theme).
64
+ */
65
+ branded?: boolean | InlineCallbackBrandedConfig;
66
+ /**
67
+ * Where to redirect on a failed inline callback (state mismatch, missing
68
+ * code, code-exchange error from the issuer, etc). When omitted, the
69
+ * plain inline flow returns a JSON error body and the branded flow
70
+ * surfaces the failure via the spinner script's catch handler. When set,
71
+ * the GET handler 302s to this path with `?error=<code>` appended.
72
+ */
73
+ errorPath?: string;
74
+ /**
75
+ * Cookie name the browser SDK uses to publish the OAuth `state` value
76
+ * before redirect. Validated against the `?state=` query param on the
77
+ * return trip. Defaults to `iqauth_state`.
78
+ */
79
+ stateCookieName?: string;
80
+ /**
81
+ * Cookie name the browser SDK uses to publish the post-login destination
82
+ * before redirect. The inline GET handler reads it and 302s the user
83
+ * there after a successful exchange. Defaults to `iqauth_return_to`.
84
+ */
85
+ returnToCookieName?: string;
86
+ }
25
87
  interface IQAuthExpressOptions extends IQAuthHelperConfig, CookieAwareMiddlewareOptions {
26
88
  /** Mount path prefix for the auto-mounted helper routes. */
27
89
  mountPath?: string;
28
90
  /** Set to false to skip mounting helper routes (verify-only mode). */
29
91
  mountHelperRoutes?: boolean;
92
+ /**
93
+ * Mount a GET handler on the callback path so the OAuth redirect lands
94
+ * on a server-rendered page. Off by default (browser SDK posts the code
95
+ * itself). See {@link InlineCallbackConfig}.
96
+ */
97
+ inlineCallback?: boolean | InlineCallbackConfig;
30
98
  }
31
99
  interface ExpressLikeApp {
32
100
  post(path: string, handler: (req: any, res: any) => unknown | Promise<unknown>): unknown;
101
+ get?(path: string, handler: (req: any, res: any) => unknown | Promise<unknown>): unknown;
33
102
  use?: (...args: unknown[]) => unknown;
34
103
  }
35
104
  interface ExpressLikeRouter {
36
105
  post(path: string, handler: (req: any, res: any) => unknown | Promise<unknown>): unknown;
106
+ get?(path: string, handler: (req: any, res: any) => unknown | Promise<unknown>): unknown;
37
107
  }
38
108
  declare function iqAuth(options: IQAuthExpressOptions): {
39
109
  (req: IQAuthRequestLike, res: IQAuthResponseLike, next: IQAuthNextFunction): unknown;
@@ -42,4 +112,4 @@ declare function iqAuth(options: IQAuthExpressOptions): {
42
112
  client: IQAuthClient;
43
113
  };
44
114
 
45
- export { CookieAwareMiddlewareOptions, type IQAuthExpressOptions, iqAuth };
115
+ export { CookieAwareMiddlewareOptions, type IQAuthExpressOptions, type InlineCallbackBrandedConfig, type InlineCallbackBrandedRenderArgs, type InlineCallbackConfig, iqAuth };
package/dist/express.d.ts CHANGED
@@ -1,10 +1,9 @@
1
- import { I as IQAuthClient } from './client-DXbHb2ul.js';
2
- import { C as CookieAwareMiddlewareOptions } from './express-B4o3P8vK.js';
3
- export { i as iqAuthMiddleware } from './express-B4o3P8vK.js';
1
+ import { I as IQAuthClient } from './client-DTX4hNdS.js';
2
+ import { C as CookieAwareMiddlewareOptions } from './express-Bo_pJKHN.js';
3
+ export { i as iqAuthMiddleware } from './express-Bo_pJKHN.js';
4
4
  import { IQAuthHelperConfig } from './server/handlers.js';
5
5
  import { Q as IQAuthRequestLike, R as IQAuthResponseLike, V as IQAuthNextFunction } from './types-Cxl3bQHt.js';
6
6
  export { E as ErrorCodes, I as IQAuthError } from './errors-CDdl24MP.js';
7
- import 'jsonwebtoken';
8
7
 
9
8
  /**
10
9
  * @iqauth/sdk/express — drop-in Express adapter.
@@ -22,18 +21,89 @@ import 'jsonwebtoken';
22
21
  * app.use(iqAuth({ publishableKey: process.env.IQAUTH_PUBLISHABLE_KEY!, secretKey: process.env.IQAUTH_SECRET_KEY! }));
23
22
  */
24
23
 
24
+ interface InlineCallbackBrandedRenderArgs {
25
+ /** Issuer URL the SDK will use to mint the authorization code (publishable key origin). */
26
+ issuer: string;
27
+ /** Path of the JSON exchange endpoint to POST to (e.g. `/api/iqauth/callback/exchange`). */
28
+ exchangePath: string;
29
+ /** The raw `?code=` value from the OAuth redirect (already escaped for HTML). */
30
+ code: string;
31
+ /** The raw `?state=` value from the OAuth redirect (already escaped for HTML). */
32
+ state: string;
33
+ /** If `errorPath` is configured on the inline-callback options, it's threaded
34
+ * here so a custom render function can reuse it for its own catch handler.
35
+ * `""` when unset. */
36
+ errorPath?: string;
37
+ }
38
+ interface InlineCallbackBrandedConfig {
39
+ /**
40
+ * Optional override for the spinner page HTML. Receives the issuer URL, the
41
+ * exchange endpoint path, and the (HTML-escaped) `code` + `state` from the
42
+ * OAuth redirect. Returns a full HTML document. When omitted, a minimal
43
+ * neutral spinner is rendered.
44
+ */
45
+ render?: (args: InlineCallbackBrandedRenderArgs) => string;
46
+ }
47
+ interface InlineCallbackConfig {
48
+ /**
49
+ * When truthy, mount a GET-method handler on the same path as the POST
50
+ * callback so the OAuth redirect lands on a server-rendered page (no
51
+ * blank-tab while waiting for client JS). When `false`, only `POST` is
52
+ * mounted (the browser SDK posts the code + verifier itself).
53
+ *
54
+ * - `inlineCallback: true` — GET exchanges the code synchronously
55
+ * (PKCE verifier read from the `iqauth_pkce` first-party cookie set by
56
+ * the browser SDK before redirect) and 302s to the final URL.
57
+ *
58
+ * - `inlineCallback: { branded: true }` — GET returns a small spinner HTML
59
+ * document; the exchange happens via a sibling JSON endpoint at
60
+ * `${callbackPath}/exchange`.
61
+ *
62
+ * - `inlineCallback: { branded: { render } }` — same as above but lets
63
+ * you supply your own HTML (logo, copy, theme).
64
+ */
65
+ branded?: boolean | InlineCallbackBrandedConfig;
66
+ /**
67
+ * Where to redirect on a failed inline callback (state mismatch, missing
68
+ * code, code-exchange error from the issuer, etc). When omitted, the
69
+ * plain inline flow returns a JSON error body and the branded flow
70
+ * surfaces the failure via the spinner script's catch handler. When set,
71
+ * the GET handler 302s to this path with `?error=<code>` appended.
72
+ */
73
+ errorPath?: string;
74
+ /**
75
+ * Cookie name the browser SDK uses to publish the OAuth `state` value
76
+ * before redirect. Validated against the `?state=` query param on the
77
+ * return trip. Defaults to `iqauth_state`.
78
+ */
79
+ stateCookieName?: string;
80
+ /**
81
+ * Cookie name the browser SDK uses to publish the post-login destination
82
+ * before redirect. The inline GET handler reads it and 302s the user
83
+ * there after a successful exchange. Defaults to `iqauth_return_to`.
84
+ */
85
+ returnToCookieName?: string;
86
+ }
25
87
  interface IQAuthExpressOptions extends IQAuthHelperConfig, CookieAwareMiddlewareOptions {
26
88
  /** Mount path prefix for the auto-mounted helper routes. */
27
89
  mountPath?: string;
28
90
  /** Set to false to skip mounting helper routes (verify-only mode). */
29
91
  mountHelperRoutes?: boolean;
92
+ /**
93
+ * Mount a GET handler on the callback path so the OAuth redirect lands
94
+ * on a server-rendered page. Off by default (browser SDK posts the code
95
+ * itself). See {@link InlineCallbackConfig}.
96
+ */
97
+ inlineCallback?: boolean | InlineCallbackConfig;
30
98
  }
31
99
  interface ExpressLikeApp {
32
100
  post(path: string, handler: (req: any, res: any) => unknown | Promise<unknown>): unknown;
101
+ get?(path: string, handler: (req: any, res: any) => unknown | Promise<unknown>): unknown;
33
102
  use?: (...args: unknown[]) => unknown;
34
103
  }
35
104
  interface ExpressLikeRouter {
36
105
  post(path: string, handler: (req: any, res: any) => unknown | Promise<unknown>): unknown;
106
+ get?(path: string, handler: (req: any, res: any) => unknown | Promise<unknown>): unknown;
37
107
  }
38
108
  declare function iqAuth(options: IQAuthExpressOptions): {
39
109
  (req: IQAuthRequestLike, res: IQAuthResponseLike, next: IQAuthNextFunction): unknown;
@@ -42,4 +112,4 @@ declare function iqAuth(options: IQAuthExpressOptions): {
42
112
  client: IQAuthClient;
43
113
  };
44
114
 
45
- export { CookieAwareMiddlewareOptions, type IQAuthExpressOptions, iqAuth };
115
+ export { CookieAwareMiddlewareOptions, type IQAuthExpressOptions, type InlineCallbackBrandedConfig, type InlineCallbackBrandedRenderArgs, type InlineCallbackConfig, iqAuth };