@finueva/auth 0.15.0 → 0.16.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.
package/README.md CHANGED
@@ -32,6 +32,22 @@ Do not reuse configuration from another application or environment.
32
32
  | Server or SSR handler | `@finueva/auth/server` | Incoming Auth cookie or bearer | Once per request |
33
33
  | CLI or native application | `@finueva/auth/device` | Device bearer session | Once per process |
34
34
 
35
+ ## Private Identity Resolution Candidate
36
+
37
+ For private server-to-server identity resolution, the next package candidate exports `createIdentityResolverAuth` from `@finueva/auth/server`. This helper is not in published `0.15.0`.
38
+
39
+ ```ts
40
+ import { createIdentityResolverAuth } from "@finueva/auth/server";
41
+
42
+ const identities = createIdentityResolverAuth({ authOrigin: "https://auth.example.test" });
43
+
44
+ async function resolvePrivateIdentity(identifier: string, clientAssertion: string) {
45
+ return identities.resolveIdentity({ identifier }, clientAssertion);
46
+ }
47
+ ```
48
+
49
+ The approved server signs a fresh assertion with its registered key and explicit `identities:resolve` capability. The method returns only `{ userId }` or null for one exact username or email. Keep the result private. It grants no product authority. See the [server guide](https://github.com/Ifkafin/auth/blob/main/apps/developer/docs/sdk/server.md#resolve-a-private-identity) for the assertion contract and limits.
50
+
35
51
  ## Browser Quick Start
36
52
 
37
53
  Create the client with the exact Auth and application origins:
package/dist/index.js CHANGED
@@ -4472,7 +4472,7 @@ function createAuthClient(options) {
4472
4472
  baseURL: authOrigin,
4473
4473
  basePath: "/api/v1/auth",
4474
4474
  fetchOptions: {
4475
- customFetchImpl: fetchImpl,
4475
+ customFetchImpl: fetchBetterAuth,
4476
4476
  credentials: "include",
4477
4477
  retry: 0,
4478
4478
  timeout: timeoutMs
@@ -4508,6 +4508,15 @@ function createAuthClient(options) {
4508
4508
  let signalRefreshPending = false;
4509
4509
  let signalRefreshScheduled = false;
4510
4510
  let signalRefreshRequest;
4511
+ let betterAuthTransportSequence = 0;
4512
+ async function fetchBetterAuth(input, init) {
4513
+ const request = new Request(input, init);
4514
+ const url = new URL(request.url);
4515
+ if (!authStateStarted || request.method !== "GET" || url.origin !== authOrigin || url.pathname !== "/api/v1/auth/get-session" || signOutRequest || activeAccountMutationGeneration !== void 0) return fetchImpl(input, init);
4516
+ betterAuthTransportSequence += 1;
4517
+ const response = await readAuthoritySession(authorityGeneration, request);
4518
+ return new Response(JSON.stringify(response.value), { headers: { "content-type": "application/json" } });
4519
+ }
4511
4520
  function advanceAuthorityGeneration() {
4512
4521
  recentAuthenticationStartAttempt = void 0;
4513
4522
  return ++authorityGeneration;
@@ -4569,12 +4578,13 @@ function createAuthClient(options) {
4569
4578
  authoritative: false
4570
4579
  });
4571
4580
  });
4572
- getSession().catch(() => void 0);
4581
+ if (typeof window === "undefined") getSession().catch(() => void 0);
4573
4582
  }
4574
4583
  function subscribeToSessionSignals() {
4575
4584
  if (sessionSignalSubscribed) return;
4576
4585
  sessionSignalSubscribed = true;
4577
4586
  let initialized = false;
4587
+ let transportSequence = betterAuthTransportSequence;
4578
4588
  betterAuth.useSession.subscribe((state) => {
4579
4589
  const refetching = state.isRefetching === true;
4580
4590
  if (!initialized) {
@@ -4583,6 +4593,7 @@ function createAuthClient(options) {
4583
4593
  return;
4584
4594
  }
4585
4595
  const settled = betterAuthRefetching && !refetching;
4596
+ if (refetching && !betterAuthRefetching) transportSequence = betterAuthTransportSequence;
4586
4597
  betterAuthRefetching = refetching;
4587
4598
  if (refetching && signOutRequest) suppressBetterAuthSettlement = true;
4588
4599
  if (!settled) return;
@@ -4590,6 +4601,7 @@ function createAuthClient(options) {
4590
4601
  suppressBetterAuthSettlement = false;
4591
4602
  return;
4592
4603
  }
4604
+ if (transportSequence !== betterAuthTransportSequence) return;
4593
4605
  queueSignalRefresh();
4594
4606
  });
4595
4607
  }
@@ -4626,24 +4638,35 @@ function createAuthClient(options) {
4626
4638
  sessionUserId = session?.user.id;
4627
4639
  return session;
4628
4640
  }
4629
- async function refreshAuthoritySession(generation) {
4641
+ async function readAuthoritySession(generation, request) {
4630
4642
  const readEpoch = ++sessionReadEpoch;
4631
4643
  try {
4632
- const response = await requestJsonResponse(joinURL(authOrigin, "api/v1/auth/get-session"), {
4644
+ const response = await requestJsonResponse(request?.url ?? joinURL(authOrigin, "api/v1/auth/get-session"), {
4633
4645
  fetch: fetchImpl,
4634
4646
  timeoutMs,
4635
- method: "GET"
4647
+ method: "GET",
4648
+ ...request ? {
4649
+ headers: request.headers,
4650
+ signal: request.signal
4651
+ } : {}
4636
4652
  });
4637
4653
  if (generation !== authorityGeneration || readEpoch !== sessionReadEpoch) throw new AuthError("state_conflict", 409);
4638
- return acceptAuthoritySession(normalizeSession(response.value), generation);
4654
+ return {
4655
+ ...response,
4656
+ session: acceptAuthoritySession(normalizeSession(response.value), generation)
4657
+ };
4639
4658
  } catch (error) {
4640
4659
  if (generation !== authorityGeneration || readEpoch !== sessionReadEpoch) throw new AuthError("state_conflict", 409);
4641
4660
  if (error instanceof AuthError && error.code === "state_conflict") throw error;
4642
- unavailable();
4661
+ if (error instanceof AuthError && error.status === 401) acceptAuthoritySession(null, generation);
4662
+ else unavailable();
4643
4663
  if (error instanceof AuthError) throw error;
4644
4664
  throw authorityUnavailable();
4645
4665
  }
4646
4666
  }
4667
+ async function refreshAuthoritySession(generation) {
4668
+ return (await readAuthoritySession(generation)).session;
4669
+ }
4647
4670
  function getSession() {
4648
4671
  if (signOutRequest || activeAccountMutationGeneration !== void 0) return Promise.reject(new AuthError("state_conflict", 409));
4649
4672
  return refreshAuthoritySession(authorityGeneration);
package/dist/server.d.ts CHANGED
@@ -1,4 +1,22 @@
1
1
  import { A as PersonalWorkspaceSelection, C as OrganizationPage, D as OrganizationTeamMemberPage, E as OrganizationTeamDisplay, G as SocialIdTokenProvider, K as UpdateOrganizationDisplayInput, O as OrganizationTeamPage, Q as AuthErrorStatus, R as ReceivedOrganizationInvitation, S as OrganizationMemberPage, T as OrganizationTeam, V as ResentOrganizationInvitation, W as SocialIdTokenInput, X as AuthError, Y as WorkspaceSelection, Z as AuthErrorCode, _ as OrganizationDisplay, b as OrganizationMember, c as AuthUser, d as CreatedOrganizationMemberInvitation, f as CreatedOrganizationTeam, g as InvitationAcceptance, k as OrganizationWorkspaceSelection, l as CanceledOrganizationInvitation, m as DriveCredentialInput, o as AuthSession, p as DriveCredential, q as UpdateOrganizationTeamDisplayInput, r as AuthOrganization, t as AcceptInvitationInput, u as CreateOrganizationMemberInvitationInput, v as OrganizationInvitation, w as OrganizationPageInput, x as OrganizationMemberAuthority, y as OrganizationInvitationPage, z as ReceivedOrganizationInvitationPage } from "./types-Dpy17xNC.js";
2
+ //#region src/identity-resolution.d.ts
3
+ interface IdentityResolverAuthOptions {
4
+ authOrigin: string;
5
+ fetch?: typeof globalThis.fetch;
6
+ timeoutMs?: number;
7
+ }
8
+ interface ResolveIdentityInput {
9
+ identifier: string;
10
+ }
11
+ interface ResolvedIdentity {
12
+ userId: string;
13
+ }
14
+ interface FinuevaIdentityResolverAuth {
15
+ resolveIdentity(input: ResolveIdentityInput, clientAssertion: string): Promise<ResolvedIdentity | null>;
16
+ }
17
+ /** Server-only identity resolution. Keep results private; they grant no product authority. */
18
+ declare function createIdentityResolverAuth(options: IdentityResolverAuthOptions): FinuevaIdentityResolverAuth;
19
+ //#endregion
2
20
  //#region src/drive-recipients.d.ts
3
21
  interface DriveRecipientAuthOptions {
4
22
  authOrigin: string;
@@ -80,4 +98,4 @@ interface FinuevaServerAuth {
80
98
  }
81
99
  declare function createServerAuth(options: ServerAuthOptions): FinuevaServerAuth;
82
100
  //#endregion
83
- export { type AcceptInvitationInput, AuthError, type AuthErrorCode, type AuthErrorStatus, type AuthOrganization, type AuthSession, type AuthUser, type CanceledOrganizationInvitation, type CreateOrganizationMemberInvitationInput, type CreatedOrganizationMemberInvitation, type CreatedOrganizationTeam, type DriveCredential, type DriveCredentialInput, type DriveProvisioningAuthOptions, type DriveProvisioningCredential, type DriveRecipient, type DriveRecipientAuthOptions, type FinuevaDriveProvisioningAuth, type FinuevaDriveRecipientAuth, FinuevaServerAuth, type InvitationAcceptance, type OrganizationDisplay, type OrganizationInvitation, type OrganizationInvitationPage, type OrganizationMember, type OrganizationMemberAuthority, type OrganizationMemberPage, type OrganizationPage, type OrganizationPageInput, type OrganizationTeam, type OrganizationTeamDisplay, type OrganizationTeamMemberPage, type OrganizationTeamPage, type OrganizationWorkspaceSelection, type PersonalWorkspaceSelection, type ReceivedOrganizationInvitation, type ReceivedOrganizationInvitationPage, type ResentOrganizationInvitation, type ResolveDriveRecipientInput, ServerAuthOptions, ServerInvitationAcceptance, ServerSignOutResult, ServerSocialIdTokenSession, type SocialIdTokenInput, type SocialIdTokenProvider, type UpdateOrganizationDisplayInput, type UpdateOrganizationTeamDisplayInput, type WorkspaceSelection, createDriveProvisioningAuth, createDriveRecipientAuth, createServerAuth };
101
+ export { type AcceptInvitationInput, AuthError, type AuthErrorCode, type AuthErrorStatus, type AuthOrganization, type AuthSession, type AuthUser, type CanceledOrganizationInvitation, type CreateOrganizationMemberInvitationInput, type CreatedOrganizationMemberInvitation, type CreatedOrganizationTeam, type DriveCredential, type DriveCredentialInput, type DriveProvisioningAuthOptions, type DriveProvisioningCredential, type DriveRecipient, type DriveRecipientAuthOptions, type FinuevaDriveProvisioningAuth, type FinuevaDriveRecipientAuth, type FinuevaIdentityResolverAuth, FinuevaServerAuth, type IdentityResolverAuthOptions, type InvitationAcceptance, type OrganizationDisplay, type OrganizationInvitation, type OrganizationInvitationPage, type OrganizationMember, type OrganizationMemberAuthority, type OrganizationMemberPage, type OrganizationPage, type OrganizationPageInput, type OrganizationTeam, type OrganizationTeamDisplay, type OrganizationTeamMemberPage, type OrganizationTeamPage, type OrganizationWorkspaceSelection, type PersonalWorkspaceSelection, type ReceivedOrganizationInvitation, type ReceivedOrganizationInvitationPage, type ResentOrganizationInvitation, type ResolveDriveRecipientInput, type ResolveIdentityInput, type ResolvedIdentity, ServerAuthOptions, ServerInvitationAcceptance, ServerSignOutResult, ServerSocialIdTokenSession, type SocialIdTokenInput, type SocialIdTokenProvider, type UpdateOrganizationDisplayInput, type UpdateOrganizationTeamDisplayInput, type WorkspaceSelection, createDriveProvisioningAuth, createDriveRecipientAuth, createIdentityResolverAuth, createServerAuth };
package/dist/server.js CHANGED
@@ -307,7 +307,7 @@ function normalizeIdInput(value) {
307
307
  return value;
308
308
  }
309
309
  function normalizeOrganization(value) {
310
- exact$1(value, [
310
+ exact$2(value, [
311
311
  "capabilities",
312
312
  "id",
313
313
  "lifecycle",
@@ -317,7 +317,7 @@ function normalizeOrganization(value) {
317
317
  "slug"
318
318
  ]);
319
319
  if (!isId(value.id) || !boundedString(value.name, 255) || !boundedString(value.slug, 255) || value.lifecycle !== "ordinary" && value.lifecycle !== "managed" || !role(value.role) || !isObject(value.capabilities)) throw authorityUnavailable();
320
- exact$1(value.capabilities, [
320
+ exact$2(value.capabilities, [
321
321
  "readInvitations",
322
322
  "readMembers",
323
323
  "readTeams"
@@ -378,7 +378,7 @@ function normalizeOrganizationTeamCreationInput(name, idempotencyKey) {
378
378
  };
379
379
  }
380
380
  function normalizeCreatedOrganizationTeam(value) {
381
- exact$1(value, ["id", "name"]);
381
+ exact$2(value, ["id", "name"]);
382
382
  if (!isId(value.id) || !boundedString(value.name, 255)) throw authorityUnavailable();
383
383
  return {
384
384
  id: value.id,
@@ -405,7 +405,7 @@ function normalizeOrganizationMemberInvitationCreationInput(value) {
405
405
  };
406
406
  }
407
407
  function normalizeCreatedOrganizationMemberInvitation(value) {
408
- exact$1(value, ["expiresAt", "id"]);
408
+ exact$2(value, ["expiresAt", "id"]);
409
409
  if (!isId(value.id)) throw authorityUnavailable();
410
410
  return {
411
411
  id: value.id,
@@ -422,12 +422,12 @@ function normalizeOrganizationInvitationAdministrationInput(idempotencyKey) {
422
422
  return { idempotencyKey };
423
423
  }
424
424
  function normalizeCanceledOrganizationInvitation(value) {
425
- exact$1(value, ["status"]);
425
+ exact$2(value, ["status"]);
426
426
  if (value.status !== "canceled") throw authorityUnavailable();
427
427
  return { status: "canceled" };
428
428
  }
429
429
  function normalizeOrganizations(value) {
430
- exact$1(value, ["nextCursor", "organizations"]);
430
+ exact$2(value, ["nextCursor", "organizations"]);
431
431
  if (!Array.isArray(value.organizations) || value.organizations.length > 100) throw authorityUnavailable();
432
432
  return {
433
433
  organizations: unique(value.organizations.map(normalizeOrganization), (item) => item.id),
@@ -435,11 +435,11 @@ function normalizeOrganizations(value) {
435
435
  };
436
436
  }
437
437
  function normalizeOrganizationMembers(value) {
438
- exact$1(value, ["members", "nextCursor"]);
438
+ exact$2(value, ["members", "nextCursor"]);
439
439
  if (!Array.isArray(value.members) || value.members.length > 100) throw authorityUnavailable();
440
440
  return {
441
441
  members: unique(value.members.map((member) => {
442
- exact$1(member, [
442
+ exact$2(member, [
443
443
  "email",
444
444
  "id",
445
445
  "image",
@@ -468,11 +468,11 @@ function normalizeOrganizationMembers(value) {
468
468
  };
469
469
  }
470
470
  function normalizeOrganizationTeams(value) {
471
- exact$1(value, ["nextCursor", "teams"]);
471
+ exact$2(value, ["nextCursor", "teams"]);
472
472
  if (!Array.isArray(value.teams) || value.teams.length > 100) throw authorityUnavailable();
473
473
  return {
474
474
  teams: unique(value.teams.map((team) => {
475
- exact$1(team, [
475
+ exact$2(team, [
476
476
  "category",
477
477
  "createdAt",
478
478
  "id",
@@ -490,7 +490,7 @@ function normalizeOrganizationTeams(value) {
490
490
  };
491
491
  }
492
492
  function normalizeOrganizationTeamMembers(value) {
493
- exact$1(value, ["memberIds", "nextCursor"]);
493
+ exact$2(value, ["memberIds", "nextCursor"]);
494
494
  if (!Array.isArray(value.memberIds) || value.memberIds.length > 100) throw authorityUnavailable();
495
495
  return {
496
496
  memberIds: unique(value.memberIds.map((item) => normalizeResponseId(item)), (item) => item),
@@ -498,7 +498,7 @@ function normalizeOrganizationTeamMembers(value) {
498
498
  };
499
499
  }
500
500
  function normalizeOrganizationInvitation(value) {
501
- exact$1(value, [
501
+ exact$2(value, [
502
502
  "createdAt",
503
503
  "email",
504
504
  "expiresAt",
@@ -509,7 +509,7 @@ function normalizeOrganizationInvitation(value) {
509
509
  "teamIds"
510
510
  ]);
511
511
  if (!isId(value.id) || typeof value.email !== "string" || value.email.length > 320 || !email.test(value.email) || !role(value.role) || value.status !== "pending" || !Array.isArray(value.teamIds) || value.teamIds.length > 100 || !isObject(value.inviter)) throw authorityUnavailable();
512
- exact$1(value.inviter, ["membershipId", "name"]);
512
+ exact$2(value.inviter, ["membershipId", "name"]);
513
513
  if (!isId(value.inviter.membershipId) || !boundedString(value.inviter.name, 255)) throw authorityUnavailable();
514
514
  return {
515
515
  id: value.id,
@@ -526,7 +526,7 @@ function normalizeOrganizationInvitation(value) {
526
526
  };
527
527
  }
528
528
  function normalizeOrganizationInvitations(value) {
529
- exact$1(value, ["invitations", "nextCursor"]);
529
+ exact$2(value, ["invitations", "nextCursor"]);
530
530
  if (!Array.isArray(value.invitations) || value.invitations.length > 100) throw authorityUnavailable();
531
531
  return {
532
532
  invitations: unique(value.invitations.map(normalizeOrganizationInvitation), (item) => item.id),
@@ -534,7 +534,7 @@ function normalizeOrganizationInvitations(value) {
534
534
  };
535
535
  }
536
536
  function normalizeReceivedInvitation(value) {
537
- exact$1(value, [
537
+ exact$2(value, [
538
538
  "expiresAt",
539
539
  "id",
540
540
  "inviterName",
@@ -543,7 +543,7 @@ function normalizeReceivedInvitation(value) {
543
543
  "teams"
544
544
  ]);
545
545
  if (!isId(value.id) || !boundedString(value.inviterName, 255) || !role(value.role) || !Array.isArray(value.teams) || value.teams.length > 100 || !isObject(value.organization)) throw authorityUnavailable();
546
- exact$1(value.organization, [
546
+ exact$2(value.organization, [
547
547
  "id",
548
548
  "logo",
549
549
  "name",
@@ -560,7 +560,7 @@ function normalizeReceivedInvitation(value) {
560
560
  },
561
561
  role: value.role,
562
562
  teams: unique(value.teams.map((team) => {
563
- exact$1(team, ["id", "name"]);
563
+ exact$2(team, ["id", "name"]);
564
564
  if (!isId(team.id) || !boundedString(team.name, 255)) throw authorityUnavailable();
565
565
  return {
566
566
  id: team.id,
@@ -572,7 +572,7 @@ function normalizeReceivedInvitation(value) {
572
572
  };
573
573
  }
574
574
  function normalizeReceivedInvitations(value) {
575
- exact$1(value, ["invitations", "nextCursor"]);
575
+ exact$2(value, ["invitations", "nextCursor"]);
576
576
  if (!Array.isArray(value.invitations) || value.invitations.length > 100) throw authorityUnavailable();
577
577
  return {
578
578
  invitations: unique(value.invitations.map(normalizeReceivedInvitation), (item) => item.id),
@@ -648,7 +648,7 @@ function fromOrganizationInvitationAdministrationError(value, status, retryAfter
648
648
  return fromOrganizationMutationAuthorityError(value, status, retryAfter);
649
649
  }
650
650
  function normalizeOrganizationMemberAuthority(value, organizationId, memberId) {
651
- exact$1(value, [
651
+ exact$2(value, [
652
652
  "organizationId",
653
653
  "actor",
654
654
  "target"
@@ -666,7 +666,7 @@ function normalizeOrganizationMemberAuthority(value, organizationId, memberId) {
666
666
  };
667
667
  }
668
668
  function normalizeOrganizationAuthorityMemberOutcome(value, includesStatus = false) {
669
- exact$1(value, [
669
+ exact$2(value, [
670
670
  "memberId",
671
671
  "manualRole",
672
672
  "role",
@@ -759,7 +759,7 @@ function toDate(value) {
759
759
  function isId(value) {
760
760
  return typeof value === "string" && id.test(value);
761
761
  }
762
- function exact$1(value, keys) {
762
+ function exact$2(value, keys) {
763
763
  if (!isObject(value) || Object.keys(value).sort().join(",") !== [...keys].sort().join(",")) throw authorityUnavailable();
764
764
  }
765
765
  function boundedString(value, maximum) {
@@ -803,6 +803,98 @@ function isObject(value) {
803
803
  return typeof value === "object" && value !== null && !Array.isArray(value);
804
804
  }
805
805
  //#endregion
806
+ //#region src/identity-resolution.ts
807
+ const emailPattern$1 = /^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9-]*\.)+[A-Za-z]{2,}$(?![\s\S])/;
808
+ function exact$1(value, keys) {
809
+ return value !== null && typeof value === "object" && !Array.isArray(value) && Object.keys(value).length === keys.length && keys.every((key) => Object.hasOwn(value, key));
810
+ }
811
+ function createIdentityResolverAuth(options) {
812
+ const url = joinURL(normalizeOrigin(options.authOrigin, "authOrigin"), "api/v1/auth/identities/resolve");
813
+ const fetchImpl = options.fetch ?? globalThis.fetch?.bind(globalThis);
814
+ if (typeof fetchImpl !== "function") throw new TypeError("fetch is required");
815
+ const timeoutMs = normalizeTimeout(options.timeoutMs);
816
+ let inFlight = false;
817
+ return { async resolveIdentity(input, clientAssertion) {
818
+ if (!exact$1(input, ["identifier"]) || typeof input.identifier !== "string" || input.identifier.length > 324) throw new AuthError("invalid_request", 400);
819
+ const identifier = input.identifier.trim().toLowerCase();
820
+ if (!(identifier.length <= 320 && emailPattern$1.test(identifier)) && !/^[a-z0-9_.]{3,30}$(?![\s\S])/.test(identifier)) throw new AuthError("invalid_request", 400);
821
+ if (typeof clientAssertion !== "string" || clientAssertion.length + 7 > 16 * 1024 || !/^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$(?![\s\S])/.test(clientAssertion)) throw new AuthError("invalid_request", 400);
822
+ if (inFlight) throw new AuthError("state_conflict", 409);
823
+ inFlight = true;
824
+ const controller = new AbortController();
825
+ let reader;
826
+ let timer;
827
+ let mappedError;
828
+ try {
829
+ const deadline = new Promise((_, reject) => {
830
+ timer = setTimeout(() => {
831
+ reject(authorityUnavailable());
832
+ controller.abort();
833
+ reader?.cancel().catch(() => {});
834
+ }, timeoutMs);
835
+ });
836
+ const exchange = async () => {
837
+ const response = await fetchImpl(url, {
838
+ method: "POST",
839
+ headers: {
840
+ authorization: `Bearer ${clientAssertion}`,
841
+ accept: "application/json",
842
+ "content-type": "application/json"
843
+ },
844
+ body: JSON.stringify({ identifier }),
845
+ credentials: "omit",
846
+ cache: "no-store",
847
+ redirect: "manual",
848
+ signal: controller.signal
849
+ });
850
+ if (controller.signal.aborted) {
851
+ response.body?.cancel().catch(() => {});
852
+ throw authorityUnavailable();
853
+ }
854
+ if (!response.body) throw authorityUnavailable();
855
+ reader = response.body.getReader();
856
+ const length = response.headers.get("content-length");
857
+ if (response.redirected || response.headers.get("content-type")?.split(";", 1)[0]?.trim().toLowerCase() !== "application/json" || length !== null && (!/^\d+$/.test(length) || Number(length) > 1024)) throw authorityUnavailable();
858
+ const bytes = new Uint8Array(1024);
859
+ let size = 0;
860
+ while (true) {
861
+ const chunk = await reader.read();
862
+ if (controller.signal.aborted) throw authorityUnavailable();
863
+ if (chunk.done) break;
864
+ if (size + chunk.value.byteLength > bytes.length) throw authorityUnavailable();
865
+ bytes.set(chunk.value, size);
866
+ size += chunk.value.byteLength;
867
+ }
868
+ const value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes.subarray(0, size)));
869
+ if (response.status !== 200) {
870
+ if (!exact$1(value, ["code", "message"])) throw authorityUnavailable();
871
+ if (response.status === 401 && value.code === "UNAUTHORIZED" && value.message === "The identity resolution assertion is invalid.") mappedError = new AuthError("authentication_required", 401);
872
+ if (response.status === 429 && value.code === "TOO_MANY_REQUESTS" && value.message === "Identity resolution requests are temporarily limited.") {
873
+ const retryAfter = response.headers.get("retry-after");
874
+ if (retryAfter === null || !/^(?:[1-9]|[1-5][0-9]|60)$/.test(retryAfter)) throw authorityUnavailable();
875
+ mappedError = new AuthError("auth_request_rate_limited", 429, Number(retryAfter));
876
+ }
877
+ throw mappedError ?? authorityUnavailable();
878
+ }
879
+ if (!exact$1(value, ["identity"])) throw authorityUnavailable();
880
+ if (value.identity === null) return null;
881
+ if (!exact$1(value.identity, ["userId"]) || typeof value.identity.userId !== "string" || !/^[A-Za-z0-9_-]{1,128}$(?![\s\S])/.test(value.identity.userId)) throw authorityUnavailable();
882
+ return { userId: value.identity.userId };
883
+ };
884
+ return await Promise.race([exchange().finally(() => {
885
+ inFlight = false;
886
+ }), deadline]);
887
+ } catch (error) {
888
+ if (mappedError && mappedError === error) throw mappedError;
889
+ throw authorityUnavailable();
890
+ } finally {
891
+ clearTimeout(timer);
892
+ controller.abort();
893
+ reader?.cancel().catch(() => {});
894
+ }
895
+ } };
896
+ }
897
+ //#endregion
806
898
  //#region src/drive-recipients.ts
807
899
  const encoder = new TextEncoder();
808
900
  const emailPattern = /^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9-]*\.)+[A-Za-z]{2,}$/;
@@ -1693,4 +1785,4 @@ function authAuthorizationHeader(value) {
1693
1785
  return value;
1694
1786
  }
1695
1787
  //#endregion
1696
- export { AuthError, createDriveProvisioningAuth, createDriveRecipientAuth, createServerAuth };
1788
+ export { AuthError, createDriveProvisioningAuth, createDriveRecipientAuth, createIdentityResolverAuth, createServerAuth };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@finueva/auth",
3
- "version": "0.15.0",
3
+ "version": "0.16.1",
4
4
  "description": "Public browser and server SDK for Finueva Auth",
5
5
  "type": "module",
6
6
  "sideEffects": false,