@iqauth/sdk 2.7.0 → 2.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/dist/browser-session.d.mts +3 -3
  2. package/dist/browser-session.d.ts +3 -3
  3. package/dist/browser-session.js +31 -5
  4. package/dist/browser-session.mjs +1 -1
  5. package/dist/browser.d.mts +3 -3
  6. package/dist/browser.d.ts +3 -3
  7. package/dist/browser.js +23 -3
  8. package/dist/browser.mjs +1 -1
  9. package/dist/{chunk-YVALAG3B.mjs → chunk-25SSYDIP.mjs} +1 -1
  10. package/dist/{chunk-RTJAIBXY.mjs → chunk-4V7FKOTG.mjs} +23 -3
  11. package/dist/{chunk-SL3KRS4W.mjs → chunk-CIJORODR.mjs} +23 -1
  12. package/dist/chunk-JRDVUWAL.mjs +46 -0
  13. package/dist/{chunk-5T7GHBX6.mjs → chunk-TLET552H.mjs} +36 -0
  14. package/dist/{chunk-PMAFENVI.mjs → chunk-VYQ3ETCK.mjs} +27 -12
  15. package/dist/{chunk-RR2MGPTK.mjs → chunk-WHT6WKTY.mjs} +539 -83
  16. package/dist/{chunk-RUJXRTEW.mjs → chunk-WSH4SW7F.mjs} +122 -8
  17. package/dist/{chunk-JXQI62A7.mjs → chunk-ZLJPABB7.mjs} +31 -5
  18. package/dist/{client-BGFnBpfc.d.mts → client-D8L-PaWr.d.mts} +14 -4
  19. package/dist/{client-CDQ21LvW.d.ts → client-DkPL0EPZ.d.ts} +14 -4
  20. package/dist/{express-Piv2WhWM.d.ts → express-Budysq4h.d.ts} +2 -2
  21. package/dist/{express-CVNQEkOr.d.mts → express-DDTA3qV1.d.mts} +2 -2
  22. package/dist/express.d.mts +5 -5
  23. package/dist/express.d.ts +5 -5
  24. package/dist/express.js +217 -36
  25. package/dist/express.mjs +38 -26
  26. package/dist/fastify.d.mts +10 -2
  27. package/dist/fastify.d.ts +10 -2
  28. package/dist/fastify.js +260 -16
  29. package/dist/fastify.mjs +80 -5
  30. package/dist/hono.d.mts +10 -2
  31. package/dist/hono.d.ts +10 -2
  32. package/dist/hono.js +240 -16
  33. package/dist/hono.mjs +60 -5
  34. package/dist/{index-5KSZEnDe.d.ts → index-Cko-d5po.d.mts} +227 -5
  35. package/dist/{index-CKoZHAoc.d.mts → index-RNqwEcmY.d.ts} +227 -5
  36. package/dist/index.d.mts +5 -5
  37. package/dist/index.d.ts +5 -5
  38. package/dist/index.js +149 -26
  39. package/dist/index.mjs +5 -5
  40. package/dist/locales.d.mts +1 -1
  41. package/dist/locales.d.ts +1 -1
  42. package/dist/locales.js +36 -0
  43. package/dist/locales.mjs +1 -1
  44. package/dist/mobile.d.mts +3 -3
  45. package/dist/mobile.d.ts +3 -3
  46. package/dist/mobile.js +31 -5
  47. package/dist/mobile.mjs +1 -1
  48. package/dist/next.d.mts +10 -2
  49. package/dist/next.d.ts +10 -2
  50. package/dist/next.js +212 -11
  51. package/dist/next.mjs +62 -4
  52. package/dist/{provisioningBridge-M5G47LWO.d.mts → provisioningBridge-BXPMZCLe.d.ts} +30 -2
  53. package/dist/{provisioningBridge-CGpMRie4.d.ts → provisioningBridge-IEycmsgb.d.mts} +30 -2
  54. package/dist/react-permissions.d.mts +4 -4
  55. package/dist/react-permissions.d.ts +4 -4
  56. package/dist/react-permissions.mjs +4 -3
  57. package/dist/react.d.mts +4 -4
  58. package/dist/react.d.ts +4 -4
  59. package/dist/react.js +570 -41
  60. package/dist/react.mjs +19 -5
  61. package/dist/server/handlers.d.mts +56 -5
  62. package/dist/server/handlers.d.ts +56 -5
  63. package/dist/server/handlers.js +123 -8
  64. package/dist/server/handlers.mjs +3 -1
  65. package/dist/server.d.mts +28 -8
  66. package/dist/server.d.ts +28 -8
  67. package/dist/server.js +176 -14
  68. package/dist/server.mjs +9 -4
  69. package/dist/service.d.mts +3 -3
  70. package/dist/service.d.ts +3 -3
  71. package/dist/service.js +31 -5
  72. package/dist/service.mjs +1 -1
  73. package/dist/{signIn-T-CZ6t6r.d.mts → signIn-CReqfXsh.d.mts} +18 -1
  74. package/dist/{signIn-BLFnz8SV.d.ts → signIn-Cfa1GTpO.d.ts} +18 -1
  75. package/dist/{tokens-Bqhmqq_R.d.ts → tokens-9F6ETrzk.d.ts} +1 -1
  76. package/dist/{tokens-CITeoG6P.d.mts → tokens-B06VtvUi.d.mts} +1 -1
  77. package/dist/{types-XOV9XPVi.d.mts → types-Bn8O-OEd.d.mts} +66 -2
  78. package/dist/{types-XOV9XPVi.d.ts → types-Bn8O-OEd.d.ts} +66 -2
  79. package/dist/{types-BdQ2lqfT.d.mts → types-DnU2LhXR.d.mts} +6 -0
  80. package/dist/{types-BdQ2lqfT.d.ts → types-DnU2LhXR.d.ts} +6 -0
  81. package/dist/webhooks.d.mts +22 -9
  82. package/dist/webhooks.d.ts +22 -9
  83. package/dist/webhooks.js +27 -12
  84. package/dist/webhooks.mjs +1 -1
  85. package/dist/ws.d.mts +2 -2
  86. package/dist/ws.d.ts +2 -2
  87. package/docs/guides/invitations.md +65 -0
  88. package/package.json +7 -2
package/dist/index.d.ts CHANGED
@@ -1,14 +1,14 @@
1
- export { j as ApiKeysModule, g as AppsModule, A as AuthModule, B as BrandingModule, m as ClientsModule, C as CreateAppRequest, h as CreateAppResponse, E as EntitlementsModule, G as GdprModule, H as HierarchyModule, I as IQAuthClient, a as InMemoryOidcStateStore, k as InvitesModule, M as MembershipsModule, p as MfaModule, d as OidcAuthRequest, e as OidcCallbackResult, O as OidcModule, f as OidcModuleOptions, b as OidcStateStore, c as OidcStoredRequest, i as PermissionGroupsModule, P as PermissionsModule, o as PinModule, R as RolesModule, n as ScopeModule, S as SessionsModule, l as SourcesModule, T as TenantsModule, U as UsersModule, V as VendorsModule, W as WebhooksModule } from './client-CDQ21LvW.js';
1
+ export { j as ApiKeysModule, g as AppsModule, A as AuthModule, B as BrandingModule, m as ClientsModule, C as CreateAppRequest, h as CreateAppResponse, E as EntitlementsModule, G as GdprModule, H as HierarchyModule, I as IQAuthClient, a as InMemoryOidcStateStore, k as InvitesModule, M as MembershipsModule, p as MfaModule, d as OidcAuthRequest, e as OidcCallbackResult, O as OidcModule, f as OidcModuleOptions, b as OidcStateStore, c as OidcStoredRequest, i as PermissionGroupsModule, P as PermissionsModule, o as PinModule, R as RolesModule, n as ScopeModule, S as SessionsModule, l as SourcesModule, T as TenantsModule, U as UsersModule, V as VendorsModule, W as WebhooksModule } from './client-DkPL0EPZ.js';
2
2
  export { b as ErrorCode, E as ErrorCodes, I as IQAuthError, c as IQAuthErrorCode, a as IQ_AUTH_ERROR_CODES } from './errors-Jl1Jtm-6.js';
3
- export { i as iqAuthMiddleware } from './express-Piv2WhWM.js';
4
- export { b as DEFAULT_CLOCK_TOLERANCE_SECONDS, a as DEFAULT_TOKEN_AUDIENCE, D as DEFAULT_TOKEN_ISSUER, c as TokenVerifyOptions, T as TokensModule, d as TokensModuleOptions } from './tokens-Bqhmqq_R.js';
3
+ export { i as iqAuthMiddleware } from './express-Budysq4h.js';
4
+ export { b as DEFAULT_CLOCK_TOLERANCE_SECONDS, a as DEFAULT_TOKEN_AUDIENCE, D as DEFAULT_TOKEN_ISSUER, c as TokenVerifyOptions, T as TokensModule, d as TokensModuleOptions } from './tokens-9F6ETrzk.js';
5
5
  export { K as KeyMode, c as ParsedPublishableKey, P as PublishableKeyPayload, a as assertPublishableKey, e as encodePublishableKey, i as isPublishableKey, b as isSecretKey, p as parsePublishableKey } from './publishableKey-f2kq-rKw.js';
6
6
  export { UserinfoResponse, buildUserinfoResponse, handleUserinfo } from './server/handlers.js';
7
7
  export { VerifyWsUpgradeOptions, VerifyWsUpgradeResult, WsUpgradeRequestLike, verifyWsUpgrade } from './ws.js';
8
8
  export { CreateTestIssuerOptions, MintAuthCodeOptions, MintTokenOptions, TestIssuer, createTestIssuer } from './test.js';
9
- export { ap as AcceptInviteRequest, ac as AddGroupPermissionRequest, af as AddUserOverrideRequest, D as ApiErrorResponse, ai as ApiKeyInfo, al as ApiKeyIntrospection, E as ApiResponse, A as ApiSuccessResponse, a0 as AppInfo, $ as AppManifest, a2 as AppSyncResult, a6 as AssignRoleRequest, aO as AvailableScopesTree, b0 as BackupCodeCountResult, a$ as BackupCodesResult, u as BrandingAsset, B as BrandingConfig, w as BrandingDomainMapping, aD as Client, aj as CreateApiKeyRequest, ak as CreateApiKeyResult, aE as CreateClientRequest, an as CreateInviteRequest, aL as CreateMembershipRequest, a4 as CreateRoleRequest, aB as CreateSourceRequest, C as CreateTenantRequest, ay as CreateVendorRequest, ar as CreateWebhookRequest, as as CreateWebhookResult, ag as EffectivePermission, a_ as EmailEnrollResult, av as Entitlement, X as ExpressMiddlewareOptions, aT as GdprExportData, aw as GrantEntitlementRequest, ab as GroupPermission, aI as HierarchyClient, aJ as HierarchyLink, aH as HierarchySource, aG as HierarchyVendor, i as IQAuthBaseClaims, I as IQAuthBrowserSessionClientConfig, h as IQAuthClaims, e as IQAuthClientConfig, d as IQAuthEnvironment, c as IQAuthNextFunction, a as IQAuthRequestLike, b as IQAuthResponseLike, Y as IQAuthRetryConfig, f as IQAuthTokenClientConfig, Z as IQAuthVerifyConfig, ad as InheritanceRelation, am as Invitation, q as InviteTenantUserRequest, r as InviteTenantUserResult, ao as InviteValidation, x as JwksKey, y as JwksResponse, J as JwtClaims, L as LoginResult, aK as Membership, aN as MembershipWithDetails, aW as MfaAvailableMethods, G as MfaEnrollment, F as MfaMethod, Q as MfaPolicy, K as MfaVerifyResult, M as MigrateUserRequest, O as OidcDiscovery, z as OidcTokenResponse, N as PasswordPolicy, ah as PermissionCheckResult, aa as PermissionGroup, a1 as PermissionNodeInfo, _ as PermissionNodeManifest, aV as PinLoginResult, aU as PinStatus, P as PromoteToVendorRequest, p as PromoteToVendorResult, V as ProvisionUserRequest, W as ProvisionUserResponse, a3 as Role, g as ScopeContext, aS as ScopeSwitchResult, aP as ScopeTreeClient, aQ as ScopeTreeSource, aR as ScopeTreeVendor, m as Session, l as SessionAuthenticatedLoginResult, S as SessionUser, aZ as SmsEnrollResult, aA as Source, j as Tenant, n as TenantInfo, a9 as TenantUser, s as TenantUserRoleUpdate, k as TokenAuthenticatedLoginResult, T as TokenPair, aX as TotpEnrollResult, H as TotpEnrollmentResult, aY as TotpVerifyResult, t as UpdateBrandingRequest, aF as UpdateClientRequest, aM as UpdateMembershipRequest, a5 as UpdateRoleRequest, aC as UpdateSourceRequest, o as UpdateTenantRequest, az as UpdateVendorRequest, v as UploadAssetRequest, a8 as UserGroupAssignment, ae as UserPermissionOverride, R as UserPermissions, U as UserProfile, a7 as UserRoleAssignment, ax as Vendor, at as WebhookDelivery, aq as WebhookEndpoint, au as WebhookTestResult } from './types-XOV9XPVi.js';
9
+ export { ap as AcceptInviteRequest, ac as AddGroupPermissionRequest, af as AddUserOverrideRequest, D as ApiErrorResponse, ai as ApiKeyInfo, al as ApiKeyIntrospection, E as ApiResponse, A as ApiSuccessResponse, a0 as AppInfo, $ as AppManifest, a2 as AppSyncResult, a6 as AssignRoleRequest, aO as AvailableScopesTree, b0 as BackupCodeCountResult, a$ as BackupCodesResult, u as BrandingAsset, B as BrandingConfig, w as BrandingDomainMapping, aD as Client, aj as CreateApiKeyRequest, ak as CreateApiKeyResult, aE as CreateClientRequest, an as CreateInviteRequest, aL as CreateMembershipRequest, a4 as CreateRoleRequest, aB as CreateSourceRequest, C as CreateTenantRequest, ay as CreateVendorRequest, ar as CreateWebhookRequest, as as CreateWebhookResult, ag as EffectivePermission, a_ as EmailEnrollResult, av as Entitlement, X as ExpressMiddlewareOptions, aT as GdprExportData, aw as GrantEntitlementRequest, ab as GroupPermission, aI as HierarchyClient, aJ as HierarchyLink, aH as HierarchySource, aG as HierarchyVendor, i as IQAuthBaseClaims, I as IQAuthBrowserSessionClientConfig, h as IQAuthClaims, e as IQAuthClientConfig, d as IQAuthEnvironment, c as IQAuthNextFunction, a as IQAuthRequestLike, b as IQAuthResponseLike, Y as IQAuthRetryConfig, f as IQAuthTokenClientConfig, Z as IQAuthVerifyConfig, ad as InheritanceRelation, am as Invitation, q as InviteTenantUserRequest, r as InviteTenantUserResult, ao as InviteValidation, x as JwksKey, y as JwksResponse, J as JwtClaims, L as LoginResult, aK as Membership, aN as MembershipWithDetails, aW as MfaAvailableMethods, G as MfaEnrollment, F as MfaMethod, Q as MfaPolicy, K as MfaVerifyResult, M as MigrateUserRequest, O as OidcDiscovery, z as OidcTokenResponse, N as PasswordPolicy, ah as PermissionCheckResult, aa as PermissionGroup, a1 as PermissionNodeInfo, _ as PermissionNodeManifest, aV as PinLoginResult, aU as PinStatus, P as PromoteToVendorRequest, p as PromoteToVendorResult, V as ProvisionUserRequest, W as ProvisionUserResponse, a3 as Role, g as ScopeContext, aS as ScopeSwitchResult, aP as ScopeTreeClient, aQ as ScopeTreeSource, aR as ScopeTreeVendor, m as Session, l as SessionAuthenticatedLoginResult, S as SessionUser, aZ as SmsEnrollResult, aA as Source, j as Tenant, n as TenantInfo, a9 as TenantUser, s as TenantUserRoleUpdate, k as TokenAuthenticatedLoginResult, T as TokenPair, aX as TotpEnrollResult, H as TotpEnrollmentResult, aY as TotpVerifyResult, t as UpdateBrandingRequest, aF as UpdateClientRequest, aM as UpdateMembershipRequest, a5 as UpdateRoleRequest, aC as UpdateSourceRequest, o as UpdateTenantRequest, az as UpdateVendorRequest, v as UploadAssetRequest, a8 as UserGroupAssignment, ae as UserPermissionOverride, R as UserPermissions, U as UserProfile, a7 as UserRoleAssignment, ax as Vendor, at as WebhookDelivery, aq as WebhookEndpoint, au as WebhookTestResult } from './types-Bn8O-OEd.js';
10
10
  export { IQAUTH_SIGNATURE_HEADER, IQAuthEvent, IQAuthWebhookEvent, LEGACY_SIGNATURE_HEADERS, ParseWebhookEventOptions, VerifyWebhookOptions, WebhookSignatureError, isValidWebhookSignature, parseWebhookEvent, verifyWebhookSignature } from './webhooks.js';
11
- export { P as ProvisioningBridge, a as ProvisioningBridgeOptions, d as ProvisioningContext, b as ProvisioningStorage, c as createProvisioningBridge } from './provisioningBridge-CGpMRie4.js';
11
+ export { P as ProvisioningBridge, a as ProvisioningBridgeOptions, d as ProvisioningContext, b as ProvisioningStorage, c as createProvisioningBridge } from './provisioningBridge-BXPMZCLe.js';
12
12
 
13
13
  /**
14
14
  * Shared wildcard permission utilities.
package/dist/index.js CHANGED
@@ -393,17 +393,27 @@ function parseLoginResponse(data, browserSessionMode) {
393
393
  tenants: data.tenants
394
394
  };
395
395
  }
396
+ if (data.type === "scope_selection" && data.scopeSelectionToken && data.scopes && data.tenantId) {
397
+ return {
398
+ status: "scope_selection",
399
+ scopeSelectionToken: data.scopeSelectionToken,
400
+ tenantId: data.tenantId,
401
+ scopes: data.scopes
402
+ };
403
+ }
396
404
  throw new Error("Unexpected login response shape");
397
405
  }
398
406
  var AuthModule = class {
399
407
  constructor(http) {
400
408
  this.http = http;
401
409
  }
402
- async login(email, password) {
410
+ async login(email, password, opts) {
411
+ const body = { email, password };
412
+ if (opts?.scopeHint) body.scopeHint = opts.scopeHint;
403
413
  const data = await this.http.request(
404
414
  "POST",
405
415
  "/api/v1/auth/login",
406
- { email, password },
416
+ body,
407
417
  { skipAutoRefresh: true }
408
418
  );
409
419
  return parseLoginResponse(data, this.http.isBrowserSession());
@@ -441,13 +451,29 @@ var AuthModule = class {
441
451
  method
442
452
  }, { skipAutoRefresh: true });
443
453
  }
444
- async selectTenant(tenantSelectionToken, tenantId) {
454
+ async selectTenant(tenantSelectionToken, tenantId, opts) {
455
+ const body = { tenantSelectionToken, tenantId };
456
+ if (opts?.scopeHint) body.scopeHint = opts.scopeHint;
445
457
  const data = await this.http.request(
446
458
  "POST",
447
459
  "/api/v1/auth/select-tenant",
460
+ body,
461
+ { skipAutoRefresh: true }
462
+ );
463
+ return parseLoginResponse(data, this.http.isBrowserSession());
464
+ }
465
+ /**
466
+ * Task #171 — redeem a scope-selection token + chosen membership for a
467
+ * real authenticated session. `membershipId` must be one of the scopes
468
+ * returned in the prior `scope_selection` envelope.
469
+ */
470
+ async selectScope(scopeSelectionToken, membershipId) {
471
+ const data = await this.http.request(
472
+ "POST",
473
+ "/api/v1/auth/select-scope",
448
474
  {
449
- tenantSelectionToken,
450
- tenantId
475
+ scopeSelectionToken,
476
+ membershipId
451
477
  },
452
478
  { skipAutoRefresh: true }
453
479
  );
@@ -2321,7 +2347,11 @@ async function buildUserinfoResponse(claims, opts = {}) {
2321
2347
  tenantId: claims.tenantId,
2322
2348
  vendorId: claims.vendorId,
2323
2349
  roles: claims.roles ?? [],
2324
- entitlements: claims.entitlements ?? []
2350
+ entitlements: claims.entitlements ?? [],
2351
+ // Task #171 — project the active source/client scope onto the userinfo
2352
+ // payload so server handlers (`getSessionUser`, `/api/iqauth/userinfo`)
2353
+ // expose it without consumers having to re-decode the JWT.
2354
+ ...claims.scopeContext !== void 0 ? { scopeContext: claims.scopeContext } : {}
2325
2355
  };
2326
2356
  const enriched = opts.enrich ? await opts.enrich(claims) : null;
2327
2357
  const user = enriched ? { ...baseUser, ...enriched } : baseUser;
@@ -2336,19 +2366,62 @@ async function buildUserinfoResponse(claims, opts = {}) {
2336
2366
  }
2337
2367
  var ACCESS_TOKEN_TTL_SECONDS = 60 * 15;
2338
2368
  var REFRESH_TOKEN_TTL_SECONDS = 60 * 60 * 24 * 30;
2369
+ function assertCookiePrefixInvariants(name, secure, path, domain) {
2370
+ if (name.startsWith("__Host-")) {
2371
+ if (!secure) {
2372
+ throw new IQAuthError(
2373
+ "config_invalid",
2374
+ `Cookie "${name}" uses the __Host- prefix, which browsers only accept on a Secure cookie. Set secure:true (and serve over HTTPS).`
2375
+ );
2376
+ }
2377
+ if (path !== "/") {
2378
+ throw new IQAuthError(
2379
+ "config_invalid",
2380
+ `Cookie "${name}" uses the __Host- prefix, which requires Path=/ (got "${path}"). Remove cookiePath or set it to "/".`
2381
+ );
2382
+ }
2383
+ if (domain) {
2384
+ throw new IQAuthError(
2385
+ "config_invalid",
2386
+ `Cookie "${name}" uses the __Host- prefix, which forbids a Domain attribute (the cookie is host-locked). Remove cookieDomain.`
2387
+ );
2388
+ }
2389
+ } else if (name.startsWith("__Secure-") && !secure) {
2390
+ throw new IQAuthError(
2391
+ "config_invalid",
2392
+ `Cookie "${name}" uses the __Secure- prefix, which browsers only accept on a Secure cookie. Set secure:true (and serve over HTTPS).`
2393
+ );
2394
+ }
2395
+ }
2339
2396
  function resolve(config) {
2340
2397
  const parsed = assertPublishableKey(config.publishableKey, { context: "@iqauth/sdk helpers" });
2341
2398
  const inferredIssuer = parsed.iss.startsWith("http") ? parsed.iss : `https://${parsed.iss}`;
2399
+ maybeWarnDefaultSignoutRegistry(config);
2400
+ const secure = config.secure ?? true;
2401
+ if (config.secure === false && config.allowInsecureCookies !== true) {
2402
+ throw new IQAuthError(
2403
+ "config_invalid",
2404
+ "Refusing to issue auth cookies with secure:false \u2014 this exposes session cookies over plaintext HTTP. For local HTTP development, set allowInsecureCookies:true to acknowledge the risk. Production MUST use HTTPS with secure cookies."
2405
+ );
2406
+ }
2407
+ const accessCookieName = config.accessCookieName ?? config.cookieNames?.access ?? "iqauth_at";
2408
+ const refreshCookieName = config.refreshCookieName ?? config.cookieNames?.refresh ?? "iqauth_rt";
2409
+ const stateCookieName = config.stateCookieName ?? "iqauth_state";
2410
+ const cookiePath = config.cookiePath ?? "/";
2411
+ const cookieDomain = config.cookieDomain;
2412
+ for (const name of [accessCookieName, refreshCookieName, stateCookieName]) {
2413
+ assertCookiePrefixInvariants(name, secure, cookiePath, cookieDomain);
2414
+ }
2342
2415
  return {
2343
2416
  publishableKey: config.publishableKey,
2344
2417
  secretKey: config.secretKey,
2345
2418
  issuer: (config.issuer ?? inferredIssuer).replace(/\/+$/, ""),
2346
- accessCookieName: config.accessCookieName ?? config.cookieNames?.access ?? "iqauth_at",
2347
- refreshCookieName: config.refreshCookieName ?? config.cookieNames?.refresh ?? "iqauth_rt",
2348
- cookieDomain: config.cookieDomain,
2419
+ accessCookieName,
2420
+ refreshCookieName,
2421
+ cookieDomain,
2349
2422
  sameSite: config.sameSite ?? "lax",
2350
- secure: config.secure ?? true,
2351
- cookiePath: config.cookiePath ?? "/",
2423
+ secure,
2424
+ cookiePath,
2352
2425
  tokenPath: config.tokenPath ?? "/oidc/token",
2353
2426
  refreshPath: config.refreshPath ?? "/api/v1/auth/refresh",
2354
2427
  logoutPath: config.logoutPath ?? "/api/v1/auth/logout",
@@ -2361,7 +2434,9 @@ function resolve(config) {
2361
2434
  debug: config.debug,
2362
2435
  onTimingEvent: config.onTimingEvent,
2363
2436
  signoutRegistry: config.signoutRegistry ?? defaultSignoutRegistry,
2364
- signoutMarkerTtlMs: config.signoutMarkerTtlMs ?? DEFAULT_SIGNOUT_TTL_MS
2437
+ signoutMarkerTtlMs: config.signoutMarkerTtlMs ?? DEFAULT_SIGNOUT_TTL_MS,
2438
+ requireOAuthState: config.requireOAuthState ?? true,
2439
+ stateCookieName: config.stateCookieName ?? "iqauth_state"
2365
2440
  };
2366
2441
  }
2367
2442
  var DEFAULT_SIGNOUT_TTL_MS = 6e4;
@@ -2389,6 +2464,15 @@ var defaultSignoutRegistry = {
2389
2464
  return true;
2390
2465
  }
2391
2466
  };
2467
+ var warnedDefaultSignoutRegistry = false;
2468
+ function maybeWarnDefaultSignoutRegistry(config) {
2469
+ if (warnedDefaultSignoutRegistry) return;
2470
+ if (config.signoutRegistry) return;
2471
+ warnedDefaultSignoutRegistry = true;
2472
+ console.warn(
2473
+ "[IQAuth] Using the in-memory signout registry (process-local). Signout idempotency is NOT shared across instances \u2014 in a multi-replica deployment a /refresh racing a /signout on another replica can reissue cookies after sign-out. Plug a shared backend (e.g. Redis) into IQAuthHelperConfig.signoutRegistry to fix this and silence this warning."
2474
+ );
2475
+ }
2392
2476
  var TOKENS_CACHE = /* @__PURE__ */ new Map();
2393
2477
  function getTokensFor(issuer) {
2394
2478
  let m = TOKENS_CACHE.get(issuer);
@@ -2409,7 +2493,10 @@ async function handleUserinfo(config, input) {
2409
2493
  }
2410
2494
  let claims;
2411
2495
  try {
2412
- claims = await getTokensFor(cfg.issuer).verify(input.accessToken, config.verify);
2496
+ claims = await getTokensFor(cfg.issuer).verify(input.accessToken, {
2497
+ issuer: cfg.issuer,
2498
+ ...config.verify
2499
+ });
2413
2500
  } catch (err) {
2414
2501
  const code = err instanceof IQAuthError ? err.code : err.code || "TOKEN_INVALID";
2415
2502
  const message = err instanceof Error ? err.message : "Access token verification failed";
@@ -2890,12 +2977,8 @@ function verifyWebhookSignature(opts) {
2890
2977
  }
2891
2978
  const body = toBuffer(opts.payload);
2892
2979
  const { modern, legacy } = computeSignatures(opts.secret, body, t);
2893
- const matched = v1.some((sig) => {
2894
- const lower = sig.toLowerCase();
2895
- if (timingSafeEqualHex(lower, modern)) return true;
2896
- if (legacy && timingSafeEqualHex(lower, legacy)) return true;
2897
- return false;
2898
- });
2980
+ const expected = Number.isFinite(t) ? legacy : modern;
2981
+ const matched = expected !== null && v1.some((sig) => timingSafeEqualHex(sig.toLowerCase(), expected));
2899
2982
  if (!matched) {
2900
2983
  throw new WebhookSignatureError("SIGNATURE_MISMATCH", "Webhook signature does not match expected value");
2901
2984
  }
@@ -2905,6 +2988,29 @@ function verifyWebhookSignature(opts) {
2905
2988
  } catch {
2906
2989
  throw new WebhookSignatureError("MALFORMED_BODY", "Webhook body is not valid JSON");
2907
2990
  }
2991
+ if (!Number.isFinite(t) && !opts.allowMissingTimestamp) {
2992
+ const rawTime = parsed.time;
2993
+ if (typeof rawTime !== "string" || !rawTime) {
2994
+ throw new WebhookSignatureError(
2995
+ "MISSING_TIMESTAMP",
2996
+ "Modern webhook delivery has no header `t=` and no envelope `time`; cannot enforce replay protection. Use parseWebhookEvent, or set allowMissingTimestamp:true (insecure)."
2997
+ );
2998
+ }
2999
+ const eventMs = Date.parse(rawTime);
3000
+ if (!Number.isFinite(eventMs)) {
3001
+ throw new WebhookSignatureError(
3002
+ "MALFORMED_TIMESTAMP",
3003
+ `Envelope \`time\` is not a valid ISO timestamp: ${rawTime}`
3004
+ );
3005
+ }
3006
+ const nowMs = (opts.nowSeconds ?? Math.floor(Date.now() / 1e3)) * 1e3;
3007
+ if (Math.abs(nowMs - eventMs) > tolerance * 1e3) {
3008
+ throw new WebhookSignatureError(
3009
+ "TIMESTAMP_OUT_OF_TOLERANCE",
3010
+ `Envelope time ${rawTime} is outside the ${tolerance}s tolerance window`
3011
+ );
3012
+ }
3013
+ }
2908
3014
  return parsed;
2909
3015
  }
2910
3016
  function isValidWebhookSignature(opts) {
@@ -2974,12 +3080,8 @@ function parseWebhookEvent(rawBody, headers, secrets, opts = {}) {
2974
3080
  const secret = secrets[i];
2975
3081
  if (!secret) continue;
2976
3082
  const { modern, legacy } = computeSignatures(secret, body, t);
2977
- const ok = v1.some((sig) => {
2978
- const lower = sig.toLowerCase();
2979
- if (timingSafeEqualHex(lower, modern)) return true;
2980
- if (legacy && timingSafeEqualHex(lower, legacy)) return true;
2981
- return false;
2982
- });
3083
+ const expected = Number.isFinite(t) ? legacy : modern;
3084
+ const ok = expected !== null && v1.some((sig) => timingSafeEqualHex(sig.toLowerCase(), expected));
2983
3085
  if (ok) {
2984
3086
  verifiedIdx = i;
2985
3087
  break;
@@ -3036,6 +3138,13 @@ function parseWebhookEvent(rawBody, headers, secrets, opts = {}) {
3036
3138
  }
3037
3139
 
3038
3140
  // src/server/provisioningBridge.ts
3141
+ var ProvisioningError = class extends Error {
3142
+ constructor(code, message) {
3143
+ super(message);
3144
+ this.name = "ProvisioningError";
3145
+ this.code = code;
3146
+ }
3147
+ };
3039
3148
  function defaultIsUniqueViolation(err) {
3040
3149
  if (!err || typeof err !== "object") return false;
3041
3150
  const e = err;
@@ -3047,6 +3156,16 @@ function defaultIsUniqueViolation(err) {
3047
3156
  function createProvisioningBridge(options) {
3048
3157
  const { storage } = options;
3049
3158
  const isUniqueViolation = options.isUniqueViolation ?? defaultIsUniqueViolation;
3159
+ const allowUnverifiedEmailAdopt = options.allowUnverifiedEmailAdopt === true;
3160
+ const emailVerified = (claims) => claims.email_verified === true;
3161
+ const assertAdoptAllowed = (claims) => {
3162
+ if (!allowUnverifiedEmailAdopt && !emailVerified(claims)) {
3163
+ throw new ProvisioningError(
3164
+ "UNVERIFIED_EMAIL_ADOPT_REFUSED",
3165
+ "Refusing to adopt a pre-existing local account from an unverified email (claims.email_verified !== true). Set allowUnverifiedEmailAdopt:true only if your issuer is trusted to never emit unverified emails for adoption."
3166
+ );
3167
+ }
3168
+ };
3050
3169
  const roleOf = (claims) => {
3051
3170
  try {
3052
3171
  return options.roleMapper?.(claims) ?? null;
@@ -3063,6 +3182,7 @@ function createProvisioningBridge(options) {
3063
3182
  if (claims.email) {
3064
3183
  const byEmail = await storage.findByEmail(claims.email);
3065
3184
  if (byEmail) {
3185
+ assertAdoptAllowed(claims);
3066
3186
  if (storage.adoptByEmail) {
3067
3187
  const adopted = await storage.adoptByEmail(byEmail, claims, roleOf(claims));
3068
3188
  return { user: adopted, claims, created: false, adopted: true };
@@ -3078,7 +3198,10 @@ function createProvisioningBridge(options) {
3078
3198
  if (after) return { user: after, claims, created: false, adopted: false };
3079
3199
  if (claims.email) {
3080
3200
  const byEmail = await storage.findByEmail(claims.email);
3081
- if (byEmail) return { user: byEmail, claims, created: false, adopted: true };
3201
+ if (byEmail) {
3202
+ assertAdoptAllowed(claims);
3203
+ return { user: byEmail, claims, created: false, adopted: true };
3204
+ }
3082
3205
  }
3083
3206
  throw err;
3084
3207
  }
package/dist/index.mjs CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-GLXSIGVS.mjs";
5
5
  import {
6
6
  createProvisioningBridge
7
- } from "./chunk-SL3KRS4W.mjs";
7
+ } from "./chunk-CIJORODR.mjs";
8
8
  import {
9
9
  createTestIssuer
10
10
  } from "./chunk-WIFG74IK.mjs";
@@ -15,17 +15,17 @@ import {
15
15
  isValidWebhookSignature,
16
16
  parseWebhookEvent,
17
17
  verifyWebhookSignature
18
- } from "./chunk-PMAFENVI.mjs";
18
+ } from "./chunk-VYQ3ETCK.mjs";
19
19
  import {
20
20
  verifyWsUpgrade
21
21
  } from "./chunk-WCELYTJ3.mjs";
22
22
  import {
23
23
  iqAuthMiddleware
24
- } from "./chunk-YVALAG3B.mjs";
24
+ } from "./chunk-25SSYDIP.mjs";
25
25
  import {
26
26
  buildUserinfoResponse,
27
27
  handleUserinfo
28
- } from "./chunk-RUJXRTEW.mjs";
28
+ } from "./chunk-WSH4SW7F.mjs";
29
29
  import {
30
30
  assertPublishableKey,
31
31
  encodePublishableKey,
@@ -59,7 +59,7 @@ import {
59
59
  UsersModule,
60
60
  VendorsModule,
61
61
  WebhooksModule
62
- } from "./chunk-JXQI62A7.mjs";
62
+ } from "./chunk-ZLJPABB7.mjs";
63
63
  import {
64
64
  DEFAULT_CLOCK_TOLERANCE_SECONDS,
65
65
  DEFAULT_TOKEN_AUDIENCE,
@@ -1,4 +1,4 @@
1
- import { I as IQAuthLocaleBundle, b as IQAuthLocaleOverride, a as IQAuthLocaleKey } from './types-BdQ2lqfT.mjs';
1
+ import { I as IQAuthLocaleBundle, b as IQAuthLocaleOverride, a as IQAuthLocaleKey } from './types-DnU2LhXR.mjs';
2
2
 
3
3
  declare const enUS: IQAuthLocaleBundle;
4
4
 
package/dist/locales.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { I as IQAuthLocaleBundle, b as IQAuthLocaleOverride, a as IQAuthLocaleKey } from './types-BdQ2lqfT.js';
1
+ import { I as IQAuthLocaleBundle, b as IQAuthLocaleOverride, a as IQAuthLocaleKey } from './types-DnU2LhXR.js';
2
2
 
3
3
  declare const enUS: IQAuthLocaleBundle;
4
4
 
package/dist/locales.js CHANGED
@@ -74,6 +74,7 @@ var enUS = {
74
74
  "signIn.useDifferentAccount": "Use a different account",
75
75
  "signIn.selectTenant": "Choose an organization",
76
76
  "signIn.selectTenantSubtitle": "You belong to multiple organizations. Pick one to continue.",
77
+ "signIn.selectScope": "Choose scope",
77
78
  "signIn.dividerOr": "or",
78
79
  "signIn.preparingExperience": "Preparing your sign-in experience.",
79
80
  "signIn.applicationUnavailable": "Application unavailable",
@@ -162,6 +163,11 @@ var enUS = {
162
163
  "orgSwitcher.createNew": "Create organization",
163
164
  "orgSwitcher.manage": "Manage organization",
164
165
  "orgSwitcher.noOrgs": "You don't belong to any organizations yet.",
166
+ "orgSwitcher.mfaRequiredTitle": "Two-factor verification required",
167
+ "orgSwitcher.mfaRequiredBody": "This organization requires an extra verification step. Continue in the hosted sign-in to finish switching.",
168
+ "orgSwitcher.scopeSelectionRequiredTitle": "Choose what to access",
169
+ "orgSwitcher.scopeSelectionRequiredBody": "This organization needs you to pick which workspace to open. Continue in the hosted sign-in to choose.",
170
+ "orgSwitcher.continueInHostedSignIn": "Continue in hosted sign-in \u2192",
165
171
  "orgProfile.title": "Organization settings",
166
172
  "orgProfile.generalTab": "General",
167
173
  "orgProfile.membersTab": "Members",
@@ -254,6 +260,7 @@ var frFR = {
254
260
  "signIn.useDifferentAccount": "Utiliser un autre compte",
255
261
  "signIn.selectTenant": "Choisir une organisation",
256
262
  "signIn.selectTenantSubtitle": "Vous appartenez \xE0 plusieurs organisations. Choisissez-en une pour continuer.",
263
+ "signIn.selectScope": "Choisir une port\xE9e",
257
264
  "signIn.dividerOr": "ou",
258
265
  "signIn.preparingExperience": "Pr\xE9paration de votre exp\xE9rience de connexion.",
259
266
  "signIn.applicationUnavailable": "Application indisponible",
@@ -342,6 +349,11 @@ var frFR = {
342
349
  "orgSwitcher.createNew": "Cr\xE9er une organisation",
343
350
  "orgSwitcher.manage": "G\xE9rer l'organisation",
344
351
  "orgSwitcher.noOrgs": "Vous n'appartenez encore \xE0 aucune organisation.",
352
+ "orgSwitcher.mfaRequiredTitle": "V\xE9rification en deux \xE9tapes requise",
353
+ "orgSwitcher.mfaRequiredBody": "Cette organisation n\xE9cessite une \xE9tape de v\xE9rification suppl\xE9mentaire. Poursuivez sur la page de connexion h\xE9berg\xE9e pour terminer le changement.",
354
+ "orgSwitcher.scopeSelectionRequiredTitle": "Choisir l'espace \xE0 ouvrir",
355
+ "orgSwitcher.scopeSelectionRequiredBody": "Cette organisation n\xE9cessite que vous choisissiez un espace de travail. Poursuivez sur la page de connexion h\xE9berg\xE9e pour choisir.",
356
+ "orgSwitcher.continueInHostedSignIn": "Continuer sur la connexion h\xE9berg\xE9e \u2192",
345
357
  "orgProfile.title": "Param\xE8tres de l'organisation",
346
358
  "orgProfile.generalTab": "G\xE9n\xE9ral",
347
359
  "orgProfile.membersTab": "Membres",
@@ -434,6 +446,7 @@ var esES = {
434
446
  "signIn.useDifferentAccount": "Usar otra cuenta",
435
447
  "signIn.selectTenant": "Elige una organizaci\xF3n",
436
448
  "signIn.selectTenantSubtitle": "Perteneces a varias organizaciones. Selecciona una para continuar.",
449
+ "signIn.selectScope": "Elegir \xE1mbito",
437
450
  "signIn.dividerOr": "o",
438
451
  "signIn.preparingExperience": "Preparando tu experiencia de inicio de sesi\xF3n.",
439
452
  "signIn.applicationUnavailable": "Aplicaci\xF3n no disponible",
@@ -522,6 +535,11 @@ var esES = {
522
535
  "orgSwitcher.createNew": "Crear organizaci\xF3n",
523
536
  "orgSwitcher.manage": "Gestionar organizaci\xF3n",
524
537
  "orgSwitcher.noOrgs": "A\xFAn no perteneces a ninguna organizaci\xF3n.",
538
+ "orgSwitcher.mfaRequiredTitle": "Se requiere verificaci\xF3n en dos pasos",
539
+ "orgSwitcher.mfaRequiredBody": "Esta organizaci\xF3n requiere un paso de verificaci\xF3n adicional. Contin\xFAa en el inicio de sesi\xF3n alojado para completar el cambio.",
540
+ "orgSwitcher.scopeSelectionRequiredTitle": "Elige a qu\xE9 acceder",
541
+ "orgSwitcher.scopeSelectionRequiredBody": "Esta organizaci\xF3n requiere que elijas un espacio de trabajo. Contin\xFAa en el inicio de sesi\xF3n alojado para elegir.",
542
+ "orgSwitcher.continueInHostedSignIn": "Continuar en el inicio de sesi\xF3n alojado \u2192",
525
543
  "orgProfile.title": "Configuraci\xF3n de la organizaci\xF3n",
526
544
  "orgProfile.generalTab": "Generales",
527
545
  "orgProfile.membersTab": "Miembros",
@@ -614,6 +632,7 @@ var deDE = {
614
632
  "signIn.useDifferentAccount": "Anderes Konto verwenden",
615
633
  "signIn.selectTenant": "Organisation ausw\xE4hlen",
616
634
  "signIn.selectTenantSubtitle": "Sie geh\xF6ren zu mehreren Organisationen. Bitte w\xE4hlen Sie eine aus.",
635
+ "signIn.selectScope": "Bereich ausw\xE4hlen",
617
636
  "signIn.preparingExperience": "Anmeldung wird vorbereitet.",
618
637
  "signIn.applicationUnavailable": "Anwendung nicht verf\xFCgbar",
619
638
  "signIn.applicationNotFound": "Anwendung nicht gefunden",
@@ -702,6 +721,11 @@ var deDE = {
702
721
  "orgSwitcher.createNew": "Organisation erstellen",
703
722
  "orgSwitcher.manage": "Organisation verwalten",
704
723
  "orgSwitcher.noOrgs": "Sie geh\xF6ren noch keiner Organisation an.",
724
+ "orgSwitcher.mfaRequiredTitle": "Zwei-Faktor-Verifizierung erforderlich",
725
+ "orgSwitcher.mfaRequiredBody": "Diese Organisation erfordert einen zus\xE4tzlichen Verifizierungsschritt. Fahren Sie in der gehosteten Anmeldung fort, um den Wechsel abzuschlie\xDFen.",
726
+ "orgSwitcher.scopeSelectionRequiredTitle": "Zugriff ausw\xE4hlen",
727
+ "orgSwitcher.scopeSelectionRequiredBody": "F\xFCr diese Organisation m\xFCssen Sie einen Arbeitsbereich ausw\xE4hlen. Fahren Sie in der gehosteten Anmeldung fort, um auszuw\xE4hlen.",
728
+ "orgSwitcher.continueInHostedSignIn": "In der gehosteten Anmeldung fortfahren \u2192",
705
729
  "orgProfile.title": "Organisationseinstellungen",
706
730
  "orgProfile.generalTab": "Allgemein",
707
731
  "orgProfile.membersTab": "Mitglieder",
@@ -794,6 +818,7 @@ var jaJP = {
794
818
  "signIn.useDifferentAccount": "\u5225\u306E\u30A2\u30AB\u30A6\u30F3\u30C8\u3092\u4F7F\u7528",
795
819
  "signIn.selectTenant": "\u7D44\u7E54\u3092\u9078\u629E",
796
820
  "signIn.selectTenantSubtitle": "\u8907\u6570\u306E\u7D44\u7E54\u306B\u6240\u5C5E\u3057\u3066\u3044\u307E\u3059\u3002\u7D9A\u3051\u308B\u306B\u306F 1 \u3064\u3092\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
821
+ "signIn.selectScope": "\u30B9\u30B3\u30FC\u30D7\u3092\u9078\u629E",
797
822
  "signIn.preparingExperience": "\u30B5\u30A4\u30F3\u30A4\u30F3\u306E\u6E96\u5099\u3092\u3057\u3066\u3044\u307E\u3059\u3002",
798
823
  "signIn.applicationUnavailable": "\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u3092\u5229\u7528\u3067\u304D\u307E\u305B\u3093",
799
824
  "signIn.applicationNotFound": "\u30A2\u30D7\u30EA\u30B1\u30FC\u30B7\u30E7\u30F3\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093",
@@ -882,6 +907,11 @@ var jaJP = {
882
907
  "orgSwitcher.createNew": "\u7D44\u7E54\u3092\u4F5C\u6210",
883
908
  "orgSwitcher.manage": "\u7D44\u7E54\u3092\u7BA1\u7406",
884
909
  "orgSwitcher.noOrgs": "\u307E\u3060\u3069\u306E\u7D44\u7E54\u306B\u3082\u6240\u5C5E\u3057\u3066\u3044\u307E\u305B\u3093\u3002",
910
+ "orgSwitcher.mfaRequiredTitle": "\u4E8C\u6BB5\u968E\u8A8D\u8A3C\u304C\u5FC5\u8981\u3067\u3059",
911
+ "orgSwitcher.mfaRequiredBody": "\u3053\u306E\u7D44\u7E54\u3067\u306F\u8FFD\u52A0\u306E\u672C\u4EBA\u78BA\u8A8D\u304C\u5FC5\u8981\u3067\u3059\u3002\u30DB\u30B9\u30C8\u3055\u308C\u305F\u30B5\u30A4\u30F3\u30A4\u30F3\u3067\u7D9A\u3051\u3066\u5207\u308A\u66FF\u3048\u3092\u5B8C\u4E86\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
912
+ "orgSwitcher.scopeSelectionRequiredTitle": "\u30A2\u30AF\u30BB\u30B9\u5148\u3092\u9078\u629E",
913
+ "orgSwitcher.scopeSelectionRequiredBody": "\u3053\u306E\u7D44\u7E54\u3067\u306F\u30EF\u30FC\u30AF\u30B9\u30DA\u30FC\u30B9\u306E\u9078\u629E\u304C\u5FC5\u8981\u3067\u3059\u3002\u30DB\u30B9\u30C8\u3055\u308C\u305F\u30B5\u30A4\u30F3\u30A4\u30F3\u3067\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
914
+ "orgSwitcher.continueInHostedSignIn": "\u30DB\u30B9\u30C8\u3055\u308C\u305F\u30B5\u30A4\u30F3\u30A4\u30F3\u306B\u9032\u3080 \u2192",
885
915
  "orgProfile.title": "\u7D44\u7E54\u306E\u8A2D\u5B9A",
886
916
  "orgProfile.generalTab": "\u4E00\u822C",
887
917
  "orgProfile.membersTab": "\u30E1\u30F3\u30D0\u30FC",
@@ -974,6 +1004,7 @@ var ptBR = {
974
1004
  "signIn.useDifferentAccount": "Usar outra conta",
975
1005
  "signIn.selectTenant": "Escolher uma organiza\xE7\xE3o",
976
1006
  "signIn.selectTenantSubtitle": "Voc\xEA pertence a v\xE1rias organiza\xE7\xF5es. Escolha uma para continuar.",
1007
+ "signIn.selectScope": "Escolher escopo",
977
1008
  "signIn.preparingExperience": "Preparando sua experi\xEAncia de login.",
978
1009
  "signIn.applicationUnavailable": "Aplicativo indispon\xEDvel",
979
1010
  "signIn.applicationNotFound": "Aplicativo n\xE3o encontrado",
@@ -1061,6 +1092,11 @@ var ptBR = {
1061
1092
  "orgSwitcher.personal": "Conta pessoal",
1062
1093
  "orgSwitcher.createNew": "Criar organiza\xE7\xE3o",
1063
1094
  "orgSwitcher.manage": "Gerenciar organiza\xE7\xE3o",
1095
+ "orgSwitcher.mfaRequiredTitle": "Verifica\xE7\xE3o em duas etapas necess\xE1ria",
1096
+ "orgSwitcher.mfaRequiredBody": "Esta organiza\xE7\xE3o requer uma etapa de verifica\xE7\xE3o adicional. Continue no login hospedado para concluir a troca.",
1097
+ "orgSwitcher.scopeSelectionRequiredTitle": "Escolha o que acessar",
1098
+ "orgSwitcher.scopeSelectionRequiredBody": "Esta organiza\xE7\xE3o exige que voc\xEA escolha um espa\xE7o de trabalho. Continue no login hospedado para escolher.",
1099
+ "orgSwitcher.continueInHostedSignIn": "Continuar no login hospedado \u2192",
1064
1100
  "orgSwitcher.noOrgs": "Voc\xEA ainda n\xE3o pertence a nenhuma organiza\xE7\xE3o.",
1065
1101
  "orgProfile.title": "Configura\xE7\xF5es da organiza\xE7\xE3o",
1066
1102
  "orgProfile.generalTab": "Geral",
package/dist/locales.mjs CHANGED
@@ -11,7 +11,7 @@ import {
11
11
  ptBR,
12
12
  resolveBundle,
13
13
  t
14
- } from "./chunk-5T7GHBX6.mjs";
14
+ } from "./chunk-TLET552H.mjs";
15
15
  import "./chunk-Y6FXYEAI.mjs";
16
16
  export {
17
17
  builtInLocales,
package/dist/mobile.d.mts CHANGED
@@ -1,7 +1,7 @@
1
- import { I as IQAuthClient } from './client-BGFnBpfc.mjs';
2
- import { f as IQAuthTokenClientConfig } from './types-XOV9XPVi.mjs';
1
+ import { I as IQAuthClient } from './client-D8L-PaWr.mjs';
2
+ import { f as IQAuthTokenClientConfig } from './types-Bn8O-OEd.mjs';
3
3
  export { E as ErrorCodes, I as IQAuthError } from './errors-Jl1Jtm-6.mjs';
4
- import './tokens-CITeoG6P.mjs';
4
+ import './tokens-B06VtvUi.mjs';
5
5
 
6
6
  /**
7
7
  * Mobile client — wraps IQAuthClient with React Native / Expo–aware behavior.
package/dist/mobile.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { I as IQAuthClient } from './client-CDQ21LvW.js';
2
- import { f as IQAuthTokenClientConfig } from './types-XOV9XPVi.js';
1
+ import { I as IQAuthClient } from './client-DkPL0EPZ.js';
2
+ import { f as IQAuthTokenClientConfig } from './types-Bn8O-OEd.js';
3
3
  export { E as ErrorCodes, I as IQAuthError } from './errors-Jl1Jtm-6.js';
4
- import './tokens-Bqhmqq_R.js';
4
+ import './tokens-9F6ETrzk.js';
5
5
 
6
6
  /**
7
7
  * Mobile client — wraps IQAuthClient with React Native / Expo–aware behavior.
package/dist/mobile.js CHANGED
@@ -335,17 +335,27 @@ function parseLoginResponse(data, browserSessionMode) {
335
335
  tenants: data.tenants
336
336
  };
337
337
  }
338
+ if (data.type === "scope_selection" && data.scopeSelectionToken && data.scopes && data.tenantId) {
339
+ return {
340
+ status: "scope_selection",
341
+ scopeSelectionToken: data.scopeSelectionToken,
342
+ tenantId: data.tenantId,
343
+ scopes: data.scopes
344
+ };
345
+ }
338
346
  throw new Error("Unexpected login response shape");
339
347
  }
340
348
  var AuthModule = class {
341
349
  constructor(http) {
342
350
  this.http = http;
343
351
  }
344
- async login(email, password) {
352
+ async login(email, password, opts) {
353
+ const body = { email, password };
354
+ if (opts?.scopeHint) body.scopeHint = opts.scopeHint;
345
355
  const data = await this.http.request(
346
356
  "POST",
347
357
  "/api/v1/auth/login",
348
- { email, password },
358
+ body,
349
359
  { skipAutoRefresh: true }
350
360
  );
351
361
  return parseLoginResponse(data, this.http.isBrowserSession());
@@ -383,13 +393,29 @@ var AuthModule = class {
383
393
  method
384
394
  }, { skipAutoRefresh: true });
385
395
  }
386
- async selectTenant(tenantSelectionToken, tenantId) {
396
+ async selectTenant(tenantSelectionToken, tenantId, opts) {
397
+ const body = { tenantSelectionToken, tenantId };
398
+ if (opts?.scopeHint) body.scopeHint = opts.scopeHint;
387
399
  const data = await this.http.request(
388
400
  "POST",
389
401
  "/api/v1/auth/select-tenant",
402
+ body,
403
+ { skipAutoRefresh: true }
404
+ );
405
+ return parseLoginResponse(data, this.http.isBrowserSession());
406
+ }
407
+ /**
408
+ * Task #171 — redeem a scope-selection token + chosen membership for a
409
+ * real authenticated session. `membershipId` must be one of the scopes
410
+ * returned in the prior `scope_selection` envelope.
411
+ */
412
+ async selectScope(scopeSelectionToken, membershipId) {
413
+ const data = await this.http.request(
414
+ "POST",
415
+ "/api/v1/auth/select-scope",
390
416
  {
391
- tenantSelectionToken,
392
- tenantId
417
+ scopeSelectionToken,
418
+ membershipId
393
419
  },
394
420
  { skipAutoRefresh: true }
395
421
  );
package/dist/mobile.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  IQAuthClient
3
- } from "./chunk-JXQI62A7.mjs";
3
+ } from "./chunk-ZLJPABB7.mjs";
4
4
  import "./chunk-NUO2I65G.mjs";
5
5
  import {
6
6
  ErrorCodes,
package/dist/next.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { IQAuthHelperConfig } from './server/handlers.mjs';
2
- import { J as JwtClaims } from './types-XOV9XPVi.mjs';
3
- import './tokens-CITeoG6P.mjs';
2
+ import { J as JwtClaims } from './types-Bn8O-OEd.mjs';
3
+ import './tokens-B06VtvUi.mjs';
4
4
 
5
5
  /**
6
6
  * @iqauth/sdk/next — Next.js (App Router) adapter.
@@ -21,6 +21,14 @@ import './tokens-CITeoG6P.mjs';
21
21
  interface IQAuthNextOptions extends IQAuthHelperConfig {
22
22
  /** Path prefix where the helper routes live. */
23
23
  mountPath?: string;
24
+ /**
25
+ * Cookie name the browser SDK publishes the post-login destination into
26
+ * before redirect. The callback handler reads it, surfaces it as `returnTo`
27
+ * in the JSON response body after a successful exchange, and clears it on
28
+ * success. Mirrors the express inline-callback adapter. Defaults to
29
+ * `iqauth_return_to`.
30
+ */
31
+ returnToCookieName?: string;
24
32
  }
25
33
  declare function handler(options: IQAuthNextOptions): (req: Request) => Promise<Response>;
26
34
  /**
package/dist/next.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { IQAuthHelperConfig } from './server/handlers.js';
2
- import { J as JwtClaims } from './types-XOV9XPVi.js';
3
- import './tokens-Bqhmqq_R.js';
2
+ import { J as JwtClaims } from './types-Bn8O-OEd.js';
3
+ import './tokens-9F6ETrzk.js';
4
4
 
5
5
  /**
6
6
  * @iqauth/sdk/next — Next.js (App Router) adapter.
@@ -21,6 +21,14 @@ import './tokens-Bqhmqq_R.js';
21
21
  interface IQAuthNextOptions extends IQAuthHelperConfig {
22
22
  /** Path prefix where the helper routes live. */
23
23
  mountPath?: string;
24
+ /**
25
+ * Cookie name the browser SDK publishes the post-login destination into
26
+ * before redirect. The callback handler reads it, surfaces it as `returnTo`
27
+ * in the JSON response body after a successful exchange, and clears it on
28
+ * success. Mirrors the express inline-callback adapter. Defaults to
29
+ * `iqauth_return_to`.
30
+ */
31
+ returnToCookieName?: string;
24
32
  }
25
33
  declare function handler(options: IQAuthNextOptions): (req: Request) => Promise<Response>;
26
34
  /**