@finueva/auth 0.8.0 → 0.9.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.
package/dist/device.js CHANGED
@@ -444,6 +444,7 @@ const messages = {
444
444
  password_reset_invalid: "The password reset request is invalid.",
445
445
  authentication_required: "Authentication is required.",
446
446
  request_origin_denied: "The request origin is not allowed.",
447
+ invalid_request: "The request is invalid.",
447
448
  invalid_invitation: "The invitation is invalid.",
448
449
  invitation_unavailable: "The invitation is not available.",
449
450
  invitation_recipient_mismatch: "The invitation cannot be accepted by this account.",
@@ -563,6 +564,209 @@ function normalizeDriveCredential(value, now) {
563
564
  expiresAt: new Date(now + value.expiresIn * 1e3)
564
565
  };
565
566
  }
567
+ function normalizeOrganizationPageInput(value) {
568
+ if (value === void 0) return new URLSearchParams();
569
+ if (!isObject(value) || Object.keys(value).some((key) => key !== "limit" && key !== "cursor")) throw new AuthError("invalid_request", 400);
570
+ const input = value;
571
+ const query = new URLSearchParams();
572
+ if (input.limit !== void 0) {
573
+ if (!Number.isSafeInteger(input.limit) || input.limit < 1 || input.limit > 100) throw new AuthError("invalid_request", 400);
574
+ query.set("limit", String(input.limit));
575
+ }
576
+ if (input.cursor !== void 0) {
577
+ if (typeof input.cursor !== "string" || input.cursor.length < 1 || input.cursor.length > 820 || !/^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/.test(input.cursor)) throw new AuthError("invalid_request", 400);
578
+ query.set("cursor", input.cursor);
579
+ }
580
+ return query;
581
+ }
582
+ function normalizeIdInput(value) {
583
+ if (!isId(value)) throw new AuthError("invalid_request", 400);
584
+ return value;
585
+ }
586
+ function normalizeOrganization(value) {
587
+ exact(value, [
588
+ "capabilities",
589
+ "id",
590
+ "lifecycle",
591
+ "logo",
592
+ "name",
593
+ "role",
594
+ "slug"
595
+ ]);
596
+ 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();
597
+ exact(value.capabilities, [
598
+ "readInvitations",
599
+ "readMembers",
600
+ "readTeams"
601
+ ]);
602
+ if (value.capabilities.readMembers !== true || value.capabilities.readTeams !== true || typeof value.capabilities.readInvitations !== "boolean") throw authorityUnavailable();
603
+ return {
604
+ id: value.id,
605
+ name: value.name,
606
+ slug: value.slug,
607
+ logo: nullableHttpsUrl(value.logo),
608
+ lifecycle: value.lifecycle,
609
+ role: value.role,
610
+ capabilities: {
611
+ readMembers: true,
612
+ readTeams: true,
613
+ readInvitations: value.capabilities.readInvitations
614
+ }
615
+ };
616
+ }
617
+ function normalizeOrganizations(value) {
618
+ exact(value, ["nextCursor", "organizations"]);
619
+ if (!Array.isArray(value.organizations) || value.organizations.length > 100) throw authorityUnavailable();
620
+ return {
621
+ organizations: unique(value.organizations.map(normalizeOrganization), (item) => item.id),
622
+ nextCursor: nullableCursor(value.nextCursor)
623
+ };
624
+ }
625
+ function normalizeOrganizationMembers(value) {
626
+ exact(value, ["members", "nextCursor"]);
627
+ if (!Array.isArray(value.members) || value.members.length > 100) throw authorityUnavailable();
628
+ return {
629
+ members: unique(value.members.map((member) => {
630
+ exact(member, [
631
+ "email",
632
+ "id",
633
+ "image",
634
+ "joinedAt",
635
+ "name",
636
+ "owner",
637
+ "role",
638
+ "teamIds",
639
+ "userId"
640
+ ]);
641
+ if (!isId(member.id) || !isId(member.userId) || !boundedString(member.name, 255) || typeof member.email !== "string" || member.email.length > 320 || !email.test(member.email) || !role(member.role) || typeof member.owner !== "boolean" || !Array.isArray(member.teamIds) || member.teamIds.length > 100) throw authorityUnavailable();
642
+ const teamIds = unique(member.teamIds.map((teamId) => normalizeResponseId(teamId)), (item) => item);
643
+ return {
644
+ id: member.id,
645
+ userId: member.userId,
646
+ name: member.name,
647
+ email: member.email,
648
+ image: nullableHttpsUrl(member.image),
649
+ role: member.role,
650
+ owner: member.owner,
651
+ joinedAt: responseDate(member.joinedAt),
652
+ teamIds
653
+ };
654
+ }), (item) => item.id),
655
+ nextCursor: nullableCursor(value.nextCursor)
656
+ };
657
+ }
658
+ function normalizeOrganizationTeams(value) {
659
+ exact(value, ["nextCursor", "teams"]);
660
+ if (!Array.isArray(value.teams) || value.teams.length > 100) throw authorityUnavailable();
661
+ return {
662
+ teams: unique(value.teams.map((team) => {
663
+ exact(team, [
664
+ "category",
665
+ "createdAt",
666
+ "id",
667
+ "name"
668
+ ]);
669
+ if (!isId(team.id) || !boundedString(team.name, 255) || team.category !== "manual" && team.category !== "managed") throw authorityUnavailable();
670
+ return {
671
+ id: team.id,
672
+ name: team.name,
673
+ category: team.category,
674
+ createdAt: responseDate(team.createdAt)
675
+ };
676
+ }), (item) => item.id),
677
+ nextCursor: nullableCursor(value.nextCursor)
678
+ };
679
+ }
680
+ function normalizeOrganizationTeamMembers(value) {
681
+ exact(value, ["memberIds", "nextCursor"]);
682
+ if (!Array.isArray(value.memberIds) || value.memberIds.length > 100) throw authorityUnavailable();
683
+ return {
684
+ memberIds: unique(value.memberIds.map((item) => normalizeResponseId(item)), (item) => item),
685
+ nextCursor: nullableCursor(value.nextCursor)
686
+ };
687
+ }
688
+ function normalizeOrganizationInvitation(value) {
689
+ exact(value, [
690
+ "createdAt",
691
+ "email",
692
+ "expiresAt",
693
+ "id",
694
+ "inviter",
695
+ "role",
696
+ "status",
697
+ "teamIds"
698
+ ]);
699
+ 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();
700
+ exact(value.inviter, ["membershipId", "name"]);
701
+ if (!isId(value.inviter.membershipId) || !boundedString(value.inviter.name, 255)) throw authorityUnavailable();
702
+ return {
703
+ id: value.id,
704
+ email: value.email,
705
+ role: value.role,
706
+ teamIds: unique(value.teamIds.map(normalizeResponseId), (item) => item),
707
+ inviter: {
708
+ membershipId: value.inviter.membershipId,
709
+ name: value.inviter.name
710
+ },
711
+ createdAt: responseDate(value.createdAt),
712
+ expiresAt: responseDate(value.expiresAt),
713
+ status: "pending"
714
+ };
715
+ }
716
+ function normalizeOrganizationInvitations(value) {
717
+ exact(value, ["invitations", "nextCursor"]);
718
+ if (!Array.isArray(value.invitations) || value.invitations.length > 100) throw authorityUnavailable();
719
+ return {
720
+ invitations: unique(value.invitations.map(normalizeOrganizationInvitation), (item) => item.id),
721
+ nextCursor: nullableCursor(value.nextCursor)
722
+ };
723
+ }
724
+ function normalizeReceivedInvitation(value) {
725
+ exact(value, [
726
+ "expiresAt",
727
+ "id",
728
+ "inviterName",
729
+ "organization",
730
+ "role",
731
+ "teams"
732
+ ]);
733
+ if (!isId(value.id) || !boundedString(value.inviterName, 255) || !role(value.role) || !Array.isArray(value.teams) || value.teams.length > 100 || !isObject(value.organization)) throw authorityUnavailable();
734
+ exact(value.organization, [
735
+ "id",
736
+ "logo",
737
+ "name",
738
+ "slug"
739
+ ]);
740
+ if (!isId(value.organization.id) || !boundedString(value.organization.name, 255) || !boundedString(value.organization.slug, 255)) throw authorityUnavailable();
741
+ return {
742
+ id: value.id,
743
+ organization: {
744
+ id: value.organization.id,
745
+ name: value.organization.name,
746
+ slug: value.organization.slug,
747
+ logo: nullableHttpsUrl(value.organization.logo)
748
+ },
749
+ role: value.role,
750
+ teams: unique(value.teams.map((team) => {
751
+ exact(team, ["id", "name"]);
752
+ if (!isId(team.id) || !boundedString(team.name, 255)) throw authorityUnavailable();
753
+ return {
754
+ id: team.id,
755
+ name: team.name
756
+ };
757
+ }), (item) => item.id),
758
+ inviterName: value.inviterName,
759
+ expiresAt: responseDate(value.expiresAt)
760
+ };
761
+ }
762
+ function normalizeReceivedInvitations(value) {
763
+ exact(value, ["invitations", "nextCursor"]);
764
+ if (!Array.isArray(value.invitations) || value.invitations.length > 100) throw authorityUnavailable();
765
+ return {
766
+ invitations: unique(value.invitations.map(normalizeReceivedInvitation), (item) => item.id),
767
+ nextCursor: nullableCursor(value.nextCursor)
768
+ };
769
+ }
566
770
  function normalizeWorkspaceSelection(value) {
567
771
  if (!isObject(value)) throw new AuthError("invalid_workspace_selection", 422);
568
772
  const keys = Object.keys(value);
@@ -600,6 +804,12 @@ function fromSocialIdTokenAuthorityError(value, status, retryAfter) {
600
804
  function authorityUnavailable() {
601
805
  return new AuthError("identity_authority_unavailable", 503);
602
806
  }
807
+ function fromOrganizationReadAuthorityError(value, status) {
808
+ if (status === 400) return new AuthError("invalid_request", 400);
809
+ if (status === 401) return new AuthError("authentication_required", 401);
810
+ if (status === 403) return new AuthError("access_denied", 403);
811
+ return authorityUnavailable();
812
+ }
603
813
  function mapError(code, status) {
604
814
  if (code === "authentication_required") return {
605
815
  code,
@@ -674,6 +884,46 @@ function normalizeProfileImage(value) {
674
884
  function isId(value) {
675
885
  return typeof value === "string" && id.test(value);
676
886
  }
887
+ function exact(value, keys) {
888
+ if (!isObject(value) || Object.keys(value).sort().join(",") !== [...keys].sort().join(",")) throw authorityUnavailable();
889
+ }
890
+ function boundedString(value, maximum) {
891
+ return typeof value === "string" && value.length > 0 && new TextEncoder().encode(value).byteLength <= maximum;
892
+ }
893
+ function role(value) {
894
+ return value === "owner" || value === "admin" || value === "member";
895
+ }
896
+ function nullableHttpsUrl(value) {
897
+ if (value === null) return null;
898
+ if (typeof value !== "string" || value.length > 2048) throw authorityUnavailable();
899
+ let url;
900
+ try {
901
+ url = new URL(value);
902
+ } catch {
903
+ throw authorityUnavailable();
904
+ }
905
+ if (url.protocol !== "https:" || url.username || url.password || url.toString() !== value) throw authorityUnavailable();
906
+ return value;
907
+ }
908
+ function responseDate(value) {
909
+ if (typeof value !== "string") throw authorityUnavailable();
910
+ const date = new Date(value);
911
+ if (!Number.isFinite(date.getTime()) || date.toISOString() !== value) throw authorityUnavailable();
912
+ return date;
913
+ }
914
+ function nullableCursor(value) {
915
+ if (value === null) return null;
916
+ if (typeof value !== "string" || value.length < 1 || value.length > 820 || !/^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/.test(value)) throw authorityUnavailable();
917
+ return value;
918
+ }
919
+ function normalizeResponseId(value) {
920
+ if (!isId(value)) throw authorityUnavailable();
921
+ return value;
922
+ }
923
+ function unique(items, key) {
924
+ if (new Set(items.map(key)).size !== items.length) throw authorityUnavailable();
925
+ return items;
926
+ }
677
927
  function isObject(value) {
678
928
  return typeof value === "object" && value !== null && !Array.isArray(value);
679
929
  }
@@ -700,18 +950,18 @@ async function requestJsonResponse(url, options) {
700
950
  }
701
951
  const contentType = response.headers.get("content-type")?.split(";", 1)[0]?.trim().toLowerCase();
702
952
  if (response.status >= 300 || response.status < 200 || contentType !== "application/json") {
703
- const body = contentType === "application/json" ? await readJson(response) : void 0;
953
+ const body = contentType === "application/json" ? await readJson(response, options.maxResponseBytes) : void 0;
704
954
  await response.body?.cancel();
705
955
  throw (options.mapError ?? fromAuthorityError)(body, response.status, response.headers.get("retry-after"));
706
956
  }
707
957
  return {
708
- value: await readJson(response),
958
+ value: await readJson(response, options.maxResponseBytes),
709
959
  headers: response.headers
710
960
  };
711
961
  }
712
- async function readJson(response) {
962
+ async function readJson(response, maximum = MAX_RESPONSE_BYTES) {
713
963
  const length = response.headers.get("content-length");
714
- if (length && (!/^\d+$/.test(length) || Number(length) > MAX_RESPONSE_BYTES)) {
964
+ if (length && (!/^\d+$/.test(length) || Number(length) > maximum)) {
715
965
  await response.body?.cancel();
716
966
  throw authorityUnavailable();
717
967
  }
@@ -724,7 +974,7 @@ async function readJson(response) {
724
974
  const { done, value } = await reader.read();
725
975
  if (done) break;
726
976
  size += value.byteLength;
727
- if (size > MAX_RESPONSE_BYTES) throw authorityUnavailable();
977
+ if (size > maximum) throw authorityUnavailable();
728
978
  chunks.push(value);
729
979
  }
730
980
  } catch (error) {
@@ -1092,6 +1342,20 @@ function createDeviceAuth(options) {
1092
1342
  }
1093
1343
  return session;
1094
1344
  }
1345
+ async function organizationRead(path, input, normalize) {
1346
+ const query = normalizeOrganizationPageInput(input);
1347
+ const current = await readToken();
1348
+ if (!current) throw new AuthError("authentication_required", 401);
1349
+ return normalize((await requestJsonResponse(`${joinURL(authOrigin, "api/v1/auth/organizations", path)}${query.size ? `?${query}` : ""}`, {
1350
+ fetch: requestFetch,
1351
+ timeoutMs,
1352
+ method: "GET",
1353
+ credentials: "omit",
1354
+ headers: { authorization: `Bearer ${current.accessToken}` },
1355
+ mapError: fromOrganizationReadAuthorityError,
1356
+ maxResponseBytes: 256 * 1024
1357
+ })).value);
1358
+ }
1095
1359
  async function removePersisted() {
1096
1360
  if (!options.persistence) return;
1097
1361
  try {
@@ -1117,6 +1381,57 @@ function createDeviceAuth(options) {
1117
1381
  },
1118
1382
  getToken: () => serializeAuthority(async () => copyTokenOrNull(await readToken())),
1119
1383
  getSession: () => serializeAuthority(getSession, true),
1384
+ listOrganizations: (input) => serializeAuthority(() => organizationRead("", input, normalizeOrganizations)),
1385
+ getOrganization(organizationId) {
1386
+ try {
1387
+ return serializeAuthority(() => organizationRead(normalizeIdInput(organizationId), void 0, normalizeOrganization));
1388
+ } catch (error) {
1389
+ return Promise.reject(error);
1390
+ }
1391
+ },
1392
+ listOrganizationMembers(organizationId, input) {
1393
+ try {
1394
+ return serializeAuthority(() => organizationRead(`${normalizeIdInput(organizationId)}/members`, input, normalizeOrganizationMembers));
1395
+ } catch (error) {
1396
+ return Promise.reject(error);
1397
+ }
1398
+ },
1399
+ listOrganizationTeams(organizationId, input) {
1400
+ try {
1401
+ return serializeAuthority(() => organizationRead(`${normalizeIdInput(organizationId)}/teams`, input, normalizeOrganizationTeams));
1402
+ } catch (error) {
1403
+ return Promise.reject(error);
1404
+ }
1405
+ },
1406
+ listOrganizationTeamMembers(organizationId, teamId, input) {
1407
+ try {
1408
+ return serializeAuthority(() => organizationRead(`${normalizeIdInput(organizationId)}/teams/${normalizeIdInput(teamId)}/members`, input, normalizeOrganizationTeamMembers));
1409
+ } catch (error) {
1410
+ return Promise.reject(error);
1411
+ }
1412
+ },
1413
+ listOrganizationInvitations(organizationId, input) {
1414
+ try {
1415
+ return serializeAuthority(() => organizationRead(`${normalizeIdInput(organizationId)}/invitations`, input, normalizeOrganizationInvitations));
1416
+ } catch (error) {
1417
+ return Promise.reject(error);
1418
+ }
1419
+ },
1420
+ getOrganizationInvitation(organizationId, invitationId) {
1421
+ try {
1422
+ return serializeAuthority(() => organizationRead(`${normalizeIdInput(organizationId)}/invitations/${normalizeIdInput(invitationId)}`, void 0, normalizeOrganizationInvitation));
1423
+ } catch (error) {
1424
+ return Promise.reject(error);
1425
+ }
1426
+ },
1427
+ listReceivedInvitations: (input) => serializeAuthority(() => organizationRead("received-invitations", input, normalizeReceivedInvitations)),
1428
+ getReceivedInvitation(invitationId) {
1429
+ try {
1430
+ return serializeAuthority(() => organizationRead(`received-invitations/${normalizeIdInput(invitationId)}`, void 0, normalizeReceivedInvitation));
1431
+ } catch (error) {
1432
+ return Promise.reject(error);
1433
+ }
1434
+ },
1120
1435
  signUpWithEmail(email, password, name) {
1121
1436
  let body;
1122
1437
  try {
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { C as AuthErrorStatus, S as AuthErrorCode, _ as SocialIdTokenInput, a as AuthPersistenceDriver, b as WorkspaceSelection, c as AuthUser, d as FinuevaAuthClient, f as InvitationAcceptance, g as SignInInput, h as SendVerificationEmailInput, i as AuthPersistence, l as DriveCredential, m as PersonalWorkspaceSelection, n as AuthClientOptions, o as AuthSession, p as OrganizationWorkspaceSelection, r as AuthOrganization, s as AuthState, t as AcceptInvitationInput, u as DriveCredentialInput, v as SocialIdTokenProvider, x as AuthError, y as UpdateProfileInput } from "./types-DDopQNZR.js";
1
+ import { A as UpdateProfileInput, C as PersonalWorkspaceSelection, D as SignInInput, E as SendVerificationEmailInput, M as AuthError, N as AuthErrorCode, O as SocialIdTokenInput, P as AuthErrorStatus, S as OrganizationWorkspaceSelection, T as ReceivedOrganizationInvitationPage, _ as OrganizationPage, a as AuthPersistenceDriver, b as OrganizationTeamMemberPage, c as AuthUser, d as FinuevaAuthClient, f as InvitationAcceptance, g as OrganizationMemberPage, h as OrganizationMember, i as AuthPersistence, j as WorkspaceSelection, k as SocialIdTokenProvider, l as DriveCredential, m as OrganizationInvitationPage, n as AuthClientOptions, o as AuthSession, p as OrganizationInvitation, r as AuthOrganization, s as AuthState, t as AcceptInvitationInput, u as DriveCredentialInput, v as OrganizationPageInput, w as ReceivedOrganizationInvitation, x as OrganizationTeamPage, y as OrganizationTeam } from "./types-B67VIvTQ.js";
2
2
  //#region src/client.d.ts
3
3
  declare function createAuthClient(options: AuthClientOptions): FinuevaAuthClient;
4
4
  //#endregion
5
- export { type AcceptInvitationInput, type AuthClientOptions, AuthError, type AuthErrorCode, type AuthErrorStatus, type AuthOrganization, type AuthPersistence, type AuthPersistenceDriver, type AuthSession, type AuthState, type AuthUser, type DriveCredential, type DriveCredentialInput, type FinuevaAuthClient, type InvitationAcceptance, type OrganizationWorkspaceSelection, type PersonalWorkspaceSelection, type SendVerificationEmailInput, type SignInInput, type SocialIdTokenInput, type SocialIdTokenProvider, type UpdateProfileInput, type WorkspaceSelection, createAuthClient };
5
+ export { type AcceptInvitationInput, type AuthClientOptions, AuthError, type AuthErrorCode, type AuthErrorStatus, type AuthOrganization, type AuthPersistence, type AuthPersistenceDriver, type AuthSession, type AuthState, type AuthUser, type DriveCredential, type DriveCredentialInput, type FinuevaAuthClient, type InvitationAcceptance, type OrganizationInvitation, type OrganizationInvitationPage, type OrganizationMember, type OrganizationMemberPage, type OrganizationPage, type OrganizationPageInput, type OrganizationTeam, type OrganizationTeamMemberPage, type OrganizationTeamPage, type OrganizationWorkspaceSelection, type PersonalWorkspaceSelection, type ReceivedOrganizationInvitation, type ReceivedOrganizationInvitationPage, type SendVerificationEmailInput, type SignInInput, type SocialIdTokenInput, type SocialIdTokenProvider, type UpdateProfileInput, type WorkspaceSelection, createAuthClient };