@finueva/auth 0.16.1 → 0.18.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/README.md CHANGED
@@ -94,28 +94,31 @@ The browser owns the session cookie. The SDK does not expose or persist it.
94
94
 
95
95
  ## Common Tasks
96
96
 
97
- | Task | Browser | Server | Device |
98
- | ----------------------------- | ------------------------------------------- | ----------------------------------------- | -------------------------------------- |
99
- | Create an account | `signUp()` | Not available | `signUpWithEmail()` |
100
- | Read the current session | `getSession()` | `getSession()` | `getSession()` |
101
- | Observe session state | `onAuthStateChange()` | Not available | Not available |
102
- | Update a profile | `updateProfile()` | Not available | `updateProfile()` |
103
- | Change a password | `changePassword()` | Not available | `changePassword()` |
104
- | Reset a password | `requestPasswordReset()`, `resetPassword()` | Not available | Not available |
105
- | Verify an email | `sendVerificationEmail()` | Not available | Not available |
106
- | List organizations | `listOrganizations()` | `listOrganizations()` | `listOrganizations()` |
107
- | Read member authority | `getOrganizationMemberAuthority()` | `getOrganizationMemberAuthority()` | `getOrganizationMemberAuthority()` |
108
- | Create an ordinary team | `createOrganizationTeam()` | `createOrganizationTeam()` | `createOrganizationTeam()` |
109
- | Invite an ordinary member | `createOrganizationMemberInvitation()` | `createOrganizationMemberInvitation()` | `createOrganizationMemberInvitation()` |
110
- | Resend a member invitation | `resendOrganizationInvitation()` | `resendOrganizationInvitation()` (cookie) | Not available |
111
- | Cancel a member invitation | `cancelOrganizationInvitation()` | `cancelOrganizationInvitation()` (cookie) | Not available |
112
- | Accept an invitation | `acceptInvitation()` | `acceptInvitation()` | Not available |
113
- | Enroll a passkey | `addPasskey()` | Not available | Not available |
114
- | Perform recent authentication | Platform and organization action methods | Not available | Not available |
115
- | Change manual roles or owners | Organization authority methods | Not available | Not available |
116
- | Read current platform roles | `getPlatformStatus()` | Not available | Not available |
117
- | Request Drive access | `getDriveCredential()` | `getDriveCredential()` | `getDriveCredential()` |
118
- | End the current session | `signOut()` | `signOut()` | `revokeCurrentDevice()` |
97
+ | Task | Browser | Server | Device |
98
+ | ------------------------------- | ------------------------------------------- | ----------------------------------------- | -------------------------------------- |
99
+ | Create an account | `signUp()` | Not available | `signUpWithEmail()` |
100
+ | Read the current session | `getSession()` | `getSession()` | `getSession()` |
101
+ | Observe session state | `onAuthStateChange()` | Not available | Not available |
102
+ | Update a profile | `updateProfile()` | Not available | `updateProfile()` |
103
+ | Change a password | `changePassword()` | Not available | `changePassword()` |
104
+ | Reset a password | `requestPasswordReset()`, `resetPassword()` | Not available | Not available |
105
+ | Verify an email | `sendVerificationEmail()` | Not available | Not available |
106
+ | List organizations | `listOrganizations()` | `listOrganizations()` | `listOrganizations()` |
107
+ | Read member authority | `getOrganizationMemberAuthority()` | `getOrganizationMemberAuthority()` | `getOrganizationMemberAuthority()` |
108
+ | Create an ordinary organization | `createOrganization()` | `createOrganization()` | `createOrganization()` |
109
+ | Create an ordinary team | `createOrganizationTeam()` | `createOrganizationTeam()` | `createOrganizationTeam()` |
110
+ | Invite an ordinary member | `createOrganizationMemberInvitation()` | `createOrganizationMemberInvitation()` | `createOrganizationMemberInvitation()` |
111
+ | Resend a member invitation | `resendOrganizationInvitation()` | `resendOrganizationInvitation()` (cookie) | Not available |
112
+ | Cancel a member invitation | `cancelOrganizationInvitation()` | `cancelOrganizationInvitation()` (cookie) | Not available |
113
+ | Remove an ordinary member | `removeOrganizationMember()` | `removeOrganizationMember()` (cookie) | Not available |
114
+ | Leave an organization | `leaveOrganization()` | `leaveOrganization()` (cookie) | Not available |
115
+ | Accept an invitation | `acceptInvitation()` | `acceptInvitation()` | Not available |
116
+ | Enroll a passkey | `addPasskey()` | Not available | Not available |
117
+ | Perform recent authentication | Platform and organization action methods | Not available | Not available |
118
+ | Change manual roles or owners | Organization authority methods | Not available | Not available |
119
+ | Read current platform roles | `getPlatformStatus()` | Not available | Not available |
120
+ | Request Drive access | `getDriveCredential()` | `getDriveCredential()` | `getDriveCredential()` |
121
+ | End the current session | `signOut()` | `signOut()` | `revokeCurrentDevice()` |
119
122
 
120
123
  ## Create An Organization Invitation
121
124
 
@@ -142,6 +145,17 @@ const canceled = await auth.cancelOrganizationInvitation(organizationId, invitat
142
145
 
143
146
  Resend success confirms the committed expiry. It does not confirm email delivery. Repeat an action with the same key only after a lost response. A bearer-backed server client rejects both methods before network I/O.
144
147
 
148
+ ## Remove An Organization Member
149
+
150
+ Manual owners and manual administrators remove one other member. Every current member can leave. Both operations use browser-cookie or server-cookie authority:
151
+
152
+ ```ts
153
+ const removed = await auth.removeOrganizationMember(organizationId, memberId, crypto.randomUUID());
154
+ const left = await auth.leaveOrganization(organizationId, crypto.randomUUID());
155
+ ```
156
+
157
+ An owner target is refused. Transfer ownership first. Repeat a removal with the same key only after a lost response; a membership that is already gone under that key replays the same result. A bearer-backed server client rejects both methods before network I/O.
158
+
145
159
  ## Perform Recent Authentication
146
160
 
147
161
  The experimental passkey methods run only when the browser page uses the exact Auth Origin. To enroll a passkey, first create a new password-authenticated session on that origin:
package/dist/device.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { C as OrganizationPage, D as OrganizationTeamMemberPage, E as OrganizationTeamDisplay, G as SocialIdTokenProvider, J as UpdateProfileInput, K as UpdateOrganizationDisplayInput, O as OrganizationTeamPage, Q as AuthErrorStatus, R as ReceivedOrganizationInvitation, S as OrganizationMemberPage, T as OrganizationTeam, W as SocialIdTokenInput, X as AuthError, Z as AuthErrorCode, _ as OrganizationDisplay, b as OrganizationMember, d as CreatedOrganizationMemberInvitation, f as CreatedOrganizationTeam, m as DriveCredentialInput, o as AuthSession, p as DriveCredential, q as UpdateOrganizationTeamDisplayInput, r as AuthOrganization, u as CreateOrganizationMemberInvitationInput, v as OrganizationInvitation, w as OrganizationPageInput, x as OrganizationMemberAuthority, y as OrganizationInvitationPage, z as ReceivedOrganizationInvitationPage } from "./types-Dpy17xNC.js";
1
+ import { $ as AuthError, A as OrganizationTeamPage, B as ReceivedOrganizationInvitation, C as OrganizationMemberAuthority, D as OrganizationTeam, E as OrganizationPageInput, J as SocialIdTokenProvider, O as OrganizationTeamDisplay, S as OrganizationMember, T as OrganizationPage, V as ReceivedOrganizationInvitationPage, X as UpdateOrganizationTeamDisplayInput, Y as UpdateOrganizationDisplayInput, Z as UpdateProfileInput, b as OrganizationInvitation, d as CreatedOrganization, et as AuthErrorCode, f as CreatedOrganizationMemberInvitation, h as DriveCredentialInput, k as OrganizationTeamMemberPage, m as DriveCredential, nt as UserApiCredential, o as AuthSession, p as CreatedOrganizationTeam, q as SocialIdTokenInput, r as AuthOrganization, rt as UserApiCredentialInput, tt as AuthErrorStatus, u as CreateOrganizationMemberInvitationInput, w as OrganizationMemberPage, x as OrganizationInvitationPage, y as OrganizationDisplay } from "./types-CrdIcEJ3.js";
2
2
  //#region src/device.d.ts
3
3
  type DeviceAuthorizationErrorCode = "device_access_denied" | "device_authorization_cancelled" | "device_code_expired" | "invalid_device_authorization" | "identity_authority_unavailable";
4
4
  declare class DeviceAuthorizationError extends Error {
@@ -46,6 +46,7 @@ interface DeviceAuthClient {
46
46
  listOrganizations(input?: OrganizationPageInput): Promise<OrganizationPage>;
47
47
  getOrganization(organizationId: string): Promise<AuthOrganization>;
48
48
  getOrganizationMemberAuthority(organizationId: string, memberId: string): Promise<OrganizationMemberAuthority>;
49
+ createOrganization(name: string, slug: string, idempotencyKey: string): Promise<CreatedOrganization>;
49
50
  createOrganizationTeam(organizationId: string, name: string, idempotencyKey: string): Promise<CreatedOrganizationTeam>;
50
51
  createOrganizationMemberInvitation(organizationId: string, input: CreateOrganizationMemberInvitationInput): Promise<CreatedOrganizationMemberInvitation>;
51
52
  updateOrganizationDisplay(organizationId: string, input: UpdateOrganizationDisplayInput): Promise<OrganizationDisplay>;
@@ -64,9 +65,10 @@ interface DeviceAuthClient {
64
65
  updateProfile(input: UpdateProfileInput): Promise<AuthSession>;
65
66
  changePassword(currentPassword: string, newPassword: string, revokeOtherSessions?: boolean): Promise<AuthSession>;
66
67
  getDriveCredential(input: DriveCredentialInput): Promise<DriveCredential>;
68
+ getUserApiCredential(input: UserApiCredentialInput): Promise<UserApiCredential>;
67
69
  revokeCurrentDevice(): Promise<void>;
68
70
  revokeAllSessions(): Promise<void>;
69
71
  }
70
72
  declare function createDeviceAuth(options: DeviceAuthOptions): DeviceAuthClient;
71
73
  //#endregion
72
- export { AuthError, type AuthErrorCode, type AuthErrorStatus, type AuthOrganization, type CreateOrganizationMemberInvitationInput, type CreatedOrganizationMemberInvitation, type CreatedOrganizationTeam, DeviceAuthClient, DeviceAuthOptions, DeviceAuthorization, DeviceAuthorizationError, DeviceAuthorizationErrorCode, DeviceToken, DeviceTokenPersistence, type OrganizationDisplay, type OrganizationInvitation, type OrganizationInvitationPage, type OrganizationMember, type OrganizationMemberAuthority, type OrganizationMemberPage, type OrganizationPage, type OrganizationPageInput, type OrganizationTeam, type OrganizationTeamDisplay, type OrganizationTeamMemberPage, type OrganizationTeamPage, PollDeviceOptions, type ReceivedOrganizationInvitation, type ReceivedOrganizationInvitationPage, type SocialIdTokenInput, type SocialIdTokenProvider, type UpdateOrganizationDisplayInput, type UpdateOrganizationTeamDisplayInput, createDeviceAuth };
74
+ export { AuthError, type AuthErrorCode, type AuthErrorStatus, type AuthOrganization, type CreateOrganizationMemberInvitationInput, type CreatedOrganization, type CreatedOrganizationMemberInvitation, type CreatedOrganizationTeam, DeviceAuthClient, DeviceAuthOptions, DeviceAuthorization, DeviceAuthorizationError, DeviceAuthorizationErrorCode, DeviceToken, DeviceTokenPersistence, type OrganizationDisplay, type OrganizationInvitation, type OrganizationInvitationPage, type OrganizationMember, type OrganizationMemberAuthority, type OrganizationMemberPage, type OrganizationPage, type OrganizationPageInput, type OrganizationTeam, type OrganizationTeamDisplay, type OrganizationTeamMemberPage, type OrganizationTeamPage, PollDeviceOptions, type ReceivedOrganizationInvitation, type ReceivedOrganizationInvitationPage, type SocialIdTokenInput, type SocialIdTokenProvider, type UpdateOrganizationDisplayInput, type UpdateOrganizationTeamDisplayInput, type UserApiCredential, type UserApiCredentialInput, createDeviceAuth };
package/dist/device.js CHANGED
@@ -173,6 +173,110 @@ function stringifyParsedURL(parsed) {
173
173
  return (parsed.protocol || parsed[protocolRelative] ? (parsed.protocol || "") + "//" : "") + auth + host + pathname + search + hash;
174
174
  }
175
175
  //#endregion
176
+ //#region src/errors.ts
177
+ const messages = {
178
+ invalid_credentials: "The credentials are invalid.",
179
+ invalid_auth_input: "The authentication input is invalid.",
180
+ account_conflict: "The account is not available.",
181
+ password_reset_invalid: "The password reset request is invalid.",
182
+ authentication_required: "Authentication is required.",
183
+ request_origin_denied: "The request origin is not allowed.",
184
+ invalid_request: "The request is invalid.",
185
+ invalid_invitation: "The invitation is invalid.",
186
+ invitation_unavailable: "The invitation is not available.",
187
+ recipient_unavailable: "The recipient is unavailable.",
188
+ invitation_recipient_mismatch: "The invitation cannot be accepted by this account.",
189
+ invitation_recovery_required: "Invitation administration requires separate recovery.",
190
+ membership_removal_recovery_required: "Membership removal requires separate recovery.",
191
+ email_verification_required: "Email verification is required.",
192
+ invalid_workspace_selection: "The workspace selection is invalid.",
193
+ organization_context_too_large: "The organization context is too large.",
194
+ credential_rate_limited: "Credential requests are temporarily limited.",
195
+ auth_request_rate_limited: "Authentication requests are temporarily limited.",
196
+ state_conflict: "The authentication state conflicts with the request.",
197
+ access_denied: "Access is denied.",
198
+ platform_target_unavailable: "The platform administration target is unavailable.",
199
+ platform_quorum_unavailable: "Platform administration quorum is unavailable.",
200
+ platform_recovery_required: "Platform administration requires separate recovery.",
201
+ identity_authority_unavailable: "Authentication is temporarily unavailable."
202
+ };
203
+ var AuthError = class extends Error {
204
+ code;
205
+ status;
206
+ retryAfterSeconds;
207
+ constructor(code, status, retryAfterSeconds) {
208
+ super(messages[code]);
209
+ this.name = "AuthError";
210
+ this.code = code;
211
+ this.status = status;
212
+ this.retryAfterSeconds = retryAfterSeconds;
213
+ }
214
+ };
215
+ //#endregion
216
+ //#region src/user-api/credential.ts
217
+ function userApiApplication(input) {
218
+ if (!input || typeof input.applicationId !== "string" || !/^[A-Za-z0-9_-]{1,128}$(?![\s\S])/.test(input.applicationId) || input.forceRefresh !== void 0 && typeof input.forceRefresh !== "boolean") throw new TypeError("User API credential input is invalid");
219
+ return input.applicationId;
220
+ }
221
+ function normalizeUserApiCredential(value, startedAt) {
222
+ if (!value || typeof value !== "object" || Array.isArray(value) || !("tokenType" in value) || value.tokenType !== "Bearer" || !("token" in value) || typeof value.token !== "string" || value.token.length + 7 > 16 * 1024 || !/^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/.test(value.token) || !("expiresIn" in value) || typeof value.expiresIn !== "number" || !Number.isInteger(value.expiresIn) || value.expiresIn < 1 || value.expiresIn > 840 || Object.keys(value).sort().join(",") !== "expiresIn,token,tokenType") throw new AuthError("identity_authority_unavailable", 503);
223
+ return {
224
+ tokenType: "Bearer",
225
+ token: value.token,
226
+ expiresAt: new Date(startedAt + value.expiresIn * 1e3)
227
+ };
228
+ }
229
+ function createUserApiCredentialCache(issue, now) {
230
+ let generation = 0;
231
+ let cached;
232
+ let pending;
233
+ const copy = (value) => ({
234
+ ...value,
235
+ expiresAt: new Date(value.expiresAt)
236
+ });
237
+ return {
238
+ hasAuthority: () => Boolean(cached || pending),
239
+ clear() {
240
+ generation += 1;
241
+ cached = void 0;
242
+ pending = void 0;
243
+ },
244
+ async get(input) {
245
+ const applicationId = userApiApplication(input);
246
+ if (pending) {
247
+ if (pending.applicationId !== applicationId) throw new AuthError("state_conflict", 409);
248
+ return copy(await pending.request);
249
+ }
250
+ const startedAt = now();
251
+ if (!input.forceRefresh && cached?.applicationId === applicationId && cached.expiresAt - startedAt > 12e4) return {
252
+ tokenType: "Bearer",
253
+ token: cached.token,
254
+ expiresAt: new Date(cached.expiresAt)
255
+ };
256
+ cached = void 0;
257
+ const epoch = generation;
258
+ const request = (async () => {
259
+ const value = normalizeUserApiCredential(await issue(applicationId), startedAt);
260
+ if (epoch !== generation) throw new AuthError("state_conflict", 409);
261
+ if (now() < startedAt || value.expiresAt.getTime() <= now()) throw new AuthError("authentication_required", 401);
262
+ cached = {
263
+ applicationId,
264
+ token: value.token,
265
+ expiresAt: value.expiresAt.getTime()
266
+ };
267
+ return value;
268
+ })().finally(() => {
269
+ if (pending?.request === request) pending = void 0;
270
+ });
271
+ pending = {
272
+ applicationId,
273
+ request
274
+ };
275
+ return copy(await request);
276
+ }
277
+ };
278
+ }
279
+ //#endregion
176
280
  //#region ../../node_modules/.pnpm/destr@2.0.5/node_modules/destr/dist/index.mjs
177
281
  const suspectProtoRx = /"(?:_|\\u0{2}5[Ff]){2}(?:p|\\u0{2}70)(?:r|\\u0{2}72)(?:o|\\u0{2}6[Ff])(?:t|\\u0{2}74)(?:o|\\u0{2}6[Ff])(?:_|\\u0{2}5[Ff]){2}"\s*:/;
178
282
  const suspectConstructorRx = /"(?:c|\\u0063)(?:o|\\u006[Ff])(?:n|\\u006[Ee])(?:s|\\u0073)(?:t|\\u0074)(?:r|\\u0072)(?:u|\\u0075)(?:c|\\u0063)(?:t|\\u0074)(?:o|\\u006[Ff])(?:r|\\u0072)"\s*:/;
@@ -436,45 +540,6 @@ function createFetch(globalOptions = {}) {
436
540
  return $fetch;
437
541
  }
438
542
  //#endregion
439
- //#region src/errors.ts
440
- const messages = {
441
- invalid_credentials: "The credentials are invalid.",
442
- invalid_auth_input: "The authentication input is invalid.",
443
- account_conflict: "The account is not available.",
444
- password_reset_invalid: "The password reset request is invalid.",
445
- authentication_required: "Authentication is required.",
446
- request_origin_denied: "The request origin is not allowed.",
447
- invalid_request: "The request is invalid.",
448
- invalid_invitation: "The invitation is invalid.",
449
- invitation_unavailable: "The invitation is not available.",
450
- recipient_unavailable: "The recipient is unavailable.",
451
- invitation_recipient_mismatch: "The invitation cannot be accepted by this account.",
452
- invitation_recovery_required: "Invitation administration requires separate recovery.",
453
- email_verification_required: "Email verification is required.",
454
- invalid_workspace_selection: "The workspace selection is invalid.",
455
- organization_context_too_large: "The organization context is too large.",
456
- credential_rate_limited: "Credential requests are temporarily limited.",
457
- auth_request_rate_limited: "Authentication requests are temporarily limited.",
458
- state_conflict: "The authentication state conflicts with the request.",
459
- access_denied: "Access is denied.",
460
- platform_target_unavailable: "The platform administration target is unavailable.",
461
- platform_quorum_unavailable: "Platform administration quorum is unavailable.",
462
- platform_recovery_required: "Platform administration requires separate recovery.",
463
- identity_authority_unavailable: "Authentication is temporarily unavailable."
464
- };
465
- var AuthError = class extends Error {
466
- code;
467
- status;
468
- retryAfterSeconds;
469
- constructor(code, status, retryAfterSeconds) {
470
- super(messages[code]);
471
- this.name = "AuthError";
472
- this.code = code;
473
- this.status = status;
474
- this.retryAfterSeconds = retryAfterSeconds;
475
- }
476
- };
477
- //#endregion
478
543
  //#region src/shared.ts
479
544
  const DRIVE_CREDENTIAL_LIFETIME_SECONDS = 840;
480
545
  const id = /^[A-Za-z0-9_-]{1,128}$/;
@@ -652,6 +717,27 @@ function normalizeOrganizationTeamDisplay(value) {
652
717
  throw authorityUnavailable();
653
718
  }
654
719
  }
720
+ function normalizeOrganizationCreationInput(name, slug, idempotencyKey) {
721
+ if (!boundedString(name, 255) || !boundedString(idempotencyKey, 255) || typeof slug !== "string" || !/^[a-z0-9-]{1,128}$/.test(slug)) throw new AuthError("invalid_request", 400);
722
+ return {
723
+ name,
724
+ slug,
725
+ idempotencyKey
726
+ };
727
+ }
728
+ function normalizeCreatedOrganization(value) {
729
+ exact(value, [
730
+ "id",
731
+ "name",
732
+ "slug"
733
+ ]);
734
+ if (!isId(value.id) || !boundedString(value.name, 255) || !boundedString(value.slug, 128)) throw authorityUnavailable();
735
+ return {
736
+ id: value.id,
737
+ name: value.name,
738
+ slug: value.slug
739
+ };
740
+ }
655
741
  function normalizeOrganizationTeamCreationInput(name, idempotencyKey) {
656
742
  if (!boundedString(name, 255) || !boundedString(idempotencyKey, 255)) throw new AuthError("invalid_request", 400);
657
743
  return {
@@ -1165,7 +1251,18 @@ function createDeviceAuth(options) {
1165
1251
  let authorityTail = Promise.resolve();
1166
1252
  let persistenceTail = Promise.resolve();
1167
1253
  let persistencePending = 0;
1254
+ const userApiCredentials = createUserApiCredentialCache(async (applicationId) => {
1255
+ const authority = await serializeAuthority(async () => ({
1256
+ current: await readToken(),
1257
+ generation: authorityGeneration
1258
+ }));
1259
+ if (!authority.current) throw new AuthError("authentication_required", 401);
1260
+ const response = await authenticated("user-api-credential", authority.current, { applicationId });
1261
+ if (authority.generation !== authorityGeneration) throw new AuthError("state_conflict", 409);
1262
+ return response.value;
1263
+ }, now);
1168
1264
  function clearCredential() {
1265
+ userApiCredentials.clear();
1169
1266
  credential = void 0;
1170
1267
  selectedWorkspaceKey = void 0;
1171
1268
  credentialGeneration += 1;
@@ -1520,6 +1617,30 @@ function createDeviceAuth(options) {
1520
1617
  const member = normalizeIdInput(memberId);
1521
1618
  return serializeAuthority(() => organizationRead(`${organization}/members/${member}/authority`, void 0, (value) => normalizeOrganizationMemberAuthority(value, organization, member)));
1522
1619
  },
1620
+ createOrganization(name, slug, idempotencyKey) {
1621
+ let body;
1622
+ try {
1623
+ body = normalizeOrganizationCreationInput(name, slug, idempotencyKey);
1624
+ } catch (error) {
1625
+ return Promise.reject(error);
1626
+ }
1627
+ return serializeAuthority(async () => {
1628
+ const current = await readToken();
1629
+ if (!current) throw new AuthError("authentication_required", 401);
1630
+ return normalizeCreatedOrganization((await requestJsonResponse(joinURL(authOrigin, "api/v1/auth/organizations"), {
1631
+ fetch: requestFetch,
1632
+ timeoutMs,
1633
+ method: "POST",
1634
+ credentials: "omit",
1635
+ headers: {
1636
+ authorization: `Bearer ${current.accessToken}`,
1637
+ "content-type": "application/json"
1638
+ },
1639
+ body,
1640
+ mapError: fromOrganizationMutationAuthorityError
1641
+ })).value);
1642
+ });
1643
+ },
1523
1644
  createOrganizationTeam(organizationId, name, idempotencyKey) {
1524
1645
  let id;
1525
1646
  let body;
@@ -1782,6 +1903,9 @@ function createDeviceAuth(options) {
1782
1903
  return session;
1783
1904
  }, true);
1784
1905
  },
1906
+ getUserApiCredential(input) {
1907
+ return userApiCredentials.get(input);
1908
+ },
1785
1909
  getDriveCredential(input) {
1786
1910
  let workspace;
1787
1911
  let forceRefresh;
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { A as PersonalWorkspaceSelection, B as RecentAuthenticationStatus, C as OrganizationPage, D as OrganizationTeamMemberPage, E as OrganizationTeamDisplay, F as PlatformRoleChangeRecentAuthenticationInput, G as SocialIdTokenProvider, H as SendVerificationEmailInput, I as PlatformRoleFirstApproval, J as UpdateProfileInput, K as UpdateOrganizationDisplayInput, L as PlatformStatus, M as PlatformRole, N as PlatformRoleApplied, O as OrganizationTeamPage, P as PlatformRoleApproval, Q as AuthErrorStatus, R as ReceivedOrganizationInvitation, S as OrganizationMemberPage, T as OrganizationTeam, U as SignInInput, V as ResentOrganizationInvitation, W as SocialIdTokenInput, X as AuthError, Y as WorkspaceSelection, Z as AuthErrorCode, _ as OrganizationDisplay, a as AuthPersistenceDriver, b as OrganizationMember, c as AuthUser, d as CreatedOrganizationMemberInvitation, f as CreatedOrganizationTeam, g as InvitationAcceptance, h as FinuevaAuthClient, i as AuthPersistence, j as PlatformBootstrapRoleChangeInput, k as OrganizationWorkspaceSelection, l as CanceledOrganizationInvitation, m as DriveCredentialInput, n as AuthClientOptions, o as AuthSession, p as DriveCredential, q as UpdateOrganizationTeamDisplayInput, r as AuthOrganization, s as AuthState, 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";
1
+ import { $ as AuthError, A as OrganizationTeamPage, B as ReceivedOrganizationInvitation, C as OrganizationMemberAuthority, D as OrganizationTeam, E as OrganizationPageInput, F as PlatformRoleApplied, G as SendVerificationEmailInput, H as RecentAuthenticationStatus, I as PlatformRoleApproval, J as SocialIdTokenProvider, K as SignInInput, L as PlatformRoleChangeRecentAuthenticationInput, M as PersonalWorkspaceSelection, N as PlatformBootstrapRoleChangeInput, O as OrganizationTeamDisplay, P as PlatformRole, Q as WorkspaceSelection, R as PlatformRoleFirstApproval, S as OrganizationMember, T as OrganizationPage, U as RemovedOrganizationMember, V as ReceivedOrganizationInvitationPage, W as ResentOrganizationInvitation, X as UpdateOrganizationTeamDisplayInput, Y as UpdateOrganizationDisplayInput, Z as UpdateProfileInput, _ as InvitationAcceptance, a as AuthPersistenceDriver, b as OrganizationInvitation, c as AuthUser, d as CreatedOrganization, et as AuthErrorCode, f as CreatedOrganizationMemberInvitation, g as FinuevaAuthClient, h as DriveCredentialInput, i as AuthPersistence, j as OrganizationWorkspaceSelection, k as OrganizationTeamMemberPage, l as CanceledOrganizationInvitation, m as DriveCredential, n as AuthClientOptions, nt as UserApiCredential, o as AuthSession, p as CreatedOrganizationTeam, q as SocialIdTokenInput, r as AuthOrganization, rt as UserApiCredentialInput, s as AuthState, t as AcceptInvitationInput, tt as AuthErrorStatus, u as CreateOrganizationMemberInvitationInput, v as OrganizationDeparture, w as OrganizationMemberPage, x as OrganizationInvitationPage, y as OrganizationDisplay, z as PlatformStatus } from "./types-CrdIcEJ3.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 CanceledOrganizationInvitation, type CreateOrganizationMemberInvitationInput, type CreatedOrganizationMemberInvitation, type CreatedOrganizationTeam, type DriveCredential, type DriveCredentialInput, type FinuevaAuthClient, 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 PlatformBootstrapRoleChangeInput, type PlatformRole, type PlatformRoleApplied, type PlatformRoleApproval, type PlatformRoleChangeRecentAuthenticationInput, type PlatformRoleFirstApproval, type PlatformStatus, type ReceivedOrganizationInvitation, type ReceivedOrganizationInvitationPage, type RecentAuthenticationStatus, type ResentOrganizationInvitation, type SendVerificationEmailInput, type SignInInput, type SocialIdTokenInput, type SocialIdTokenProvider, type UpdateOrganizationDisplayInput, type UpdateOrganizationTeamDisplayInput, 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 CanceledOrganizationInvitation, type CreateOrganizationMemberInvitationInput, type CreatedOrganization, type CreatedOrganizationMemberInvitation, type CreatedOrganizationTeam, type DriveCredential, type DriveCredentialInput, type FinuevaAuthClient, type InvitationAcceptance, type OrganizationDeparture, 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 PlatformBootstrapRoleChangeInput, type PlatformRole, type PlatformRoleApplied, type PlatformRoleApproval, type PlatformRoleChangeRecentAuthenticationInput, type PlatformRoleFirstApproval, type PlatformStatus, type ReceivedOrganizationInvitation, type ReceivedOrganizationInvitationPage, type RecentAuthenticationStatus, type RemovedOrganizationMember, type ResentOrganizationInvitation, type SendVerificationEmailInput, type SignInInput, type SocialIdTokenInput, type SocialIdTokenProvider, type UpdateOrganizationDisplayInput, type UpdateOrganizationTeamDisplayInput, type UpdateProfileInput, type UserApiCredential, type UserApiCredentialInput, type WorkspaceSelection, createAuthClient };
package/dist/index.js CHANGED
@@ -2546,6 +2546,7 @@ const messages = {
2546
2546
  recipient_unavailable: "The recipient is unavailable.",
2547
2547
  invitation_recipient_mismatch: "The invitation cannot be accepted by this account.",
2548
2548
  invitation_recovery_required: "Invitation administration requires separate recovery.",
2549
+ membership_removal_recovery_required: "Membership removal requires separate recovery.",
2549
2550
  email_verification_required: "Email verification is required.",
2550
2551
  invalid_workspace_selection: "The workspace selection is invalid.",
2551
2552
  organization_context_too_large: "The organization context is too large.",
@@ -3027,6 +3028,27 @@ function normalizeOrganizationTeamDisplay(value) {
3027
3028
  throw authorityUnavailable();
3028
3029
  }
3029
3030
  }
3031
+ function normalizeOrganizationCreationInput(name, slug, idempotencyKey) {
3032
+ if (!boundedString(name, 255) || !boundedString(idempotencyKey, 255) || typeof slug !== "string" || !/^[a-z0-9-]{1,128}$/.test(slug)) throw new AuthError("invalid_request", 400);
3033
+ return {
3034
+ name,
3035
+ slug,
3036
+ idempotencyKey
3037
+ };
3038
+ }
3039
+ function normalizeCreatedOrganization(value) {
3040
+ exact(value, [
3041
+ "id",
3042
+ "name",
3043
+ "slug"
3044
+ ]);
3045
+ if (!isId(value.id) || !boundedString(value.name, 255) || !boundedString(value.slug, 128)) throw authorityUnavailable();
3046
+ return {
3047
+ id: value.id,
3048
+ name: value.name,
3049
+ slug: value.slug
3050
+ };
3051
+ }
3030
3052
  function normalizeOrganizationTeamCreationInput(name, idempotencyKey) {
3031
3053
  if (!boundedString(name, 255) || !boundedString(idempotencyKey, 255)) throw new AuthError("invalid_request", 400);
3032
3054
  return {
@@ -3083,6 +3105,20 @@ function normalizeCanceledOrganizationInvitation(value) {
3083
3105
  if (value.status !== "canceled") throw authorityUnavailable();
3084
3106
  return { status: "canceled" };
3085
3107
  }
3108
+ function normalizeOrganizationMembershipRemovalInput(idempotencyKey) {
3109
+ if (!boundedString(idempotencyKey, 255)) throw new AuthError("invalid_request", 400);
3110
+ return { idempotencyKey };
3111
+ }
3112
+ function normalizeRemovedOrganizationMember(value) {
3113
+ exact(value, ["status"]);
3114
+ if (value.status !== "removed") throw authorityUnavailable();
3115
+ return { status: "removed" };
3116
+ }
3117
+ function normalizeOrganizationDeparture(value) {
3118
+ exact(value, ["status"]);
3119
+ if (value.status !== "left") throw authorityUnavailable();
3120
+ return { status: "left" };
3121
+ }
3086
3122
  function normalizeOrganizations(value) {
3087
3123
  exact(value, ["nextCursor", "organizations"]);
3088
3124
  if (!Array.isArray(value.organizations) || value.organizations.length > 100) throw authorityUnavailable();
@@ -3318,6 +3354,10 @@ function fromOrganizationInvitationAdministrationError(value, status, retryAfter
3318
3354
  if (status === 422 && isObject(value) && value.code === "RECOVERY_REQUIRED") return new AuthError("invitation_recovery_required", 422);
3319
3355
  return fromOrganizationMutationAuthorityError(value, status, retryAfter);
3320
3356
  }
3357
+ function fromOrganizationMembershipRemovalError(value, status, retryAfter) {
3358
+ if (status === 422 && isObject(value) && value.code === "RECOVERY_REQUIRED") return new AuthError("membership_removal_recovery_required", 422);
3359
+ return fromOrganizationMutationAuthorityError(value, status, retryAfter);
3360
+ }
3321
3361
  function normalizePlatformRoleChangeRecentAuthenticationInput(value) {
3322
3362
  if (!isObject(value) || Object.keys(value).sort().join(",") !== "changeReference,expectedGlobalRevision,expectedTargetRevision,idempotencyKey,reasonCategory,requestedRoles,targetUserId") throw new TypeError("recent authentication input is invalid");
3323
3363
  if (!isId(value.targetUserId)) throw new TypeError("targetUserId is invalid");
@@ -4456,6 +4496,70 @@ function byteLength(value) {
4456
4496
  return new TextEncoder().encode(value).byteLength;
4457
4497
  }
4458
4498
  //#endregion
4499
+ //#region src/user-api/credential.ts
4500
+ function userApiApplication(input) {
4501
+ if (!input || typeof input.applicationId !== "string" || !/^[A-Za-z0-9_-]{1,128}$(?![\s\S])/.test(input.applicationId) || input.forceRefresh !== void 0 && typeof input.forceRefresh !== "boolean") throw new TypeError("User API credential input is invalid");
4502
+ return input.applicationId;
4503
+ }
4504
+ function normalizeUserApiCredential(value, startedAt) {
4505
+ if (!value || typeof value !== "object" || Array.isArray(value) || !("tokenType" in value) || value.tokenType !== "Bearer" || !("token" in value) || typeof value.token !== "string" || value.token.length + 7 > 16 * 1024 || !/^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/.test(value.token) || !("expiresIn" in value) || typeof value.expiresIn !== "number" || !Number.isInteger(value.expiresIn) || value.expiresIn < 1 || value.expiresIn > 840 || Object.keys(value).sort().join(",") !== "expiresIn,token,tokenType") throw new AuthError("identity_authority_unavailable", 503);
4506
+ return {
4507
+ tokenType: "Bearer",
4508
+ token: value.token,
4509
+ expiresAt: new Date(startedAt + value.expiresIn * 1e3)
4510
+ };
4511
+ }
4512
+ function createUserApiCredentialCache(issue, now) {
4513
+ let generation = 0;
4514
+ let cached;
4515
+ let pending;
4516
+ const copy = (value) => ({
4517
+ ...value,
4518
+ expiresAt: new Date(value.expiresAt)
4519
+ });
4520
+ return {
4521
+ hasAuthority: () => Boolean(cached || pending),
4522
+ clear() {
4523
+ generation += 1;
4524
+ cached = void 0;
4525
+ pending = void 0;
4526
+ },
4527
+ async get(input) {
4528
+ const applicationId = userApiApplication(input);
4529
+ if (pending) {
4530
+ if (pending.applicationId !== applicationId) throw new AuthError("state_conflict", 409);
4531
+ return copy(await pending.request);
4532
+ }
4533
+ const startedAt = now();
4534
+ if (!input.forceRefresh && cached?.applicationId === applicationId && cached.expiresAt - startedAt > 12e4) return {
4535
+ tokenType: "Bearer",
4536
+ token: cached.token,
4537
+ expiresAt: new Date(cached.expiresAt)
4538
+ };
4539
+ cached = void 0;
4540
+ const epoch = generation;
4541
+ const request = (async () => {
4542
+ const value = normalizeUserApiCredential(await issue(applicationId), startedAt);
4543
+ if (epoch !== generation) throw new AuthError("state_conflict", 409);
4544
+ if (now() < startedAt || value.expiresAt.getTime() <= now()) throw new AuthError("authentication_required", 401);
4545
+ cached = {
4546
+ applicationId,
4547
+ token: value.token,
4548
+ expiresAt: value.expiresAt.getTime()
4549
+ };
4550
+ return value;
4551
+ })().finally(() => {
4552
+ if (pending?.request === request) pending = void 0;
4553
+ });
4554
+ pending = {
4555
+ applicationId,
4556
+ request
4557
+ };
4558
+ return copy(await request);
4559
+ }
4560
+ };
4561
+ }
4562
+ //#endregion
4459
4563
  //#region src/client.ts
4460
4564
  const REFRESH_WINDOW_MS = 120 * 1e3;
4461
4565
  const RECENT_AUTHENTICATION_BROWSER_HEADER = "x-finueva-recent-auth-browser";
@@ -4468,6 +4572,13 @@ function createAuthClient(options) {
4468
4572
  if (typeof fetchImpl !== "function") throw new TypeError("fetch is required");
4469
4573
  const now = options.now ?? Date.now;
4470
4574
  const timeoutMs = normalizeTimeout(options.timeoutMs);
4575
+ const userApiCredentials = createUserApiCredentialCache((applicationId) => requestJson(joinURL(authOrigin, "api/v1/auth/user-api-credential"), {
4576
+ fetch: fetchImpl,
4577
+ timeoutMs,
4578
+ method: "POST",
4579
+ headers: { "content-type": "application/json; charset=utf-8" },
4580
+ body: { applicationId }
4581
+ }), now);
4471
4582
  const betterAuth = createAuthClient$1({
4472
4583
  baseURL: authOrigin,
4473
4584
  basePath: "/api/v1/auth",
@@ -4622,13 +4733,14 @@ function createAuthClient(options) {
4622
4733
  signalRefreshRequest = request;
4623
4734
  }
4624
4735
  function clearCredential() {
4736
+ userApiCredentials.clear();
4625
4737
  credential = void 0;
4626
4738
  credentialRequests.clear();
4627
4739
  selectedWorkspaceKey = void 0;
4628
4740
  credentialGeneration += 1;
4629
4741
  }
4630
4742
  function updateSession(session) {
4631
- const unboundCredential = !sessionId && (credential || credentialRequests.size > 0);
4743
+ const unboundCredential = !sessionId && (credential || credentialRequests.size > 0 || userApiCredentials.hasAuthority());
4632
4744
  const changedSession = sessionId === void 0 ? !sessionAuthorityObserved || session !== null : !session || session.id !== sessionId || session.user.id !== sessionUserId;
4633
4745
  if (!session || unboundCredential || sessionId && (session.id !== sessionId || session.user.id !== sessionUserId)) clearCredential();
4634
4746
  if (changedSession || recentAuthenticationStartAttempt && (recentAuthenticationStartAttempt.sessionId !== session?.id || recentAuthenticationStartAttempt.userId !== session?.user.id)) recentAuthenticationStartAttempt = void 0;
@@ -4977,6 +5089,21 @@ function createAuthClient(options) {
4977
5089
  const member = normalizeIdInput(memberId);
4978
5090
  return organizationRead(`${organization}/members/${member}/authority`, void 0, (value) => normalizeOrganizationMemberAuthority(value, organization, member));
4979
5091
  },
5092
+ async createOrganization(name, slug, idempotencyKey) {
5093
+ const body = normalizeOrganizationCreationInput(name, slug, idempotencyKey);
5094
+ if (signOutRequest || activeAccountMutationGeneration !== void 0 || invitationRequest) throw new AuthError("state_conflict", 409);
5095
+ const generation = authorityGeneration;
5096
+ const value = await requestJson(joinURL(authOrigin, "api/v1/auth/organizations"), {
5097
+ fetch: fetchImpl,
5098
+ timeoutMs,
5099
+ method: "POST",
5100
+ headers: { "content-type": "application/json" },
5101
+ body,
5102
+ mapError: fromOrganizationMutationAuthorityError
5103
+ });
5104
+ if (generation !== authorityGeneration) throw new AuthError("state_conflict", 409);
5105
+ return normalizeCreatedOrganization(value);
5106
+ },
4980
5107
  async createOrganizationTeam(organizationId, name, idempotencyKey) {
4981
5108
  const id = normalizeIdInput(organizationId);
4982
5109
  const body = normalizeOrganizationTeamCreationInput(name, idempotencyKey);
@@ -5043,6 +5170,39 @@ function createAuthClient(options) {
5043
5170
  if (generation !== authorityGeneration) throw new AuthError("state_conflict", 409);
5044
5171
  return normalizeCanceledOrganizationInvitation(value);
5045
5172
  },
5173
+ async removeOrganizationMember(organizationId, memberId, idempotencyKey) {
5174
+ const organization = normalizeIdInput(organizationId);
5175
+ const member = normalizeIdInput(memberId);
5176
+ const body = normalizeOrganizationMembershipRemovalInput(idempotencyKey);
5177
+ if (signOutRequest || activeAccountMutationGeneration !== void 0 || invitationRequest) throw new AuthError("state_conflict", 409);
5178
+ const generation = authorityGeneration;
5179
+ const value = await requestJson(joinURL(authOrigin, "api/v1/auth/organizations", organization, "members", member, "remove"), {
5180
+ fetch: fetchImpl,
5181
+ timeoutMs,
5182
+ method: "POST",
5183
+ headers: { "content-type": "application/json" },
5184
+ body,
5185
+ mapError: fromOrganizationMembershipRemovalError
5186
+ });
5187
+ if (generation !== authorityGeneration) throw new AuthError("state_conflict", 409);
5188
+ return normalizeRemovedOrganizationMember(value);
5189
+ },
5190
+ async leaveOrganization(organizationId, idempotencyKey) {
5191
+ const organization = normalizeIdInput(organizationId);
5192
+ const body = normalizeOrganizationMembershipRemovalInput(idempotencyKey);
5193
+ if (signOutRequest || activeAccountMutationGeneration !== void 0 || invitationRequest) throw new AuthError("state_conflict", 409);
5194
+ const generation = authorityGeneration;
5195
+ const value = await requestJson(joinURL(authOrigin, "api/v1/auth/organizations", organization, "leave"), {
5196
+ fetch: fetchImpl,
5197
+ timeoutMs,
5198
+ method: "POST",
5199
+ headers: { "content-type": "application/json" },
5200
+ body,
5201
+ mapError: fromOrganizationMembershipRemovalError
5202
+ });
5203
+ if (generation !== authorityGeneration) throw new AuthError("state_conflict", 409);
5204
+ return normalizeOrganizationDeparture(value);
5205
+ },
5046
5206
  async updateOrganizationDisplay(organizationId, input) {
5047
5207
  const id = normalizeIdInput(organizationId);
5048
5208
  const desired = normalizeOrganizationDisplayInput(input);
@@ -5197,6 +5357,10 @@ function createAuthClient(options) {
5197
5357
  }));
5198
5358
  },
5199
5359
  signOut,
5360
+ getUserApiCredential(input) {
5361
+ if (invitationRequest || signOutRequest || activeAccountMutationGeneration !== void 0) return Promise.reject(new AuthError("state_conflict", 409));
5362
+ return userApiCredentials.get(input);
5363
+ },
5200
5364
  getDriveCredential(input) {
5201
5365
  let workspace;
5202
5366
  try {