@finueva/auth 0.10.0 → 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 +153 -385
- package/THIRD_PARTY_NOTICES.md +26 -0
- package/dist/device.d.ts +1 -1
- package/dist/device.js +3 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +863 -8
- package/dist/server.d.ts +1 -1
- package/dist/server.js +3 -0
- package/dist/{types-Byw9JQwL.d.ts → types-BqpgLkQB.d.ts} +40 -3
- package/package.json +4 -2
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/@
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 };
|