@finueva/auth 0.10.1 → 0.11.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
@@ -2,7 +2,9 @@
2
2
 
3
3
  Use `@finueva/auth` to add Finueva sign-in, organization access, invitations, and Drive credentials to a browser, server, CLI, or native application.
4
4
 
5
- The current published version is `0.10.0`. The package is ESM-only. Node.js consumers require Node.js 22 or later. Other server runtimes must provide compatible Fetch, Web Crypto, URL, and Web Streams APIs. Its interface is Experimental until version `1.0.0`.
5
+ The current published version is `0.10.1`. The package is ESM-only. Node.js consumers require Node.js 22 or later. Other server runtimes must provide compatible Fetch, Web Crypto, URL, and Web Streams APIs. Its interface is Experimental until version `1.0.0`.
6
+
7
+ The passkey and recent-authentication methods below describe this repository's next package candidate. Published version `0.10.1` does not include them.
6
8
 
7
9
  ## Install
8
10
 
@@ -76,21 +78,23 @@ The browser owns the session cookie. The SDK does not expose or persist it.
76
78
 
77
79
  ## Common Tasks
78
80
 
79
- | Task | Browser | Server | Device |
80
- | ------------------------- | ------------------------------------------- | -------------------------------------- | -------------------------------------- |
81
- | Create an account | `signUp()` | Not available | `signUpWithEmail()` |
82
- | Read the current session | `getSession()` | `getSession()` | `getSession()` |
83
- | Observe session state | `onAuthStateChange()` | Not available | Not available |
84
- | Update a profile | `updateProfile()` | Not available | `updateProfile()` |
85
- | Change a password | `changePassword()` | Not available | `changePassword()` |
86
- | Reset a password | `requestPasswordReset()`, `resetPassword()` | Not available | Not available |
87
- | Verify an email | `sendVerificationEmail()` | Not available | Not available |
88
- | List organizations | `listOrganizations()` | `listOrganizations()` | `listOrganizations()` |
89
- | Create an ordinary team | `createOrganizationTeam()` | `createOrganizationTeam()` | `createOrganizationTeam()` |
90
- | Invite an ordinary member | `createOrganizationMemberInvitation()` | `createOrganizationMemberInvitation()` | `createOrganizationMemberInvitation()` |
91
- | Accept an invitation | `acceptInvitation()` | `acceptInvitation()` | Not available |
92
- | Request Drive access | `getDriveCredential()` | `getDriveCredential()` | `getDriveCredential()` |
93
- | End the current session | `signOut()` | `signOut()` | `revokeCurrentDevice()` |
81
+ | Task | Browser | Server | Device |
82
+ | ----------------------------- | ------------------------------------------- | -------------------------------------- | -------------------------------------- |
83
+ | Create an account | `signUp()` | Not available | `signUpWithEmail()` |
84
+ | Read the current session | `getSession()` | `getSession()` | `getSession()` |
85
+ | Observe session state | `onAuthStateChange()` | Not available | Not available |
86
+ | Update a profile | `updateProfile()` | Not available | `updateProfile()` |
87
+ | Change a password | `changePassword()` | Not available | `changePassword()` |
88
+ | Reset a password | `requestPasswordReset()`, `resetPassword()` | Not available | Not available |
89
+ | Verify an email | `sendVerificationEmail()` | Not available | Not available |
90
+ | List organizations | `listOrganizations()` | `listOrganizations()` | `listOrganizations()` |
91
+ | Create an ordinary team | `createOrganizationTeam()` | `createOrganizationTeam()` | `createOrganizationTeam()` |
92
+ | Invite an ordinary member | `createOrganizationMemberInvitation()` | `createOrganizationMemberInvitation()` | `createOrganizationMemberInvitation()` |
93
+ | Accept an invitation | `acceptInvitation()` | `acceptInvitation()` | Not available |
94
+ | Enroll a passkey | `addPasskey()` | Not available | Not available |
95
+ | Perform recent authentication | Three `*RecentAuthentication*()` methods | Not available | Not available |
96
+ | Request Drive access | `getDriveCredential()` | `getDriveCredential()` | `getDriveCredential()` |
97
+ | End the current session | `signOut()` | `signOut()` | `revokeCurrentDevice()` |
94
98
 
95
99
  ## Create An Organization Invitation
96
100
 
@@ -108,6 +112,38 @@ console.log(invitation.id, invitation.expiresAt);
108
112
 
109
113
  If the response is lost, repeat the same request with the same key. The SDK does not retry automatically. The result confirms invitation creation. It does not confirm email delivery.
110
114
 
115
+ ## Perform Recent Authentication
116
+
117
+ 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:
118
+
119
+ ```ts
120
+ await auth.signIn({ email, password });
121
+ await auth.addPasskey();
122
+ ```
123
+
124
+ The enrollment authorization expires after five minutes. It is single-use. Signup, social sign-in, session refresh, bearer use, and generic session creation do not authorize enrollment. A User without a fresh password sign-in cannot use this path.
125
+
126
+ Start one reviewed platform-role action before invoking passkey authentication:
127
+
128
+ ```ts
129
+ await auth.startPlatformRoleChangeRecentAuthentication({
130
+ targetUserId: "user_01J8R7M6N5P4Q3S2T1",
131
+ requestedRoles: ["administrator", "investigator"],
132
+ expectedRevision: 4,
133
+ });
134
+
135
+ await auth.verifyRecentAuthenticationWithPasskey();
136
+
137
+ const continuation = await auth.getRecentAuthenticationStatus();
138
+ if (continuation.status === "ready") {
139
+ showReviewedActionIsReady();
140
+ }
141
+ ```
142
+
143
+ The maintained passkey client performs the WebAuthn ceremony for the pending action when a compatible authenticator is present. Auth binds the exact generated challenge to that action. The SDK returns only `pending` or `ready`. It does not expose the challenge, WebAuthn response, session, cookies, proof, or hashes. `ready` does not grant a platform role or reusable administration mode. The tracer has no proof-consumption API. A future exact mutation must add atomic one-use consumption with its state change. No platform mutation is currently implemented.
144
+
145
+ If a start result is unavailable, call `startPlatformRoleChangeRecentAuthentication()` again with the same normalized input. The client reuses the current request nonce. A different start or observed account transition removes this local retry state. Only one start request can be active. An overlapping call returns `state_conflict` before network I/O. The SDK does not retry automatically.
146
+
111
147
  ## Request A Drive Credential
112
148
 
113
149
  Request one short-lived credential for one workspace:
@@ -47,6 +47,32 @@ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
47
47
  IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
48
48
  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
49
49
 
50
+ ## @simplewebauthn/browser 13.3.0
51
+
52
+ Source: https://github.com/MasterKale/SimpleWebAuthn
53
+
54
+ MIT License
55
+
56
+ Copyright (c) 2020 Matthew Miller
57
+
58
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
59
+ this software and associated documentation files (the "Software"), to deal in
60
+ the Software without restriction, including without limitation the rights to
61
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
62
+ the Software, and to permit persons to whom the Software is furnished to do so,
63
+ subject to the following conditions:
64
+
65
+ The above copyright notice and this permission notice shall be included in all
66
+ copies or substantial portions of the Software.
67
+
68
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
69
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
70
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
71
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
72
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
73
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
74
+ SOFTWARE.
75
+
50
76
  ## ofetch 1.5.1
51
77
 
52
78
  Source: https://github.com/unjs/ofetch
package/dist/device.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { A as ReceivedOrganizationInvitationPage, B as AuthErrorCode, C as OrganizationTeam, E as OrganizationTeamPage, F as UpdateOrganizationDisplayInput, I as UpdateOrganizationTeamDisplayInput, L as UpdateProfileInput, N as SocialIdTokenInput, P as SocialIdTokenProvider, S as OrganizationPageInput, T as OrganizationTeamMemberPage, V as AuthErrorStatus, _ as OrganizationInvitation, b as OrganizationMemberPage, d as CreatedOrganizationTeam, f as DriveCredential, g as OrganizationDisplay, k as ReceivedOrganizationInvitation, l as CreateOrganizationMemberInvitationInput, o as AuthSession, p as DriveCredentialInput, r as AuthOrganization, u as CreatedOrganizationMemberInvitation, v as OrganizationInvitationPage, w as OrganizationTeamDisplay, x as OrganizationPage, y as OrganizationMember, z as AuthError } from "./types-Byw9JQwL.js";
1
+ import { B as SocialIdTokenInput, C as OrganizationTeam, E as OrganizationTeamPage, F as ReceivedOrganizationInvitation, H as UpdateOrganizationDisplayInput, I as ReceivedOrganizationInvitationPage, J as AuthErrorStatus, K as AuthError, S as OrganizationPageInput, T as OrganizationTeamMemberPage, U as UpdateOrganizationTeamDisplayInput, V as SocialIdTokenProvider, W as UpdateProfileInput, _ as OrganizationInvitation, b as OrganizationMemberPage, d as CreatedOrganizationTeam, f as DriveCredential, g as OrganizationDisplay, l as CreateOrganizationMemberInvitationInput, o as AuthSession, p as DriveCredentialInput, q as AuthErrorCode, r as AuthOrganization, u as CreatedOrganizationMemberInvitation, v as OrganizationInvitationPage, w as OrganizationTeamDisplay, x as OrganizationPage, y as OrganizationMember } from "./types-BqpgLkQB.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 {
package/dist/device.js CHANGED
@@ -455,6 +455,9 @@ const messages = {
455
455
  auth_request_rate_limited: "Authentication requests are temporarily limited.",
456
456
  state_conflict: "The authentication state conflicts with the request.",
457
457
  access_denied: "Access is denied.",
458
+ platform_target_unavailable: "The platform administration target is unavailable.",
459
+ platform_quorum_unavailable: "Platform administration quorum is unavailable.",
460
+ platform_recovery_required: "Platform administration requires separate recovery.",
458
461
  identity_authority_unavailable: "Authentication is temporarily unavailable."
459
462
  };
460
463
  var AuthError = class extends Error {
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { A as ReceivedOrganizationInvitationPage, B as AuthErrorCode, C as OrganizationTeam, D as OrganizationWorkspaceSelection, E as OrganizationTeamPage, F as UpdateOrganizationDisplayInput, I as UpdateOrganizationTeamDisplayInput, L as UpdateProfileInput, M as SignInInput, N as SocialIdTokenInput, O as PersonalWorkspaceSelection, P as SocialIdTokenProvider, R as WorkspaceSelection, S as OrganizationPageInput, T as OrganizationTeamMemberPage, V as AuthErrorStatus, _ as OrganizationInvitation, a as AuthPersistenceDriver, b as OrganizationMemberPage, c as AuthUser, d as CreatedOrganizationTeam, f as DriveCredential, g as OrganizationDisplay, h as InvitationAcceptance, i as AuthPersistence, j as SendVerificationEmailInput, k as ReceivedOrganizationInvitation, l as CreateOrganizationMemberInvitationInput, m as FinuevaAuthClient, n as AuthClientOptions, o as AuthSession, p as DriveCredentialInput, r as AuthOrganization, s as AuthState, t as AcceptInvitationInput, u as CreatedOrganizationMemberInvitation, v as OrganizationInvitationPage, w as OrganizationTeamDisplay, x as OrganizationPage, y as OrganizationMember, z as AuthError } from "./types-Byw9JQwL.js";
1
+ import { A as PlatformRole, B as SocialIdTokenInput, C as OrganizationTeam, D as OrganizationWorkspaceSelection, E as OrganizationTeamPage, F as ReceivedOrganizationInvitation, G as WorkspaceSelection, H as UpdateOrganizationDisplayInput, I as ReceivedOrganizationInvitationPage, J as AuthErrorStatus, K as AuthError, L as RecentAuthenticationStatus, M as PlatformRoleApproval, N as PlatformRoleChangeRecentAuthenticationInput, O as PersonalWorkspaceSelection, P as PlatformRoleFirstApproval, R as SendVerificationEmailInput, S as OrganizationPageInput, T as OrganizationTeamMemberPage, U as UpdateOrganizationTeamDisplayInput, V as SocialIdTokenProvider, W as UpdateProfileInput, _ as OrganizationInvitation, a as AuthPersistenceDriver, b as OrganizationMemberPage, c as AuthUser, d as CreatedOrganizationTeam, f as DriveCredential, g as OrganizationDisplay, h as InvitationAcceptance, i as AuthPersistence, j as PlatformRoleApplied, k as PlatformBootstrapRoleChangeInput, l as CreateOrganizationMemberInvitationInput, m as FinuevaAuthClient, n as AuthClientOptions, o as AuthSession, p as DriveCredentialInput, q as AuthErrorCode, r as AuthOrganization, s as AuthState, t as AcceptInvitationInput, u as CreatedOrganizationMemberInvitation, v as OrganizationInvitationPage, w as OrganizationTeamDisplay, x as OrganizationPage, y as OrganizationMember, z as SignInInput } from "./types-BqpgLkQB.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 CreateOrganizationMemberInvitationInput, type CreatedOrganizationMemberInvitation, type CreatedOrganizationTeam, type DriveCredential, type DriveCredentialInput, type FinuevaAuthClient, type InvitationAcceptance, type OrganizationDisplay, type OrganizationInvitation, type OrganizationInvitationPage, type OrganizationMember, type OrganizationMemberPage, type OrganizationPage, type OrganizationPageInput, type OrganizationTeam, type OrganizationTeamDisplay, type OrganizationTeamMemberPage, type OrganizationTeamPage, type OrganizationWorkspaceSelection, type PersonalWorkspaceSelection, type ReceivedOrganizationInvitation, type ReceivedOrganizationInvitationPage, 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 CreateOrganizationMemberInvitationInput, type CreatedOrganizationMemberInvitation, type CreatedOrganizationTeam, type DriveCredential, type DriveCredentialInput, type FinuevaAuthClient, type InvitationAcceptance, type OrganizationDisplay, type OrganizationInvitation, type OrganizationInvitationPage, type OrganizationMember, 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 ReceivedOrganizationInvitation, type ReceivedOrganizationInvitationPage, type RecentAuthenticationStatus, type SendVerificationEmailInput, type SignInInput, type SocialIdTokenInput, type SocialIdTokenProvider, type UpdateOrganizationDisplayInput, type UpdateOrganizationTeamDisplayInput, type UpdateProfileInput, type WorkspaceSelection, createAuthClient };
package/dist/index.js CHANGED
@@ -383,6 +383,109 @@ function parseJSON(value, options = { strict: true }) {
383
383
  return betterJSONParse(value, options);
384
384
  }
385
385
  //#endregion
386
+ //#region ../../node_modules/.pnpm/better-auth@1.6.22_drizzle-kit@0.31.10_drizzle-orm@0.45.2_@libsql+client@0.17.4_kysely@_fc032d4b6d0d0a5be5267474da10b212/node_modules/better-auth/dist/client/query.mjs
387
+ const isServer$1 = () => typeof window === "undefined";
388
+ function isAuthQueryStateEqual(a, b) {
389
+ return isJsonEqual(a.data, b.data) && a.error === b.error && a.isPending === b.isPending && a.isRefetching === b.isRefetching && a.refetch === b.refetch;
390
+ }
391
+ const useAuthQuery = (initializedAtom, path, $fetch, options) => {
392
+ const value = atom({
393
+ data: null,
394
+ error: null,
395
+ isPending: true,
396
+ isRefetching: false,
397
+ refetch: (queryParams) => fn(queryParams)
398
+ });
399
+ withEquality(value, isAuthQueryStateEqual);
400
+ const fn = async (queryParams) => {
401
+ return new Promise((resolve) => {
402
+ const opts = typeof options === "function" ? options({
403
+ data: value.get().data,
404
+ error: value.get().error,
405
+ isPending: value.get().isPending
406
+ }) : options;
407
+ $fetch(path, {
408
+ ...opts,
409
+ query: {
410
+ ...opts?.query,
411
+ ...queryParams?.query
412
+ },
413
+ async onSuccess(context) {
414
+ const current = value.get();
415
+ const stableData = current.data != null && context.data != null && isJsonEqual(current.data, context.data) ? current.data : context.data;
416
+ value.set({
417
+ data: stableData,
418
+ error: null,
419
+ isPending: false,
420
+ isRefetching: false,
421
+ refetch: value.value.refetch
422
+ });
423
+ await opts?.onSuccess?.(context);
424
+ },
425
+ async onError(context) {
426
+ const { request } = context;
427
+ const retryAttempts = typeof request.retry === "number" ? request.retry : request.retry?.attempts;
428
+ const retryAttempt = request.retryAttempt || 0;
429
+ if (retryAttempts && retryAttempt < retryAttempts) return;
430
+ const isUnauthorized = context.error.status === 401;
431
+ value.set({
432
+ error: context.error,
433
+ data: isUnauthorized ? null : value.get().data,
434
+ isPending: false,
435
+ isRefetching: false,
436
+ refetch: value.value.refetch
437
+ });
438
+ await opts?.onError?.(context);
439
+ },
440
+ async onRequest(context) {
441
+ const currentValue = value.get();
442
+ value.set({
443
+ isPending: currentValue.data === null,
444
+ data: currentValue.data,
445
+ error: null,
446
+ isRefetching: true,
447
+ refetch: value.value.refetch
448
+ });
449
+ await opts?.onRequest?.(context);
450
+ }
451
+ }).catch((error) => {
452
+ value.set({
453
+ error,
454
+ data: value.get().data,
455
+ isPending: false,
456
+ isRefetching: false,
457
+ refetch: value.value.refetch
458
+ });
459
+ }).finally(() => {
460
+ resolve(void 0);
461
+ });
462
+ });
463
+ };
464
+ initializedAtom = Array.isArray(initializedAtom) ? initializedAtom : [initializedAtom];
465
+ let isInitialized = false;
466
+ const cleanups = [];
467
+ for (const initAtom of initializedAtom) {
468
+ const unbind = initAtom.subscribe(async () => {
469
+ if (isServer$1()) return;
470
+ if (isInitialized) await fn();
471
+ else onMount(value, () => {
472
+ const timeoutId = setTimeout(async () => {
473
+ if (!isInitialized) {
474
+ isInitialized = true;
475
+ await fn();
476
+ }
477
+ }, 0);
478
+ return () => {
479
+ for (const u of cleanups) u();
480
+ clearTimeout(timeoutId);
481
+ };
482
+ });
483
+ });
484
+ cleanups.push(unbind);
485
+ }
486
+ return value;
487
+ };
488
+ //#endregion
386
489
  //#region ../../node_modules/.pnpm/better-auth@1.6.22_drizzle-kit@0.31.10_drizzle-orm@0.45.2_@libsql+client@0.17.4_kysely@_fc032d4b6d0d0a5be5267474da10b212/node_modules/better-auth/dist/client/session-refresh.mjs
387
490
  const now = () => Math.floor(Date.now() / 1e3);
388
491
  const FOCUS_REFETCH_RATE_LIMIT_SECONDS = 5;
@@ -564,6 +667,15 @@ Object.freeze({
564
667
  }
565
668
  });
566
669
  //#endregion
670
+ //#region ../../node_modules/.pnpm/@better-auth+core@1.6.22_@better-auth+utils@0.4.2_@better-fetch+fetch@1.3.1_better-call_17975d57218030f5a353914665f207d6/node_modules/@better-auth/core/dist/utils/error-codes.mjs
671
+ function defineErrorCodes(codes) {
672
+ return Object.fromEntries(Object.entries(codes).map(([key, value]) => [key, {
673
+ code: key,
674
+ message: value,
675
+ toString: () => key
676
+ }]));
677
+ }
678
+ //#endregion
567
679
  //#region ../../node_modules/.pnpm/better-call@1.3.7_zod@4.4.3/node_modules/better-call/dist/error.mjs
568
680
  function isErrorStackTraceLimitWritable() {
569
681
  const desc = Object.getOwnPropertyDescriptor(Error, "stackTraceLimit");
@@ -1635,7 +1747,551 @@ function createAuthClient$1(options) {
1635
1747
  }, $fetch, pluginPathMethods, pluginsAtoms, atomListeners);
1636
1748
  }
1637
1749
  //#endregion
1638
- //#region ../../node_modules/.pnpm/@chiballc+utils@2.5.0_@hono+zod-openapi@1.5.1_hono@4.13.0_zod@4.4.3__hono@4.13.0_magic-_deb761a9672ce7cf69932b564bd2142d/node_modules/@chiballc/utils/dist/index.mjs
1750
+ //#region ../../node_modules/.pnpm/@better-auth+passkey@1.6.22_@better-auth+core@1.6.22_@better-auth+utils@0.4.2_@better-f_9dcb641568b0468b4b3f2fd37291240f/node_modules/@better-auth/passkey/dist/version-uW-pDlTC.mjs
1751
+ const PASSKEY_ERROR_CODES = defineErrorCodes({
1752
+ CHALLENGE_NOT_FOUND: "Challenge not found",
1753
+ YOU_ARE_NOT_ALLOWED_TO_REGISTER_THIS_PASSKEY: "You are not allowed to register this passkey",
1754
+ FAILED_TO_VERIFY_REGISTRATION: "Failed to verify registration",
1755
+ PASSKEY_NOT_FOUND: "Passkey not found",
1756
+ AUTHENTICATION_FAILED: "Authentication failed",
1757
+ UNABLE_TO_CREATE_SESSION: "Unable to create session",
1758
+ FAILED_TO_UPDATE_PASSKEY: "Failed to update passkey",
1759
+ PREVIOUSLY_REGISTERED: "Previously registered",
1760
+ REGISTRATION_CANCELLED: "Registration cancelled",
1761
+ AUTH_CANCELLED: "Auth cancelled",
1762
+ UNKNOWN_ERROR: "Unknown error",
1763
+ SESSION_REQUIRED: "Passkey registration requires an authenticated session",
1764
+ RESOLVE_USER_REQUIRED: "Passkey registration requires either an authenticated session or a resolveUser callback when requireSession is false",
1765
+ RESOLVED_USER_INVALID: "Resolved user is invalid"
1766
+ });
1767
+ const PACKAGE_VERSION = "1.6.22";
1768
+ //#endregion
1769
+ //#region ../../node_modules/.pnpm/@simplewebauthn+browser@13.3.0/node_modules/@simplewebauthn/browser/esm/helpers/bufferToBase64URLString.js
1770
+ function bufferToBase64URLString(buffer) {
1771
+ const bytes = new Uint8Array(buffer);
1772
+ let str = "";
1773
+ for (const charCode of bytes) str += String.fromCharCode(charCode);
1774
+ return btoa(str).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
1775
+ }
1776
+ //#endregion
1777
+ //#region ../../node_modules/.pnpm/@simplewebauthn+browser@13.3.0/node_modules/@simplewebauthn/browser/esm/helpers/base64URLStringToBuffer.js
1778
+ function base64URLStringToBuffer(base64URLString) {
1779
+ const base64 = base64URLString.replace(/-/g, "+").replace(/_/g, "/");
1780
+ const padLength = (4 - base64.length % 4) % 4;
1781
+ const padded = base64.padEnd(base64.length + padLength, "=");
1782
+ const binary = atob(padded);
1783
+ const buffer = new ArrayBuffer(binary.length);
1784
+ const bytes = new Uint8Array(buffer);
1785
+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
1786
+ return buffer;
1787
+ }
1788
+ //#endregion
1789
+ //#region ../../node_modules/.pnpm/@simplewebauthn+browser@13.3.0/node_modules/@simplewebauthn/browser/esm/helpers/browserSupportsWebAuthn.js
1790
+ function browserSupportsWebAuthn() {
1791
+ return _browserSupportsWebAuthnInternals.stubThis(globalThis?.PublicKeyCredential !== void 0 && typeof globalThis.PublicKeyCredential === "function");
1792
+ }
1793
+ const _browserSupportsWebAuthnInternals = { stubThis: (value) => value };
1794
+ //#endregion
1795
+ //#region ../../node_modules/.pnpm/@simplewebauthn+browser@13.3.0/node_modules/@simplewebauthn/browser/esm/helpers/toPublicKeyCredentialDescriptor.js
1796
+ function toPublicKeyCredentialDescriptor(descriptor) {
1797
+ const { id } = descriptor;
1798
+ return {
1799
+ ...descriptor,
1800
+ id: base64URLStringToBuffer(id),
1801
+ transports: descriptor.transports
1802
+ };
1803
+ }
1804
+ //#endregion
1805
+ //#region ../../node_modules/.pnpm/@simplewebauthn+browser@13.3.0/node_modules/@simplewebauthn/browser/esm/helpers/isValidDomain.js
1806
+ function isValidDomain(hostname) {
1807
+ return hostname === "localhost" || /^((xn--[a-z0-9-]+|[a-z0-9]+(-[a-z0-9]+)*)\.)+([a-z]{2,}|xn--[a-z0-9-]+)$/i.test(hostname);
1808
+ }
1809
+ //#endregion
1810
+ //#region ../../node_modules/.pnpm/@simplewebauthn+browser@13.3.0/node_modules/@simplewebauthn/browser/esm/helpers/webAuthnError.js
1811
+ var WebAuthnError = class extends Error {
1812
+ constructor({ message, code, cause, name }) {
1813
+ super(message, { cause });
1814
+ Object.defineProperty(this, "code", {
1815
+ enumerable: true,
1816
+ configurable: true,
1817
+ writable: true,
1818
+ value: void 0
1819
+ });
1820
+ this.name = name ?? cause.name;
1821
+ this.code = code;
1822
+ }
1823
+ };
1824
+ //#endregion
1825
+ //#region ../../node_modules/.pnpm/@simplewebauthn+browser@13.3.0/node_modules/@simplewebauthn/browser/esm/helpers/identifyRegistrationError.js
1826
+ function identifyRegistrationError({ error, options }) {
1827
+ const { publicKey } = options;
1828
+ if (!publicKey) throw Error("options was missing required publicKey property");
1829
+ if (error.name === "AbortError") {
1830
+ if (options.signal instanceof AbortSignal) return new WebAuthnError({
1831
+ message: "Registration ceremony was sent an abort signal",
1832
+ code: "ERROR_CEREMONY_ABORTED",
1833
+ cause: error
1834
+ });
1835
+ } else if (error.name === "ConstraintError") {
1836
+ if (publicKey.authenticatorSelection?.requireResidentKey === true) return new WebAuthnError({
1837
+ message: "Discoverable credentials were required but no available authenticator supported it",
1838
+ code: "ERROR_AUTHENTICATOR_MISSING_DISCOVERABLE_CREDENTIAL_SUPPORT",
1839
+ cause: error
1840
+ });
1841
+ else if (options.mediation === "conditional" && publicKey.authenticatorSelection?.userVerification === "required") return new WebAuthnError({
1842
+ message: "User verification was required during automatic registration but it could not be performed",
1843
+ code: "ERROR_AUTO_REGISTER_USER_VERIFICATION_FAILURE",
1844
+ cause: error
1845
+ });
1846
+ else if (publicKey.authenticatorSelection?.userVerification === "required") return new WebAuthnError({
1847
+ message: "User verification was required but no available authenticator supported it",
1848
+ code: "ERROR_AUTHENTICATOR_MISSING_USER_VERIFICATION_SUPPORT",
1849
+ cause: error
1850
+ });
1851
+ } else if (error.name === "InvalidStateError") return new WebAuthnError({
1852
+ message: "The authenticator was previously registered",
1853
+ code: "ERROR_AUTHENTICATOR_PREVIOUSLY_REGISTERED",
1854
+ cause: error
1855
+ });
1856
+ else if (error.name === "NotAllowedError") return new WebAuthnError({
1857
+ message: error.message,
1858
+ code: "ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY",
1859
+ cause: error
1860
+ });
1861
+ else if (error.name === "NotSupportedError") {
1862
+ if (publicKey.pubKeyCredParams.filter((param) => param.type === "public-key").length === 0) return new WebAuthnError({
1863
+ message: "No entry in pubKeyCredParams was of type \"public-key\"",
1864
+ code: "ERROR_MALFORMED_PUBKEYCREDPARAMS",
1865
+ cause: error
1866
+ });
1867
+ return new WebAuthnError({
1868
+ message: "No available authenticator supported any of the specified pubKeyCredParams algorithms",
1869
+ code: "ERROR_AUTHENTICATOR_NO_SUPPORTED_PUBKEYCREDPARAMS_ALG",
1870
+ cause: error
1871
+ });
1872
+ } else if (error.name === "SecurityError") {
1873
+ const effectiveDomain = globalThis.location.hostname;
1874
+ if (!isValidDomain(effectiveDomain)) return new WebAuthnError({
1875
+ message: `${globalThis.location.hostname} is an invalid domain`,
1876
+ code: "ERROR_INVALID_DOMAIN",
1877
+ cause: error
1878
+ });
1879
+ else if (publicKey.rp.id !== effectiveDomain) return new WebAuthnError({
1880
+ message: `The RP ID "${publicKey.rp.id}" is invalid for this domain`,
1881
+ code: "ERROR_INVALID_RP_ID",
1882
+ cause: error
1883
+ });
1884
+ } else if (error.name === "TypeError") {
1885
+ if (publicKey.user.id.byteLength < 1 || publicKey.user.id.byteLength > 64) return new WebAuthnError({
1886
+ message: "User ID was not between 1 and 64 characters",
1887
+ code: "ERROR_INVALID_USER_ID_LENGTH",
1888
+ cause: error
1889
+ });
1890
+ } else if (error.name === "UnknownError") return new WebAuthnError({
1891
+ message: "The authenticator was unable to process the specified options, or could not create a new credential",
1892
+ code: "ERROR_AUTHENTICATOR_GENERAL_ERROR",
1893
+ cause: error
1894
+ });
1895
+ return error;
1896
+ }
1897
+ //#endregion
1898
+ //#region ../../node_modules/.pnpm/@simplewebauthn+browser@13.3.0/node_modules/@simplewebauthn/browser/esm/helpers/webAuthnAbortService.js
1899
+ var BaseWebAuthnAbortService = class {
1900
+ constructor() {
1901
+ Object.defineProperty(this, "controller", {
1902
+ enumerable: true,
1903
+ configurable: true,
1904
+ writable: true,
1905
+ value: void 0
1906
+ });
1907
+ }
1908
+ createNewAbortSignal() {
1909
+ if (this.controller) {
1910
+ const abortError = new Error("Cancelling existing WebAuthn API call for new one");
1911
+ abortError.name = "AbortError";
1912
+ this.controller.abort(abortError);
1913
+ }
1914
+ const newController = new AbortController();
1915
+ this.controller = newController;
1916
+ return newController.signal;
1917
+ }
1918
+ cancelCeremony() {
1919
+ if (this.controller) {
1920
+ const abortError = new Error("Manually cancelling existing WebAuthn API call");
1921
+ abortError.name = "AbortError";
1922
+ this.controller.abort(abortError);
1923
+ this.controller = void 0;
1924
+ }
1925
+ }
1926
+ };
1927
+ const WebAuthnAbortService = new BaseWebAuthnAbortService();
1928
+ //#endregion
1929
+ //#region ../../node_modules/.pnpm/@simplewebauthn+browser@13.3.0/node_modules/@simplewebauthn/browser/esm/helpers/toAuthenticatorAttachment.js
1930
+ const attachments = ["cross-platform", "platform"];
1931
+ function toAuthenticatorAttachment(attachment) {
1932
+ if (!attachment) return;
1933
+ if (attachments.indexOf(attachment) < 0) return;
1934
+ return attachment;
1935
+ }
1936
+ //#endregion
1937
+ //#region ../../node_modules/.pnpm/@simplewebauthn+browser@13.3.0/node_modules/@simplewebauthn/browser/esm/methods/startRegistration.js
1938
+ async function startRegistration(options) {
1939
+ if (!options.optionsJSON && options.challenge) {
1940
+ console.warn("startRegistration() was not called correctly. It will try to continue with the provided options, but this call should be refactored to use the expected call structure instead. See https://simplewebauthn.dev/docs/packages/browser#typeerror-cannot-read-properties-of-undefined-reading-challenge for more information.");
1941
+ options = { optionsJSON: options };
1942
+ }
1943
+ const { optionsJSON, useAutoRegister = false } = options;
1944
+ if (!browserSupportsWebAuthn()) throw new Error("WebAuthn is not supported in this browser");
1945
+ const publicKey = {
1946
+ ...optionsJSON,
1947
+ challenge: base64URLStringToBuffer(optionsJSON.challenge),
1948
+ user: {
1949
+ ...optionsJSON.user,
1950
+ id: base64URLStringToBuffer(optionsJSON.user.id)
1951
+ },
1952
+ excludeCredentials: optionsJSON.excludeCredentials?.map(toPublicKeyCredentialDescriptor)
1953
+ };
1954
+ const createOptions = {};
1955
+ if (useAutoRegister) createOptions.mediation = "conditional";
1956
+ createOptions.publicKey = publicKey;
1957
+ createOptions.signal = WebAuthnAbortService.createNewAbortSignal();
1958
+ let credential;
1959
+ try {
1960
+ credential = await navigator.credentials.create(createOptions);
1961
+ } catch (err) {
1962
+ throw identifyRegistrationError({
1963
+ error: err,
1964
+ options: createOptions
1965
+ });
1966
+ }
1967
+ if (!credential) throw new Error("Registration was not completed");
1968
+ const { id, rawId, response, type } = credential;
1969
+ let transports = void 0;
1970
+ if (typeof response.getTransports === "function") transports = response.getTransports();
1971
+ let responsePublicKeyAlgorithm = void 0;
1972
+ if (typeof response.getPublicKeyAlgorithm === "function") try {
1973
+ responsePublicKeyAlgorithm = response.getPublicKeyAlgorithm();
1974
+ } catch (error) {
1975
+ warnOnBrokenImplementation("getPublicKeyAlgorithm()", error);
1976
+ }
1977
+ let responsePublicKey = void 0;
1978
+ if (typeof response.getPublicKey === "function") try {
1979
+ const _publicKey = response.getPublicKey();
1980
+ if (_publicKey !== null) responsePublicKey = bufferToBase64URLString(_publicKey);
1981
+ } catch (error) {
1982
+ warnOnBrokenImplementation("getPublicKey()", error);
1983
+ }
1984
+ let responseAuthenticatorData;
1985
+ if (typeof response.getAuthenticatorData === "function") try {
1986
+ responseAuthenticatorData = bufferToBase64URLString(response.getAuthenticatorData());
1987
+ } catch (error) {
1988
+ warnOnBrokenImplementation("getAuthenticatorData()", error);
1989
+ }
1990
+ return {
1991
+ id,
1992
+ rawId: bufferToBase64URLString(rawId),
1993
+ response: {
1994
+ attestationObject: bufferToBase64URLString(response.attestationObject),
1995
+ clientDataJSON: bufferToBase64URLString(response.clientDataJSON),
1996
+ transports,
1997
+ publicKeyAlgorithm: responsePublicKeyAlgorithm,
1998
+ publicKey: responsePublicKey,
1999
+ authenticatorData: responseAuthenticatorData
2000
+ },
2001
+ type,
2002
+ clientExtensionResults: credential.getClientExtensionResults(),
2003
+ authenticatorAttachment: toAuthenticatorAttachment(credential.authenticatorAttachment)
2004
+ };
2005
+ }
2006
+ function warnOnBrokenImplementation(methodName, cause) {
2007
+ console.warn(`The browser extension that intercepted this WebAuthn API call incorrectly implemented ${methodName}. You should report this error to them.\n`, cause);
2008
+ }
2009
+ //#endregion
2010
+ //#region ../../node_modules/.pnpm/@simplewebauthn+browser@13.3.0/node_modules/@simplewebauthn/browser/esm/helpers/browserSupportsWebAuthnAutofill.js
2011
+ function browserSupportsWebAuthnAutofill() {
2012
+ if (!browserSupportsWebAuthn()) return _browserSupportsWebAuthnAutofillInternals.stubThis(new Promise((resolve) => resolve(false)));
2013
+ const globalPublicKeyCredential = globalThis.PublicKeyCredential;
2014
+ if (globalPublicKeyCredential?.isConditionalMediationAvailable === void 0) return _browserSupportsWebAuthnAutofillInternals.stubThis(new Promise((resolve) => resolve(false)));
2015
+ return _browserSupportsWebAuthnAutofillInternals.stubThis(globalPublicKeyCredential.isConditionalMediationAvailable());
2016
+ }
2017
+ const _browserSupportsWebAuthnAutofillInternals = { stubThis: (value) => value };
2018
+ //#endregion
2019
+ //#region ../../node_modules/.pnpm/@simplewebauthn+browser@13.3.0/node_modules/@simplewebauthn/browser/esm/helpers/identifyAuthenticationError.js
2020
+ function identifyAuthenticationError({ error, options }) {
2021
+ const { publicKey } = options;
2022
+ if (!publicKey) throw Error("options was missing required publicKey property");
2023
+ if (error.name === "AbortError") {
2024
+ if (options.signal instanceof AbortSignal) return new WebAuthnError({
2025
+ message: "Authentication ceremony was sent an abort signal",
2026
+ code: "ERROR_CEREMONY_ABORTED",
2027
+ cause: error
2028
+ });
2029
+ } else if (error.name === "NotAllowedError") return new WebAuthnError({
2030
+ message: error.message,
2031
+ code: "ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY",
2032
+ cause: error
2033
+ });
2034
+ else if (error.name === "SecurityError") {
2035
+ const effectiveDomain = globalThis.location.hostname;
2036
+ if (!isValidDomain(effectiveDomain)) return new WebAuthnError({
2037
+ message: `${globalThis.location.hostname} is an invalid domain`,
2038
+ code: "ERROR_INVALID_DOMAIN",
2039
+ cause: error
2040
+ });
2041
+ else if (publicKey.rpId !== effectiveDomain) return new WebAuthnError({
2042
+ message: `The RP ID "${publicKey.rpId}" is invalid for this domain`,
2043
+ code: "ERROR_INVALID_RP_ID",
2044
+ cause: error
2045
+ });
2046
+ } else if (error.name === "UnknownError") return new WebAuthnError({
2047
+ message: "The authenticator was unable to process the specified options, or could not create a new assertion signature",
2048
+ code: "ERROR_AUTHENTICATOR_GENERAL_ERROR",
2049
+ cause: error
2050
+ });
2051
+ return error;
2052
+ }
2053
+ //#endregion
2054
+ //#region ../../node_modules/.pnpm/@simplewebauthn+browser@13.3.0/node_modules/@simplewebauthn/browser/esm/methods/startAuthentication.js
2055
+ async function startAuthentication(options) {
2056
+ if (!options.optionsJSON && options.challenge) {
2057
+ console.warn("startAuthentication() was not called correctly. It will try to continue with the provided options, but this call should be refactored to use the expected call structure instead. See https://simplewebauthn.dev/docs/packages/browser#typeerror-cannot-read-properties-of-undefined-reading-challenge for more information.");
2058
+ options = { optionsJSON: options };
2059
+ }
2060
+ const { optionsJSON, useBrowserAutofill = false, verifyBrowserAutofillInput = true } = options;
2061
+ if (!browserSupportsWebAuthn()) throw new Error("WebAuthn is not supported in this browser");
2062
+ let allowCredentials;
2063
+ if (optionsJSON.allowCredentials?.length !== 0) allowCredentials = optionsJSON.allowCredentials?.map(toPublicKeyCredentialDescriptor);
2064
+ const publicKey = {
2065
+ ...optionsJSON,
2066
+ challenge: base64URLStringToBuffer(optionsJSON.challenge),
2067
+ allowCredentials
2068
+ };
2069
+ const getOptions = {};
2070
+ if (useBrowserAutofill) {
2071
+ if (!await browserSupportsWebAuthnAutofill()) throw Error("Browser does not support WebAuthn autofill");
2072
+ if (document.querySelectorAll("input[autocomplete$='webauthn']").length < 1 && verifyBrowserAutofillInput) throw Error("No <input> with \"webauthn\" as the only or last value in its `autocomplete` attribute was detected");
2073
+ getOptions.mediation = "conditional";
2074
+ publicKey.allowCredentials = [];
2075
+ }
2076
+ getOptions.publicKey = publicKey;
2077
+ getOptions.signal = WebAuthnAbortService.createNewAbortSignal();
2078
+ let credential;
2079
+ try {
2080
+ credential = await navigator.credentials.get(getOptions);
2081
+ } catch (err) {
2082
+ throw identifyAuthenticationError({
2083
+ error: err,
2084
+ options: getOptions
2085
+ });
2086
+ }
2087
+ if (!credential) throw new Error("Authentication was not completed");
2088
+ const { id, rawId, response, type } = credential;
2089
+ let userHandle = void 0;
2090
+ if (response.userHandle) userHandle = bufferToBase64URLString(response.userHandle);
2091
+ return {
2092
+ id,
2093
+ rawId: bufferToBase64URLString(rawId),
2094
+ response: {
2095
+ authenticatorData: bufferToBase64URLString(response.authenticatorData),
2096
+ clientDataJSON: bufferToBase64URLString(response.clientDataJSON),
2097
+ signature: bufferToBase64URLString(response.signature),
2098
+ userHandle
2099
+ },
2100
+ type,
2101
+ clientExtensionResults: credential.getClientExtensionResults(),
2102
+ authenticatorAttachment: toAuthenticatorAttachment(credential.authenticatorAttachment)
2103
+ };
2104
+ }
2105
+ //#endregion
2106
+ //#region ../../node_modules/.pnpm/@better-auth+passkey@1.6.22_@better-auth+core@1.6.22_@better-auth+utils@0.4.2_@better-f_9dcb641568b0468b4b3f2fd37291240f/node_modules/@better-auth/passkey/dist/client.mjs
2107
+ const getPasskeyActions = ($fetch, { $listPasskeys, $store }) => {
2108
+ const signInPasskey = async (opts, options) => {
2109
+ const response = await $fetch("/passkey/generate-authenticate-options", {
2110
+ method: "GET",
2111
+ throw: false
2112
+ });
2113
+ if (!response.data) return response;
2114
+ const mergedExtensions = response.data.extensions || opts?.extensions ? {
2115
+ ...response.data.extensions || {},
2116
+ ...opts?.extensions || {}
2117
+ } : void 0;
2118
+ let res;
2119
+ try {
2120
+ res = await startAuthentication({
2121
+ optionsJSON: {
2122
+ ...response.data,
2123
+ extensions: mergedExtensions
2124
+ },
2125
+ useBrowserAutofill: opts?.autoFill
2126
+ });
2127
+ } catch (err) {
2128
+ return {
2129
+ data: null,
2130
+ error: {
2131
+ code: err instanceof WebAuthnError ? err.code : "AUTH_CANCELLED",
2132
+ message: PASSKEY_ERROR_CODES.AUTH_CANCELLED.message,
2133
+ status: 400,
2134
+ statusText: "BAD_REQUEST"
2135
+ }
2136
+ };
2137
+ }
2138
+ try {
2139
+ const { clientExtensionResults, ...responseBody } = res;
2140
+ const verified = await $fetch("/passkey/verify-authentication", {
2141
+ body: { response: responseBody },
2142
+ ...opts?.fetchOptions,
2143
+ ...options,
2144
+ method: "POST",
2145
+ throw: false
2146
+ });
2147
+ $listPasskeys.set(Math.random());
2148
+ $store.notify("$sessionSignal");
2149
+ if (opts?.returnWebAuthnResponse) return {
2150
+ ...verified,
2151
+ webauthn: {
2152
+ response: res,
2153
+ clientExtensionResults
2154
+ }
2155
+ };
2156
+ return verified;
2157
+ } catch (err) {
2158
+ console.error(`[Better Auth] Error verifying passkey`, err);
2159
+ return {
2160
+ data: null,
2161
+ error: {
2162
+ code: "AUTH_CANCELLED",
2163
+ message: PASSKEY_ERROR_CODES.AUTH_CANCELLED.message,
2164
+ status: 400,
2165
+ statusText: "BAD_REQUEST"
2166
+ }
2167
+ };
2168
+ }
2169
+ };
2170
+ const registerPasskey = async (opts, fetchOpts) => {
2171
+ const options = await $fetch("/passkey/generate-register-options", {
2172
+ method: "GET",
2173
+ query: {
2174
+ ...opts?.authenticatorAttachment && { authenticatorAttachment: opts.authenticatorAttachment },
2175
+ ...opts?.name && { name: opts.name },
2176
+ ...opts?.context && { context: opts.context }
2177
+ },
2178
+ throw: false
2179
+ });
2180
+ if (!options.data) return options;
2181
+ try {
2182
+ const mergedExtensions = options.data.extensions || opts?.extensions ? {
2183
+ ...options.data.extensions || {},
2184
+ ...opts?.extensions || {}
2185
+ } : void 0;
2186
+ const res = await startRegistration({
2187
+ optionsJSON: {
2188
+ ...options.data,
2189
+ extensions: mergedExtensions
2190
+ },
2191
+ useAutoRegister: opts?.useAutoRegister
2192
+ });
2193
+ const { clientExtensionResults, ...responseBody } = res;
2194
+ const verified = await $fetch("/passkey/verify-registration", {
2195
+ ...opts?.fetchOptions,
2196
+ ...fetchOpts,
2197
+ body: {
2198
+ response: responseBody,
2199
+ name: opts?.name
2200
+ },
2201
+ method: "POST",
2202
+ throw: false
2203
+ });
2204
+ if (!verified.data) return verified;
2205
+ $listPasskeys.set(Math.random());
2206
+ if (opts?.returnWebAuthnResponse) return {
2207
+ ...verified,
2208
+ webauthn: {
2209
+ response: res,
2210
+ clientExtensionResults
2211
+ }
2212
+ };
2213
+ return verified;
2214
+ } catch (e) {
2215
+ if (e instanceof WebAuthnError) {
2216
+ if (e.code === "ERROR_AUTHENTICATOR_PREVIOUSLY_REGISTERED") return {
2217
+ data: null,
2218
+ error: {
2219
+ code: e.code,
2220
+ message: PASSKEY_ERROR_CODES.PREVIOUSLY_REGISTERED.message,
2221
+ status: 400,
2222
+ statusText: "BAD_REQUEST"
2223
+ }
2224
+ };
2225
+ if (e.code === "ERROR_CEREMONY_ABORTED") return {
2226
+ data: null,
2227
+ error: {
2228
+ code: e.code,
2229
+ message: PASSKEY_ERROR_CODES.REGISTRATION_CANCELLED.message,
2230
+ status: 400,
2231
+ statusText: "BAD_REQUEST"
2232
+ }
2233
+ };
2234
+ return {
2235
+ data: null,
2236
+ error: {
2237
+ code: e.code,
2238
+ message: e.message,
2239
+ status: 400,
2240
+ statusText: "BAD_REQUEST"
2241
+ }
2242
+ };
2243
+ }
2244
+ return {
2245
+ data: null,
2246
+ error: {
2247
+ code: "UNKNOWN_ERROR",
2248
+ message: e instanceof Error ? e.message : PASSKEY_ERROR_CODES.UNKNOWN_ERROR.message,
2249
+ status: 500,
2250
+ statusText: "INTERNAL_SERVER_ERROR"
2251
+ }
2252
+ };
2253
+ }
2254
+ };
2255
+ return {
2256
+ signIn: { passkey: signInPasskey },
2257
+ passkey: { addPasskey: registerPasskey },
2258
+ $Infer: {}
2259
+ };
2260
+ };
2261
+ const passkeyClient = () => {
2262
+ const $listPasskeys = atom();
2263
+ return {
2264
+ id: "passkey",
2265
+ version: PACKAGE_VERSION,
2266
+ $InferServerPlugin: {},
2267
+ getActions: ($fetch, $store) => getPasskeyActions($fetch, {
2268
+ $listPasskeys,
2269
+ $store
2270
+ }),
2271
+ getAtoms($fetch) {
2272
+ return {
2273
+ listPasskeys: useAuthQuery($listPasskeys, "/passkey/list-user-passkeys", $fetch, { method: "GET" }),
2274
+ $listPasskeys
2275
+ };
2276
+ },
2277
+ pathMethods: {
2278
+ "/passkey/register": "POST",
2279
+ "/passkey/authenticate": "POST"
2280
+ },
2281
+ atomListeners: [{
2282
+ matcher(path) {
2283
+ return path === "/passkey/verify-registration" || path === "/passkey/delete-passkey" || path === "/passkey/update-passkey" || path === "/sign-out";
2284
+ },
2285
+ signal: "$listPasskeys"
2286
+ }, {
2287
+ matcher: (path) => path === "/passkey/verify-authentication",
2288
+ signal: "$sessionSignal"
2289
+ }],
2290
+ $ERROR_CODES: PASSKEY_ERROR_CODES
2291
+ };
2292
+ };
2293
+ //#endregion
2294
+ //#region ../../node_modules/.pnpm/@chiballc+utils@2.5.1_@hono+zod-openapi@1.5.1_hono@4.13.0_zod@4.4.3__hono@4.13.0_magic-_dfcdb59639d9fe73412dad0223379a88/node_modules/@chiballc/utils/dist/index.mjs
1639
2295
  function isPromise(arg) {
1640
2296
  return arg && typeof arg.then === "function";
1641
2297
  }
@@ -1895,6 +2551,9 @@ const messages = {
1895
2551
  auth_request_rate_limited: "Authentication requests are temporarily limited.",
1896
2552
  state_conflict: "The authentication state conflicts with the request.",
1897
2553
  access_denied: "Access is denied.",
2554
+ platform_target_unavailable: "The platform administration target is unavailable.",
2555
+ platform_quorum_unavailable: "Platform administration quorum is unavailable.",
2556
+ platform_recovery_required: "Platform administration requires separate recovery.",
1898
2557
  identity_authority_unavailable: "Authentication is temporarily unavailable."
1899
2558
  };
1900
2559
  var AuthError = class extends Error {
@@ -2639,6 +3298,90 @@ function fromOrganizationMutationAuthorityError(value, status, retryAfter) {
2639
3298
  if (status === 403 && code === "REQUEST_ORIGIN_DENIED") return new AuthError("request_origin_denied", 403);
2640
3299
  return fromOrganizationReadAuthorityError(value, status);
2641
3300
  }
3301
+ function normalizePlatformRoleChangeRecentAuthenticationInput(value) {
3302
+ if (!isObject(value) || Object.keys(value).sort().join(",") !== "changeReference,expectedGlobalRevision,expectedTargetRevision,idempotencyKey,reasonCategory,requestedRoles,targetUserId") throw new TypeError("recent authentication input is invalid");
3303
+ if (!isId(value.targetUserId)) throw new TypeError("targetUserId is invalid");
3304
+ if (!Array.isArray(value.requestedRoles) || value.requestedRoles.length < 1 || value.requestedRoles.length > 2 || value.requestedRoles.some((role) => role !== "administrator" && role !== "investigator") || new Set(value.requestedRoles).size !== value.requestedRoles.length) throw new TypeError("requestedRoles is invalid");
3305
+ for (const field of ["expectedTargetRevision", "expectedGlobalRevision"]) if (!Number.isSafeInteger(value[field]) || value[field] < 0 || value[field] >= Number.MAX_SAFE_INTEGER) throw new TypeError(`${field} is invalid`);
3306
+ if (!(typeof value.reasonCategory === "string" && [
3307
+ "initial_bootstrap",
3308
+ "personnel_change",
3309
+ "privilege_review",
3310
+ "quorum_recovery",
3311
+ "security_response"
3312
+ ].includes(value.reasonCategory))) throw new TypeError("reasonCategory is invalid");
3313
+ for (const field of ["changeReference", "idempotencyKey"]) if (typeof value[field] !== "string" || !/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/.test(value[field])) throw new TypeError(`${field} is invalid`);
3314
+ return {
3315
+ action: "platform_role_change",
3316
+ targetUserId: value.targetUserId,
3317
+ requestedRoles: [...value.requestedRoles].sort(),
3318
+ expectedTargetRevision: value.expectedTargetRevision,
3319
+ expectedGlobalRevision: value.expectedGlobalRevision,
3320
+ reasonCategory: value.reasonCategory,
3321
+ changeReference: value.changeReference,
3322
+ idempotencyKey: value.idempotencyKey
3323
+ };
3324
+ }
3325
+ function normalizePlatformBootstrapRoleChangeInput(value) {
3326
+ const request = normalizePlatformRoleChangeRecentAuthenticationInput(value);
3327
+ if (request.reasonCategory !== "initial_bootstrap" || !request.requestedRoles.includes("administrator")) throw new TypeError("platform first approval input is invalid");
3328
+ return request;
3329
+ }
3330
+ function normalizePlatformRoleApproval(value) {
3331
+ exact(value, [
3332
+ "status",
3333
+ "targetUserId",
3334
+ "targetRevision",
3335
+ "globalRevision",
3336
+ "roles"
3337
+ ]);
3338
+ if (value.status !== "awaiting_second_approval" && value.status !== "applied" || !isId(value.targetUserId)) throw authorityUnavailable();
3339
+ if (!Number.isSafeInteger(value.targetRevision) || value.targetRevision < 0) throw authorityUnavailable();
3340
+ if (!Number.isSafeInteger(value.globalRevision) || value.globalRevision < 0) throw authorityUnavailable();
3341
+ if (!Array.isArray(value.roles) || value.status === "awaiting_second_approval" && value.roles.length !== 0 || value.status === "applied" && !(value.roles.length === 1 && value.roles[0] === "administrator" || value.roles.length === 2 && value.roles[0] === "administrator" && value.roles[1] === "investigator")) throw authorityUnavailable();
3342
+ if (value.status === "applied") return {
3343
+ status: "applied",
3344
+ targetUserId: value.targetUserId,
3345
+ targetRevision: value.targetRevision,
3346
+ globalRevision: value.globalRevision,
3347
+ roles: value.roles
3348
+ };
3349
+ return {
3350
+ status: "awaiting_second_approval",
3351
+ targetUserId: value.targetUserId,
3352
+ targetRevision: value.targetRevision,
3353
+ globalRevision: value.globalRevision,
3354
+ roles: []
3355
+ };
3356
+ }
3357
+ function normalizeRecentAuthenticationStatus(value) {
3358
+ exact(value, ["status"]);
3359
+ if (value.status !== "pending" && value.status !== "ready") throw authorityUnavailable();
3360
+ return { status: value.status };
3361
+ }
3362
+ function fromRecentAuthenticationError(value, status, retryAfter) {
3363
+ if (status === 400 || status === 413 || status === 415) return new AuthError("invalid_request", 400);
3364
+ if (status === 401) return new AuthError("authentication_required", 401);
3365
+ if (status === 403) return new AuthError("request_origin_denied", 403);
3366
+ if (status === 409) return new AuthError("state_conflict", 409);
3367
+ if (status === 429) return new AuthError("auth_request_rate_limited", 429, parseRetryAfter(retryAfter));
3368
+ return authorityUnavailable();
3369
+ }
3370
+ function fromPlatformAdministrationError(value, status, retryAfter) {
3371
+ const code = isObject(value) && typeof value.code === "string" ? value.code : void 0;
3372
+ if (status === 400 && code === "BAD_REQUEST") return new AuthError("invalid_request", 400);
3373
+ if (status === 401 && code === "UNAUTHORIZED") return new AuthError("authentication_required", 401);
3374
+ if (status === 403 && code === "FORBIDDEN") return new AuthError("access_denied", 403);
3375
+ if (status === 404 && code === "NOT_FOUND") return new AuthError("platform_target_unavailable", 404);
3376
+ if (status === 409 && code === "CONFLICT") return new AuthError("state_conflict", 409);
3377
+ if (status === 409 && code === "QUORUM_UNAVAILABLE") return new AuthError("platform_quorum_unavailable", 409);
3378
+ if (status === 413 && code === "REQUEST_TOO_LONG") return new AuthError("invalid_request", 413);
3379
+ if (status === 415 && code === "UNSUPPORTED_MEDIA_TYPE") return new AuthError("invalid_request", 415);
3380
+ if (status === 422 && code === "RECOVERY_REQUIRED") return new AuthError("platform_recovery_required", 422);
3381
+ if (status === 429 && code === "RATE_LIMITED") return new AuthError("auth_request_rate_limited", 429, parseRetryAfter(retryAfter));
3382
+ if (status === 503 && code === "SERVICE_UNAVAILABLE") return authorityUnavailable();
3383
+ return authorityUnavailable();
3384
+ }
2642
3385
  function fromInvitationAuthorityError(value, status) {
2643
3386
  const code = isObject(value) && typeof value.code === "string" ? value.code : void 0;
2644
3387
  if (status === 401 && code === "UNAUTHORIZED") return new AuthError("authentication_required", 401);
@@ -3580,6 +4323,8 @@ function byteLength(value) {
3580
4323
  //#endregion
3581
4324
  //#region src/client.ts
3582
4325
  const REFRESH_WINDOW_MS = 120 * 1e3;
4326
+ const RECENT_AUTHENTICATION_BROWSER_HEADER = "x-finueva-recent-auth-browser";
4327
+ const RECENT_AUTHENTICATION_REQUEST_HEADER = "x-finueva-recent-auth-request";
3583
4328
  function createAuthClient(options) {
3584
4329
  const authOrigin = normalizeOrigin(options.authOrigin, "authOrigin");
3585
4330
  const appOrigin = normalizeOrigin(options.appOrigin, "appOrigin");
@@ -3596,7 +4341,8 @@ function createAuthClient(options) {
3596
4341
  credentials: "include",
3597
4342
  retry: 0,
3598
4343
  timeout: timeoutMs
3599
- }
4344
+ },
4345
+ plugins: [passkeyClient()]
3600
4346
  });
3601
4347
  const persistence = createSessionPersistence(options.persistence, authOrigin, now, timeoutMs);
3602
4348
  const authStateListeners = new Set();
@@ -3608,6 +4354,9 @@ function createAuthClient(options) {
3608
4354
  let selectedWorkspaceKey;
3609
4355
  let credentialGeneration = 0;
3610
4356
  let authorityGeneration = 0;
4357
+ let recentAuthenticationBrowserToken;
4358
+ let recentAuthenticationStartAttempt;
4359
+ let recentAuthenticationStartRequest;
3611
4360
  let sessionReadEpoch = 0;
3612
4361
  let activeAccountMutationGeneration;
3613
4362
  let invitationAbort;
@@ -3621,6 +4370,10 @@ function createAuthClient(options) {
3621
4370
  let signalRefreshPending = false;
3622
4371
  let signalRefreshScheduled = false;
3623
4372
  let signalRefreshRequest;
4373
+ function advanceAuthorityGeneration() {
4374
+ recentAuthenticationStartAttempt = void 0;
4375
+ return ++authorityGeneration;
4376
+ }
3624
4377
  function setAuthState(state) {
3625
4378
  authState = state;
3626
4379
  for (const listener of authStateListeners) try {
@@ -3726,7 +4479,9 @@ function createAuthClient(options) {
3726
4479
  }
3727
4480
  function updateSession(session) {
3728
4481
  const unboundCredential = !sessionId && (credential || credentialRequests.size > 0);
4482
+ const changedSession = !session || sessionId !== void 0 && (session.id !== sessionId || session.user.id !== sessionUserId);
3729
4483
  if (!session || unboundCredential || sessionId && (session.id !== sessionId || session.user.id !== sessionUserId)) clearCredential();
4484
+ if (changedSession || recentAuthenticationStartAttempt && (recentAuthenticationStartAttempt.sessionId !== session?.id || recentAuthenticationStartAttempt.userId !== session?.user.id)) recentAuthenticationStartAttempt = void 0;
3730
4485
  sessionId = session?.id;
3731
4486
  sessionUserId = session?.user.id;
3732
4487
  return session;
@@ -3755,10 +4510,10 @@ function createAuthClient(options) {
3755
4510
  }
3756
4511
  function signOut() {
3757
4512
  if (signOutRequest) return signOutRequest;
3758
- if (activeAccountMutationGeneration !== void 0) return Promise.reject(new AuthError("state_conflict", 409));
4513
+ if (activeAccountMutationGeneration !== void 0 || recentAuthenticationStartRequest) return Promise.reject(new AuthError("state_conflict", 409));
3759
4514
  const pendingInvitation = invitationRequest;
3760
4515
  invitationAbort?.abort();
3761
- const generation = ++authorityGeneration;
4516
+ const generation = advanceAuthorityGeneration();
3762
4517
  sessionReadEpoch += 1;
3763
4518
  clearCredential();
3764
4519
  sessionId = void 0;
@@ -3803,10 +4558,10 @@ function createAuthClient(options) {
3803
4558
  return request;
3804
4559
  }
3805
4560
  async function runAccountMutation(operation) {
3806
- if (signOutRequest || activeAccountMutationGeneration !== void 0) throw new AuthError("state_conflict", 409);
4561
+ if (signOutRequest || activeAccountMutationGeneration !== void 0 || recentAuthenticationStartRequest) throw new AuthError("state_conflict", 409);
3807
4562
  const pendingInvitation = invitationRequest;
3808
4563
  invitationAbort?.abort();
3809
- const generation = ++authorityGeneration;
4564
+ const generation = advanceAuthorityGeneration();
3810
4565
  sessionReadEpoch += 1;
3811
4566
  activeAccountMutationGeneration = generation;
3812
4567
  clearCredential();
@@ -3873,8 +4628,101 @@ function createAuthClient(options) {
3873
4628
  throw error;
3874
4629
  }
3875
4630
  }
4631
+ function assertRecentAuthenticationOrigin() {
4632
+ if (appOrigin !== authOrigin) throw new TypeError("recent authentication requires the exact Auth Origin");
4633
+ }
3876
4634
  return {
3877
4635
  _type: "browser",
4636
+ async startPlatformRoleChangeRecentAuthentication(input) {
4637
+ assertRecentAuthenticationOrigin();
4638
+ const body = normalizePlatformRoleChangeRecentAuthenticationInput(input);
4639
+ if (signOutRequest || activeAccountMutationGeneration !== void 0 || invitationRequest) throw new AuthError("state_conflict", 409);
4640
+ if (recentAuthenticationStartRequest) throw new AuthError("state_conflict", 409);
4641
+ const generation = authorityGeneration;
4642
+ recentAuthenticationBrowserToken ??= createOpaqueToken();
4643
+ const normalizedInput = JSON.stringify(body);
4644
+ const attempt = recentAuthenticationStartAttempt?.input === normalizedInput ? recentAuthenticationStartAttempt : {
4645
+ input: normalizedInput,
4646
+ requestToken: createOpaqueToken(),
4647
+ sessionId,
4648
+ userId: sessionUserId
4649
+ };
4650
+ recentAuthenticationStartAttempt = attempt;
4651
+ const request = (async () => {
4652
+ try {
4653
+ const value = await requestJson(joinURL(authOrigin, "api/v1/auth/recent-authentication"), {
4654
+ fetch: fetchImpl,
4655
+ timeoutMs,
4656
+ method: "POST",
4657
+ headers: {
4658
+ "content-type": "application/json",
4659
+ [RECENT_AUTHENTICATION_BROWSER_HEADER]: recentAuthenticationBrowserToken,
4660
+ [RECENT_AUTHENTICATION_REQUEST_HEADER]: attempt.requestToken
4661
+ },
4662
+ body,
4663
+ mapError: fromRecentAuthenticationError
4664
+ });
4665
+ if (generation !== authorityGeneration) throw new AuthError("state_conflict", 409);
4666
+ const status = normalizeRecentAuthenticationStatus(value);
4667
+ if (recentAuthenticationStartAttempt === attempt) recentAuthenticationStartAttempt = void 0;
4668
+ return status;
4669
+ } catch (error) {
4670
+ if (recentAuthenticationStartAttempt === attempt && (!(error instanceof AuthError) || error.code !== "identity_authority_unavailable")) recentAuthenticationStartAttempt = void 0;
4671
+ throw error;
4672
+ }
4673
+ })();
4674
+ recentAuthenticationStartRequest = request;
4675
+ try {
4676
+ return await request;
4677
+ } finally {
4678
+ if (recentAuthenticationStartRequest === request) recentAuthenticationStartRequest = void 0;
4679
+ }
4680
+ },
4681
+ async approveBootstrapPlatformRoleChange(input) {
4682
+ assertRecentAuthenticationOrigin();
4683
+ if (signOutRequest || activeAccountMutationGeneration !== void 0 || invitationRequest) throw new AuthError("state_conflict", 409);
4684
+ const body = normalizePlatformBootstrapRoleChangeInput(input);
4685
+ const generation = authorityGeneration;
4686
+ const value = await requestJson(joinURL(authOrigin, "api/v1/auth/platform/role-approvals"), {
4687
+ fetch: fetchImpl,
4688
+ timeoutMs,
4689
+ method: "POST",
4690
+ headers: { "content-type": "application/json" },
4691
+ body,
4692
+ mapError: fromPlatformAdministrationError
4693
+ });
4694
+ if (generation !== authorityGeneration) throw new AuthError("state_conflict", 409);
4695
+ return normalizePlatformRoleApproval(value);
4696
+ },
4697
+ async verifyRecentAuthenticationWithPasskey() {
4698
+ assertRecentAuthenticationOrigin();
4699
+ if (!(await runAccountMutation(async () => {
4700
+ const verification = await betterAuth.signIn.passkey({ autoFill: false });
4701
+ if (verification.error) throw fromRecentAuthenticationError(void 0, verification.error.status);
4702
+ })).session) throw new AuthError("authentication_required", 401);
4703
+ },
4704
+ async addPasskey() {
4705
+ assertRecentAuthenticationOrigin();
4706
+ if (!(await runAccountMutation(async () => {
4707
+ const registration = await betterAuth.passkey.addPasskey({ context: createOpaqueToken() });
4708
+ if (registration.error) throw fromAccountAuthorityError(registration.error, registration.error.status);
4709
+ })).session) throw new AuthError("authentication_required", 401);
4710
+ },
4711
+ async getRecentAuthenticationStatus() {
4712
+ assertRecentAuthenticationOrigin();
4713
+ if (signOutRequest || activeAccountMutationGeneration !== void 0 || invitationRequest) throw new AuthError("state_conflict", 409);
4714
+ const generation = authorityGeneration;
4715
+ const value = await requestJson(joinURL(authOrigin, "api/v1/auth/recent-authentication/status"), {
4716
+ fetch: fetchImpl,
4717
+ timeoutMs,
4718
+ method: "POST",
4719
+ headers: { "content-type": "application/json" },
4720
+ body: {},
4721
+ mapError: fromRecentAuthenticationError
4722
+ });
4723
+ if (generation !== authorityGeneration) throw new AuthError("state_conflict", 409);
4724
+ return normalizeRecentAuthenticationStatus(value);
4725
+ },
3878
4726
  acceptInvitation(input) {
3879
4727
  let invitation;
3880
4728
  try {
@@ -3882,8 +4730,8 @@ function createAuthClient(options) {
3882
4730
  } catch (error) {
3883
4731
  return Promise.reject(error);
3884
4732
  }
3885
- if (signOutRequest || activeAccountMutationGeneration !== void 0 || invitationRequest) return Promise.reject(new AuthError("state_conflict", 409));
3886
- const generation = ++authorityGeneration;
4733
+ if (signOutRequest || activeAccountMutationGeneration !== void 0 || invitationRequest || recentAuthenticationStartRequest) return Promise.reject(new AuthError("state_conflict", 409));
4734
+ const generation = advanceAuthorityGeneration();
3887
4735
  const controller = new AbortController();
3888
4736
  clearCredential();
3889
4737
  const request = (async () => {
@@ -4138,5 +4986,12 @@ function createAuthClient(options) {
4138
4986
  }
4139
4987
  };
4140
4988
  }
4989
+ function createOpaqueToken() {
4990
+ const bytes = new Uint8Array(32);
4991
+ globalThis.crypto.getRandomValues(bytes);
4992
+ let binary = "";
4993
+ for (const byte of bytes) binary += String.fromCharCode(byte);
4994
+ return globalThis.btoa(binary).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/, "");
4995
+ }
4141
4996
  //#endregion
4142
4997
  export { AuthError, createAuthClient };
package/dist/server.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { A as ReceivedOrganizationInvitationPage, B as AuthErrorCode, C as OrganizationTeam, D as OrganizationWorkspaceSelection, E as OrganizationTeamPage, F as UpdateOrganizationDisplayInput, I as UpdateOrganizationTeamDisplayInput, N as SocialIdTokenInput, O as PersonalWorkspaceSelection, P as SocialIdTokenProvider, R as WorkspaceSelection, S as OrganizationPageInput, T as OrganizationTeamMemberPage, V as AuthErrorStatus, _ as OrganizationInvitation, b as OrganizationMemberPage, c as AuthUser, d as CreatedOrganizationTeam, f as DriveCredential, g as OrganizationDisplay, h as InvitationAcceptance, k as ReceivedOrganizationInvitation, l as CreateOrganizationMemberInvitationInput, o as AuthSession, p as DriveCredentialInput, r as AuthOrganization, t as AcceptInvitationInput, u as CreatedOrganizationMemberInvitation, v as OrganizationInvitationPage, w as OrganizationTeamDisplay, x as OrganizationPage, y as OrganizationMember, z as AuthError } from "./types-Byw9JQwL.js";
1
+ import { B as SocialIdTokenInput, C as OrganizationTeam, D as OrganizationWorkspaceSelection, E as OrganizationTeamPage, F as ReceivedOrganizationInvitation, G as WorkspaceSelection, H as UpdateOrganizationDisplayInput, I as ReceivedOrganizationInvitationPage, J as AuthErrorStatus, K as AuthError, O as PersonalWorkspaceSelection, S as OrganizationPageInput, T as OrganizationTeamMemberPage, U as UpdateOrganizationTeamDisplayInput, V as SocialIdTokenProvider, _ as OrganizationInvitation, b as OrganizationMemberPage, c as AuthUser, d as CreatedOrganizationTeam, f as DriveCredential, g as OrganizationDisplay, h as InvitationAcceptance, l as CreateOrganizationMemberInvitationInput, o as AuthSession, p as DriveCredentialInput, q as AuthErrorCode, r as AuthOrganization, t as AcceptInvitationInput, u as CreatedOrganizationMemberInvitation, v as OrganizationInvitationPage, w as OrganizationTeamDisplay, x as OrganizationPage, y as OrganizationMember } from "./types-BqpgLkQB.js";
2
2
  //#region src/server.d.ts
3
3
  interface ServerAuthOptions {
4
4
  authOrigin: string;
package/dist/server.js CHANGED
@@ -192,6 +192,9 @@ const messages = {
192
192
  auth_request_rate_limited: "Authentication requests are temporarily limited.",
193
193
  state_conflict: "The authentication state conflicts with the request.",
194
194
  access_denied: "Access is denied.",
195
+ platform_target_unavailable: "The platform administration target is unavailable.",
196
+ platform_quorum_unavailable: "Platform administration quorum is unavailable.",
197
+ platform_recovery_required: "Platform administration requires separate recovery.",
195
198
  identity_authority_unavailable: "Authentication is temporarily unavailable."
196
199
  };
197
200
  var AuthError = class extends Error {
@@ -1,6 +1,6 @@
1
1
  //#region src/errors.d.ts
2
- type AuthErrorCode = "invalid_credentials" | "invalid_auth_input" | "account_conflict" | "password_reset_invalid" | "authentication_required" | "request_origin_denied" | "invalid_request" | "invalid_invitation" | "invitation_unavailable" | "invitation_recipient_mismatch" | "email_verification_required" | "invalid_workspace_selection" | "organization_context_too_large" | "credential_rate_limited" | "auth_request_rate_limited" | "state_conflict" | "access_denied" | "identity_authority_unavailable";
3
- type AuthErrorStatus = 400 | 401 | 403 | 409 | 422 | 429 | 503;
2
+ type AuthErrorCode = "invalid_credentials" | "invalid_auth_input" | "account_conflict" | "password_reset_invalid" | "authentication_required" | "request_origin_denied" | "invalid_request" | "invalid_invitation" | "invitation_unavailable" | "invitation_recipient_mismatch" | "email_verification_required" | "invalid_workspace_selection" | "organization_context_too_large" | "credential_rate_limited" | "auth_request_rate_limited" | "state_conflict" | "access_denied" | "platform_target_unavailable" | "platform_quorum_unavailable" | "platform_recovery_required" | "identity_authority_unavailable";
3
+ type AuthErrorStatus = 400 | 401 | 403 | 404 | 409 | 413 | 415 | 422 | 429 | 503;
4
4
  declare class AuthError extends Error {
5
5
  readonly code: AuthErrorCode;
6
6
  readonly status: AuthErrorStatus;
@@ -193,6 +193,38 @@ interface DriveCredentialInput {
193
193
  workspace: WorkspaceSelection;
194
194
  forceRefresh?: boolean;
195
195
  }
196
+ type PlatformRole = "administrator" | "investigator";
197
+ interface PlatformRoleChangeRecentAuthenticationInput {
198
+ targetUserId: string;
199
+ requestedRoles: PlatformRole[];
200
+ expectedTargetRevision: number;
201
+ expectedGlobalRevision: number;
202
+ reasonCategory: "initial_bootstrap" | "personnel_change" | "privilege_review" | "quorum_recovery" | "security_response";
203
+ changeReference: string;
204
+ idempotencyKey: string;
205
+ }
206
+ interface PlatformBootstrapRoleChangeInput extends Omit<PlatformRoleChangeRecentAuthenticationInput, "reasonCategory" | "requestedRoles"> {
207
+ requestedRoles: ["administrator"] | ["administrator", "investigator"] | ["investigator", "administrator"];
208
+ reasonCategory: "initial_bootstrap";
209
+ }
210
+ interface PlatformRoleFirstApproval {
211
+ status: "awaiting_second_approval";
212
+ targetUserId: string;
213
+ targetRevision: number;
214
+ globalRevision: number;
215
+ roles: [];
216
+ }
217
+ interface PlatformRoleApplied {
218
+ status: "applied";
219
+ targetUserId: string;
220
+ targetRevision: number;
221
+ globalRevision: number;
222
+ roles: PlatformRole[];
223
+ }
224
+ type PlatformRoleApproval = PlatformRoleFirstApproval | PlatformRoleApplied;
225
+ interface RecentAuthenticationStatus {
226
+ status: "pending" | "ready";
227
+ }
196
228
  interface AuthClientOptions {
197
229
  authOrigin: string;
198
230
  appOrigin: string;
@@ -203,6 +235,11 @@ interface AuthClientOptions {
203
235
  }
204
236
  interface FinuevaAuthClient {
205
237
  readonly _type: "browser";
238
+ startPlatformRoleChangeRecentAuthentication(input: PlatformRoleChangeRecentAuthenticationInput): Promise<RecentAuthenticationStatus>;
239
+ approveBootstrapPlatformRoleChange(input: PlatformBootstrapRoleChangeInput): Promise<PlatformRoleApproval>;
240
+ addPasskey(): Promise<void>;
241
+ verifyRecentAuthenticationWithPasskey(): Promise<void>;
242
+ getRecentAuthenticationStatus(): Promise<RecentAuthenticationStatus>;
206
243
  acceptInvitation(input: AcceptInvitationInput): Promise<InvitationAcceptance>;
207
244
  getSession(): Promise<AuthSession | null>;
208
245
  listOrganizations(input?: OrganizationPageInput): Promise<OrganizationPage>;
@@ -232,4 +269,4 @@ interface FinuevaAuthClient {
232
269
  onSessionChange(listener: (session: AuthSession | null) => void): () => void;
233
270
  }
234
271
  //#endregion
235
- export { ReceivedOrganizationInvitationPage as A, AuthErrorCode as B, OrganizationTeam as C, OrganizationWorkspaceSelection as D, OrganizationTeamPage as E, UpdateOrganizationDisplayInput as F, UpdateOrganizationTeamDisplayInput as I, UpdateProfileInput as L, SignInInput as M, SocialIdTokenInput as N, PersonalWorkspaceSelection as O, SocialIdTokenProvider as P, WorkspaceSelection as R, OrganizationPageInput as S, OrganizationTeamMemberPage as T, AuthErrorStatus as V, OrganizationInvitation as _, AuthPersistenceDriver as a, OrganizationMemberPage as b, AuthUser as c, CreatedOrganizationTeam as d, DriveCredential as f, OrganizationDisplay as g, InvitationAcceptance as h, AuthPersistence as i, SendVerificationEmailInput as j, ReceivedOrganizationInvitation as k, CreateOrganizationMemberInvitationInput as l, FinuevaAuthClient as m, AuthClientOptions as n, AuthSession as o, DriveCredentialInput as p, AuthOrganization as r, AuthState as s, AcceptInvitationInput as t, CreatedOrganizationMemberInvitation as u, OrganizationInvitationPage as v, OrganizationTeamDisplay as w, OrganizationPage as x, OrganizationMember as y, AuthError as z };
272
+ export { PlatformRole as A, SocialIdTokenInput as B, OrganizationTeam as C, OrganizationWorkspaceSelection as D, OrganizationTeamPage as E, ReceivedOrganizationInvitation as F, WorkspaceSelection as G, UpdateOrganizationDisplayInput as H, ReceivedOrganizationInvitationPage as I, AuthErrorStatus as J, AuthError as K, RecentAuthenticationStatus as L, PlatformRoleApproval as M, PlatformRoleChangeRecentAuthenticationInput as N, PersonalWorkspaceSelection as O, PlatformRoleFirstApproval as P, SendVerificationEmailInput as R, OrganizationPageInput as S, OrganizationTeamMemberPage as T, UpdateOrganizationTeamDisplayInput as U, SocialIdTokenProvider as V, UpdateProfileInput as W, OrganizationInvitation as _, AuthPersistenceDriver as a, OrganizationMemberPage as b, AuthUser as c, CreatedOrganizationTeam as d, DriveCredential as f, OrganizationDisplay as g, InvitationAcceptance as h, AuthPersistence as i, PlatformRoleApplied as j, PlatformBootstrapRoleChangeInput as k, CreateOrganizationMemberInvitationInput as l, FinuevaAuthClient as m, AuthClientOptions as n, AuthSession as o, DriveCredentialInput as p, AuthErrorCode as q, AuthOrganization as r, AuthState as s, AcceptInvitationInput as t, CreatedOrganizationMemberInvitation as u, OrganizationInvitationPage as v, OrganizationTeamDisplay as w, OrganizationPage as x, OrganizationMember as y, SignInInput as z };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@finueva/auth",
3
- "version": "0.10.1",
3
+ "version": "0.11.0",
4
4
  "description": "Public browser and server SDK for Finueva Auth",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -49,7 +49,9 @@
49
49
  "typecheck": "tsc --noEmit"
50
50
  },
51
51
  "devDependencies": {
52
- "@chiballc/utils": "2.5.0",
52
+ "@better-auth/core": "1.6.22",
53
+ "@better-auth/passkey": "1.6.22",
54
+ "@chiballc/utils": "2.5.1",
53
55
  "@types/node": "24.13.3",
54
56
  "@typescript/native": "npm:typescript@7.0.2",
55
57
  "@vitest/coverage-v8": "4.1.10",