fix price and modular

This commit is contained in:
2026-02-01 17:38:39 +03:30
parent 55f34aa15d
commit 164542d3e2
801 changed files with 16129 additions and 14478 deletions
+2 -4
View File
@@ -1,9 +1,7 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const GoTrueAdminApi_1 = __importDefault(require("./GoTrueAdminApi"));
const tslib_1 = require("tslib");
const GoTrueAdminApi_1 = tslib_1.__importDefault(require("./GoTrueAdminApi"));
const AuthAdminApi = GoTrueAdminApi_1.default;
exports.default = AuthAdminApi;
//# sourceMappingURL=AuthAdminApi.js.map
+2 -4
View File
@@ -1,9 +1,7 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const GoTrueClient_1 = __importDefault(require("./GoTrueClient"));
const tslib_1 = require("tslib");
const GoTrueClient_1 = tslib_1.__importDefault(require("./GoTrueClient"));
const AuthClient = GoTrueClient_1.default;
exports.default = AuthClient;
//# sourceMappingURL=AuthClient.js.map
+176 -13
View File
@@ -1,21 +1,24 @@
"use strict";
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const fetch_1 = require("./lib/fetch");
const helpers_1 = require("./lib/helpers");
const types_1 = require("./lib/types");
const errors_1 = require("./lib/errors");
class GoTrueAdminApi {
/**
* Creates an admin API client that can be used to manage users and OAuth clients.
*
* @example
* ```ts
* import { GoTrueAdminApi } from '@supabase/auth-js'
*
* const admin = new GoTrueAdminApi({
* url: 'https://xyzcompany.supabase.co/auth/v1',
* headers: { Authorization: `Bearer ${process.env.SUPABASE_SERVICE_ROLE_KEY}` },
* })
* ```
*/
constructor({ url = '', headers = {}, fetch, }) {
this.url = url;
this.headers = headers;
@@ -24,6 +27,14 @@ class GoTrueAdminApi {
listFactors: this._listFactors.bind(this),
deleteFactor: this._deleteFactor.bind(this),
};
this.oauth = {
listClients: this._listOAuthClients.bind(this),
createClient: this._createOAuthClient.bind(this),
getClient: this._getOAuthClient.bind(this),
updateClient: this._updateOAuthClient.bind(this),
deleteClient: this._deleteOAuthClient.bind(this),
regenerateClientSecret: this._regenerateOAuthClientSecret.bind(this),
};
}
/**
* Removes a logged-in session.
@@ -79,7 +90,7 @@ class GoTrueAdminApi {
*/
async generateLink(params) {
try {
const { options } = params, rest = __rest(params, ["options"]);
const { options } = params, rest = tslib_1.__rest(params, ["options"]);
const body = Object.assign(Object.assign({}, rest), options);
if ('newEmail' in rest) {
// replace newEmail with new_email in request body
@@ -190,7 +201,7 @@ class GoTrueAdminApi {
}
}
/**
* Updates the user data.
* Updates the user data. Changes are applied directly without confirmation flows.
*
* @param attributes The data you want to update.
*
@@ -273,6 +284,158 @@ class GoTrueAdminApi {
throw error;
}
}
/**
* Lists all OAuth clients with optional pagination.
* Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
*
* This function should only be called on a server. Never expose your `service_role` key in the browser.
*/
async _listOAuthClients(params) {
var _a, _b, _c, _d, _e, _f, _g;
try {
const pagination = { nextPage: null, lastPage: 0, total: 0 };
const response = await (0, fetch_1._request)(this.fetch, 'GET', `${this.url}/admin/oauth/clients`, {
headers: this.headers,
noResolveJson: true,
query: {
page: (_b = (_a = params === null || params === void 0 ? void 0 : params.page) === null || _a === void 0 ? void 0 : _a.toString()) !== null && _b !== void 0 ? _b : '',
per_page: (_d = (_c = params === null || params === void 0 ? void 0 : params.perPage) === null || _c === void 0 ? void 0 : _c.toString()) !== null && _d !== void 0 ? _d : '',
},
xform: fetch_1._noResolveJsonResponse,
});
if (response.error)
throw response.error;
const clients = await response.json();
const total = (_e = response.headers.get('x-total-count')) !== null && _e !== void 0 ? _e : 0;
const links = (_g = (_f = response.headers.get('link')) === null || _f === void 0 ? void 0 : _f.split(',')) !== null && _g !== void 0 ? _g : [];
if (links.length > 0) {
links.forEach((link) => {
const page = parseInt(link.split(';')[0].split('=')[1].substring(0, 1));
const rel = JSON.parse(link.split(';')[1].split('=')[1]);
pagination[`${rel}Page`] = page;
});
pagination.total = parseInt(total);
}
return { data: Object.assign(Object.assign({}, clients), pagination), error: null };
}
catch (error) {
if ((0, errors_1.isAuthError)(error)) {
return { data: { clients: [] }, error };
}
throw error;
}
}
/**
* Creates a new OAuth client.
* Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
*
* This function should only be called on a server. Never expose your `service_role` key in the browser.
*/
async _createOAuthClient(params) {
try {
return await (0, fetch_1._request)(this.fetch, 'POST', `${this.url}/admin/oauth/clients`, {
body: params,
headers: this.headers,
xform: (client) => {
return { data: client, error: null };
},
});
}
catch (error) {
if ((0, errors_1.isAuthError)(error)) {
return { data: null, error };
}
throw error;
}
}
/**
* Gets details of a specific OAuth client.
* Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
*
* This function should only be called on a server. Never expose your `service_role` key in the browser.
*/
async _getOAuthClient(clientId) {
try {
return await (0, fetch_1._request)(this.fetch, 'GET', `${this.url}/admin/oauth/clients/${clientId}`, {
headers: this.headers,
xform: (client) => {
return { data: client, error: null };
},
});
}
catch (error) {
if ((0, errors_1.isAuthError)(error)) {
return { data: null, error };
}
throw error;
}
}
/**
* Updates an existing OAuth client.
* Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
*
* This function should only be called on a server. Never expose your `service_role` key in the browser.
*/
async _updateOAuthClient(clientId, params) {
try {
return await (0, fetch_1._request)(this.fetch, 'PUT', `${this.url}/admin/oauth/clients/${clientId}`, {
body: params,
headers: this.headers,
xform: (client) => {
return { data: client, error: null };
},
});
}
catch (error) {
if ((0, errors_1.isAuthError)(error)) {
return { data: null, error };
}
throw error;
}
}
/**
* Deletes an OAuth client.
* Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
*
* This function should only be called on a server. Never expose your `service_role` key in the browser.
*/
async _deleteOAuthClient(clientId) {
try {
await (0, fetch_1._request)(this.fetch, 'DELETE', `${this.url}/admin/oauth/clients/${clientId}`, {
headers: this.headers,
noResolveJson: true,
});
return { data: null, error: null };
}
catch (error) {
if ((0, errors_1.isAuthError)(error)) {
return { data: null, error };
}
throw error;
}
}
/**
* Regenerates the secret for an OAuth client.
* Only relevant when the OAuth 2.1 server is enabled in Supabase Auth.
*
* This function should only be called on a server. Never expose your `service_role` key in the browser.
*/
async _regenerateOAuthClientSecret(clientId) {
try {
return await (0, fetch_1._request)(this.fetch, 'POST', `${this.url}/admin/oauth/clients/${clientId}/regenerate_secret`, {
headers: this.headers,
xform: (client) => {
return { data: client, error: null };
},
});
}
catch (error) {
if ((0, errors_1.isAuthError)(error)) {
return { data: null, error };
}
throw error;
}
}
}
exports.default = GoTrueAdminApi;
//# sourceMappingURL=GoTrueAdminApi.js.map
File diff suppressed because it is too large Load Diff
+7 -23
View File
@@ -1,33 +1,17 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.processLock = exports.lockInternals = exports.NavigatorLockAcquireTimeoutError = exports.navigatorLock = exports.AuthClient = exports.AuthAdminApi = exports.GoTrueClient = exports.GoTrueAdminApi = void 0;
const GoTrueAdminApi_1 = __importDefault(require("./GoTrueAdminApi"));
const tslib_1 = require("tslib");
const GoTrueAdminApi_1 = tslib_1.__importDefault(require("./GoTrueAdminApi"));
exports.GoTrueAdminApi = GoTrueAdminApi_1.default;
const GoTrueClient_1 = __importDefault(require("./GoTrueClient"));
const GoTrueClient_1 = tslib_1.__importDefault(require("./GoTrueClient"));
exports.GoTrueClient = GoTrueClient_1.default;
const AuthAdminApi_1 = __importDefault(require("./AuthAdminApi"));
const AuthAdminApi_1 = tslib_1.__importDefault(require("./AuthAdminApi"));
exports.AuthAdminApi = AuthAdminApi_1.default;
const AuthClient_1 = __importDefault(require("./AuthClient"));
const AuthClient_1 = tslib_1.__importDefault(require("./AuthClient"));
exports.AuthClient = AuthClient_1.default;
__exportStar(require("./lib/types"), exports);
__exportStar(require("./lib/errors"), exports);
tslib_1.__exportStar(require("./lib/types"), exports);
tslib_1.__exportStar(require("./lib/errors"), exports);
var locks_1 = require("./lib/locks");
Object.defineProperty(exports, "navigatorLock", { enumerable: true, get: function () { return locks_1.navigatorLock; } });
Object.defineProperty(exports, "NavigatorLockAcquireTimeoutError", { enumerable: true, get: function () { return locks_1.NavigatorLockAcquireTimeoutError; } });
+10 -11
View File
@@ -5,7 +5,16 @@
* that repo!
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.bytesToBase64URL = exports.stringToUint8Array = exports.base64UrlToUint8Array = exports.stringFromUTF8 = exports.stringToUTF8 = exports.codepointToUTF8 = exports.stringFromBase64URL = exports.stringToBase64URL = exports.byteFromBase64URL = exports.byteToBase64URL = void 0;
exports.byteToBase64URL = byteToBase64URL;
exports.byteFromBase64URL = byteFromBase64URL;
exports.stringToBase64URL = stringToBase64URL;
exports.stringFromBase64URL = stringFromBase64URL;
exports.codepointToUTF8 = codepointToUTF8;
exports.stringToUTF8 = stringToUTF8;
exports.stringFromUTF8 = stringFromUTF8;
exports.base64UrlToUint8Array = base64UrlToUint8Array;
exports.stringToUint8Array = stringToUint8Array;
exports.bytesToBase64URL = bytesToBase64URL;
/**
* An array of characters that encode 6 bits into a Base64-URL alphabet
* character.
@@ -60,7 +69,6 @@ function byteToBase64URL(byte, state, emit) {
}
}
}
exports.byteToBase64URL = byteToBase64URL;
/**
* Converts a String char code (extracted using `string.charCodeAt(position)`) to a sequence of Base64-URL characters.
*
@@ -87,7 +95,6 @@ function byteFromBase64URL(charCode, state, emit) {
throw new Error(`Invalid Base64-URL character "${String.fromCharCode(charCode)}"`);
}
}
exports.byteFromBase64URL = byteFromBase64URL;
/**
* Converts a JavaScript string (which may include any valid character) into a
* Base64-URL encoded string. The string is first encoded in UTF-8 which is
@@ -107,7 +114,6 @@ function stringToBase64URL(str) {
byteToBase64URL(null, state, emitter);
return base64.join('');
}
exports.stringToBase64URL = stringToBase64URL;
/**
* Converts a Base64-URL encoded string into a JavaScript string. It is assumed
* that the underlying string has been encoded as UTF-8.
@@ -132,7 +138,6 @@ function stringFromBase64URL(str) {
}
return conv.join('');
}
exports.stringFromBase64URL = stringFromBase64URL;
/**
* Converts a Unicode codepoint to a multi-byte UTF-8 sequence.
*
@@ -164,7 +169,6 @@ function codepointToUTF8(codepoint, emit) {
}
throw new Error(`Unrecognized Unicode codepoint: ${codepoint.toString(16)}`);
}
exports.codepointToUTF8 = codepointToUTF8;
/**
* Converts a JavaScript string to a sequence of UTF-8 bytes.
*
@@ -186,7 +190,6 @@ function stringToUTF8(str, emit) {
codepointToUTF8(codepoint, emit);
}
}
exports.stringToUTF8 = stringToUTF8;
/**
* Converts a UTF-8 byte to a Unicode codepoint.
*
@@ -233,7 +236,6 @@ function stringFromUTF8(byte, state, emit) {
}
}
}
exports.stringFromUTF8 = stringFromUTF8;
/**
* Helper functions to convert different types of strings to Uint8Array
*/
@@ -248,13 +250,11 @@ function base64UrlToUint8Array(str) {
}
return new Uint8Array(result);
}
exports.base64UrlToUint8Array = base64UrlToUint8Array;
function stringToUint8Array(str) {
const result = [];
stringToUTF8(str, (byte) => result.push(byte));
return new Uint8Array(result);
}
exports.stringToUint8Array = stringToUint8Array;
function bytesToBase64URL(bytes) {
const result = [];
const state = { queue: 0, queuedBits: 0 };
@@ -266,5 +266,4 @@ function bytesToBase64URL(bytes) {
byteToBase64URL(null, state, onChar);
return result.join('');
}
exports.bytesToBase64URL = bytesToBase64URL;
//# sourceMappingURL=base64url.js.map
+159 -7
View File
@@ -1,6 +1,23 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AuthInvalidJwtError = exports.isAuthWeakPasswordError = exports.AuthWeakPasswordError = exports.isAuthRetryableFetchError = exports.AuthRetryableFetchError = exports.AuthPKCEGrantCodeExchangeError = exports.isAuthImplicitGrantRedirectError = exports.AuthImplicitGrantRedirectError = exports.AuthInvalidCredentialsError = exports.AuthInvalidTokenResponseError = exports.isAuthSessionMissingError = exports.AuthSessionMissingError = exports.CustomAuthError = exports.AuthUnknownError = exports.isAuthApiError = exports.AuthApiError = exports.isAuthError = exports.AuthError = void 0;
exports.AuthInvalidJwtError = exports.AuthWeakPasswordError = exports.AuthRetryableFetchError = exports.AuthPKCECodeVerifierMissingError = exports.AuthPKCEGrantCodeExchangeError = exports.AuthImplicitGrantRedirectError = exports.AuthInvalidCredentialsError = exports.AuthInvalidTokenResponseError = exports.AuthSessionMissingError = exports.CustomAuthError = exports.AuthUnknownError = exports.AuthApiError = exports.AuthError = void 0;
exports.isAuthError = isAuthError;
exports.isAuthApiError = isAuthApiError;
exports.isAuthSessionMissingError = isAuthSessionMissingError;
exports.isAuthImplicitGrantRedirectError = isAuthImplicitGrantRedirectError;
exports.isAuthPKCECodeVerifierMissingError = isAuthPKCECodeVerifierMissingError;
exports.isAuthRetryableFetchError = isAuthRetryableFetchError;
exports.isAuthWeakPasswordError = isAuthWeakPasswordError;
/**
* Base error thrown by Supabase Auth helpers.
*
* @example
* ```ts
* import { AuthError } from '@supabase/auth-js'
*
* throw new AuthError('Unexpected auth error', 500, 'unexpected')
* ```
*/
class AuthError extends Error {
constructor(message, status, code) {
super(message);
@@ -14,7 +31,16 @@ exports.AuthError = AuthError;
function isAuthError(error) {
return typeof error === 'object' && error !== null && '__isAuthError' in error;
}
exports.isAuthError = isAuthError;
/**
* Error returned directly from the GoTrue REST API.
*
* @example
* ```ts
* import { AuthApiError } from '@supabase/auth-js'
*
* throw new AuthApiError('Invalid credentials', 400, 'invalid_credentials')
* ```
*/
class AuthApiError extends AuthError {
constructor(message, status, code) {
super(message, status, code);
@@ -27,7 +53,20 @@ exports.AuthApiError = AuthApiError;
function isAuthApiError(error) {
return isAuthError(error) && error.name === 'AuthApiError';
}
exports.isAuthApiError = isAuthApiError;
/**
* Wraps non-standard errors so callers can inspect the root cause.
*
* @example
* ```ts
* import { AuthUnknownError } from '@supabase/auth-js'
*
* try {
* await someAuthCall()
* } catch (err) {
* throw new AuthUnknownError('Auth failed', err)
* }
* ```
*/
class AuthUnknownError extends AuthError {
constructor(message, originalError) {
super(message);
@@ -36,6 +75,16 @@ class AuthUnknownError extends AuthError {
}
}
exports.AuthUnknownError = AuthUnknownError;
/**
* Flexible error class used to create named auth errors at runtime.
*
* @example
* ```ts
* import { CustomAuthError } from '@supabase/auth-js'
*
* throw new CustomAuthError('My custom auth error', 'MyAuthError', 400, 'custom_code')
* ```
*/
class CustomAuthError extends AuthError {
constructor(message, name, status, code) {
super(message, status, code);
@@ -44,6 +93,16 @@ class CustomAuthError extends AuthError {
}
}
exports.CustomAuthError = CustomAuthError;
/**
* Error thrown when an operation requires a session but none is present.
*
* @example
* ```ts
* import { AuthSessionMissingError } from '@supabase/auth-js'
*
* throw new AuthSessionMissingError()
* ```
*/
class AuthSessionMissingError extends CustomAuthError {
constructor() {
super('Auth session missing!', 'AuthSessionMissingError', 400, undefined);
@@ -53,19 +112,51 @@ exports.AuthSessionMissingError = AuthSessionMissingError;
function isAuthSessionMissingError(error) {
return isAuthError(error) && error.name === 'AuthSessionMissingError';
}
exports.isAuthSessionMissingError = isAuthSessionMissingError;
/**
* Error thrown when the token response is malformed.
*
* @example
* ```ts
* import { AuthInvalidTokenResponseError } from '@supabase/auth-js'
*
* throw new AuthInvalidTokenResponseError()
* ```
*/
class AuthInvalidTokenResponseError extends CustomAuthError {
constructor() {
super('Auth session or user missing', 'AuthInvalidTokenResponseError', 500, undefined);
}
}
exports.AuthInvalidTokenResponseError = AuthInvalidTokenResponseError;
/**
* Error thrown when email/password credentials are invalid.
*
* @example
* ```ts
* import { AuthInvalidCredentialsError } from '@supabase/auth-js'
*
* throw new AuthInvalidCredentialsError('Email or password is incorrect')
* ```
*/
class AuthInvalidCredentialsError extends CustomAuthError {
constructor(message) {
super(message, 'AuthInvalidCredentialsError', 400, undefined);
}
}
exports.AuthInvalidCredentialsError = AuthInvalidCredentialsError;
/**
* Error thrown when implicit grant redirects contain an error.
*
* @example
* ```ts
* import { AuthImplicitGrantRedirectError } from '@supabase/auth-js'
*
* throw new AuthImplicitGrantRedirectError('OAuth redirect failed', {
* error: 'access_denied',
* code: 'oauth_error',
* })
* ```
*/
class AuthImplicitGrantRedirectError extends CustomAuthError {
constructor(message, details = null) {
super(message, 'AuthImplicitGrantRedirectError', 500, undefined);
@@ -85,7 +176,16 @@ exports.AuthImplicitGrantRedirectError = AuthImplicitGrantRedirectError;
function isAuthImplicitGrantRedirectError(error) {
return isAuthError(error) && error.name === 'AuthImplicitGrantRedirectError';
}
exports.isAuthImplicitGrantRedirectError = isAuthImplicitGrantRedirectError;
/**
* Error thrown during PKCE code exchanges.
*
* @example
* ```ts
* import { AuthPKCEGrantCodeExchangeError } from '@supabase/auth-js'
*
* throw new AuthPKCEGrantCodeExchangeError('PKCE exchange failed')
* ```
*/
class AuthPKCEGrantCodeExchangeError extends CustomAuthError {
constructor(message, details = null) {
super(message, 'AuthPKCEGrantCodeExchangeError', 500, undefined);
@@ -102,6 +202,40 @@ class AuthPKCEGrantCodeExchangeError extends CustomAuthError {
}
}
exports.AuthPKCEGrantCodeExchangeError = AuthPKCEGrantCodeExchangeError;
/**
* Error thrown when the PKCE code verifier is not found in storage.
* This typically happens when the auth flow was initiated in a different
* browser, device, or the storage was cleared.
*
* @example
* ```ts
* import { AuthPKCECodeVerifierMissingError } from '@supabase/auth-js'
*
* throw new AuthPKCECodeVerifierMissingError()
* ```
*/
class AuthPKCECodeVerifierMissingError extends CustomAuthError {
constructor() {
super('PKCE code verifier not found in storage. ' +
'This can happen if the auth flow was initiated in a different browser or device, ' +
'or if the storage was cleared. For SSR frameworks (Next.js, SvelteKit, etc.), ' +
'use @supabase/ssr on both the server and client to store the code verifier in cookies.', 'AuthPKCECodeVerifierMissingError', 400, 'pkce_code_verifier_not_found');
}
}
exports.AuthPKCECodeVerifierMissingError = AuthPKCECodeVerifierMissingError;
function isAuthPKCECodeVerifierMissingError(error) {
return isAuthError(error) && error.name === 'AuthPKCECodeVerifierMissingError';
}
/**
* Error thrown when a transient fetch issue occurs.
*
* @example
* ```ts
* import { AuthRetryableFetchError } from '@supabase/auth-js'
*
* throw new AuthRetryableFetchError('Service temporarily unavailable', 503)
* ```
*/
class AuthRetryableFetchError extends CustomAuthError {
constructor(message, status) {
super(message, 'AuthRetryableFetchError', status, undefined);
@@ -111,12 +245,21 @@ exports.AuthRetryableFetchError = AuthRetryableFetchError;
function isAuthRetryableFetchError(error) {
return isAuthError(error) && error.name === 'AuthRetryableFetchError';
}
exports.isAuthRetryableFetchError = isAuthRetryableFetchError;
/**
* This error is thrown on certain methods when the password used is deemed
* weak. Inspect the reasons to identify what password strength rules are
* inadequate.
*/
/**
* Error thrown when a supplied password is considered weak.
*
* @example
* ```ts
* import { AuthWeakPasswordError } from '@supabase/auth-js'
*
* throw new AuthWeakPasswordError('Password too short', 400, ['min_length'])
* ```
*/
class AuthWeakPasswordError extends CustomAuthError {
constructor(message, status, reasons) {
super(message, 'AuthWeakPasswordError', status, 'weak_password');
@@ -127,7 +270,16 @@ exports.AuthWeakPasswordError = AuthWeakPasswordError;
function isAuthWeakPasswordError(error) {
return isAuthError(error) && error.name === 'AuthWeakPasswordError';
}
exports.isAuthWeakPasswordError = isAuthWeakPasswordError;
/**
* Error thrown when a JWT cannot be verified or parsed.
*
* @example
* ```ts
* import { AuthInvalidJwtError } from '@supabase/auth-js'
*
* throw new AuthInvalidJwtError('Token signature is invalid')
* ```
*/
class AuthInvalidJwtError extends CustomAuthError {
constructor(message) {
super(message, 'AuthInvalidJwtError', 400, 'invalid_jwt');
+10 -21
View File
@@ -1,17 +1,14 @@
"use strict";
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports._noResolveJsonResponse = exports._generateLinkResponse = exports._ssoResponse = exports._userResponse = exports._sessionResponsePassword = exports._sessionResponse = exports._request = exports.handleError = void 0;
exports.handleError = handleError;
exports._request = _request;
exports._sessionResponse = _sessionResponse;
exports._sessionResponsePassword = _sessionResponsePassword;
exports._userResponse = _userResponse;
exports._ssoResponse = _ssoResponse;
exports._generateLinkResponse = _generateLinkResponse;
exports._noResolveJsonResponse = _noResolveJsonResponse;
const tslib_1 = require("tslib");
const constants_1 = require("./constants");
const helpers_1 = require("./helpers");
const errors_1 = require("./errors");
@@ -68,7 +65,6 @@ async function handleError(error) {
}
throw new errors_1.AuthApiError(_getErrorMessage(data), error.status || 500, errorCode);
}
exports.handleError = handleError;
const _getRequestParams = (method, options, parameters, body) => {
const params = { method, headers: (options === null || options === void 0 ? void 0 : options.headers) || {} };
if (method === 'GET') {
@@ -98,7 +94,6 @@ async function _request(fetcher, method, url, options) {
}, {}, options === null || options === void 0 ? void 0 : options.body);
return (options === null || options === void 0 ? void 0 : options.xform) ? options === null || options === void 0 ? void 0 : options.xform(data) : { data: Object.assign({}, data), error: null };
}
exports._request = _request;
async function _handleRequest(fetcher, method, url, options, parameters, body) {
const requestParams = _getRequestParams(method, options, parameters, body);
let result;
@@ -135,7 +130,6 @@ function _sessionResponse(data) {
const user = (_a = data.user) !== null && _a !== void 0 ? _a : data;
return { data: { session, user }, error: null };
}
exports._sessionResponse = _sessionResponse;
function _sessionResponsePassword(data) {
const response = _sessionResponse(data);
if (!response.error &&
@@ -150,19 +144,16 @@ function _sessionResponsePassword(data) {
}
return response;
}
exports._sessionResponsePassword = _sessionResponsePassword;
function _userResponse(data) {
var _a;
const user = (_a = data.user) !== null && _a !== void 0 ? _a : data;
return { data: { user }, error: null };
}
exports._userResponse = _userResponse;
function _ssoResponse(data) {
return { data, error: null };
}
exports._ssoResponse = _ssoResponse;
function _generateLinkResponse(data) {
const { action_link, email_otp, hashed_token, redirect_to, verification_type } = data, rest = __rest(data, ["action_link", "email_otp", "hashed_token", "redirect_to", "verification_type"]);
const { action_link, email_otp, hashed_token, redirect_to, verification_type } = data, rest = tslib_1.__rest(data, ["action_link", "email_otp", "hashed_token", "redirect_to", "verification_type"]);
const properties = {
action_link,
email_otp,
@@ -179,11 +170,9 @@ function _generateLinkResponse(data) {
error: null,
};
}
exports._generateLinkResponse = _generateLinkResponse;
function _noResolveJsonResponse(data) {
return data;
}
exports._noResolveJsonResponse = _noResolveJsonResponse;
/**
* hasSession checks if the response object contains a valid session
* @param data A response object
+70 -53
View File
@@ -1,29 +1,22 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.deepClone = exports.userNotAvailableProxy = exports.validateUUID = exports.getAlgorithm = exports.validateExp = exports.parseResponseAPIVersion = exports.getCodeChallengeAndMethod = exports.generatePKCEChallenge = exports.generatePKCEVerifier = exports.retryable = exports.sleep = exports.decodeJWT = exports.Deferred = exports.removeItemAsync = exports.getItemAsync = exports.setItemAsync = exports.looksLikeFetchResponse = exports.resolveFetch = exports.parseParametersFromURL = exports.supportsLocalStorage = exports.isBrowser = exports.uuid = exports.expiresAt = void 0;
exports.Deferred = exports.removeItemAsync = exports.getItemAsync = exports.setItemAsync = exports.looksLikeFetchResponse = exports.resolveFetch = exports.supportsLocalStorage = exports.isBrowser = void 0;
exports.expiresAt = expiresAt;
exports.generateCallbackId = generateCallbackId;
exports.parseParametersFromURL = parseParametersFromURL;
exports.decodeJWT = decodeJWT;
exports.sleep = sleep;
exports.retryable = retryable;
exports.generatePKCEVerifier = generatePKCEVerifier;
exports.generatePKCEChallenge = generatePKCEChallenge;
exports.getCodeChallengeAndMethod = getCodeChallengeAndMethod;
exports.parseResponseAPIVersion = parseResponseAPIVersion;
exports.validateExp = validateExp;
exports.getAlgorithm = getAlgorithm;
exports.validateUUID = validateUUID;
exports.userNotAvailableProxy = userNotAvailableProxy;
exports.insecureUserWarningProxy = insecureUserWarningProxy;
exports.deepClone = deepClone;
const constants_1 = require("./constants");
const errors_1 = require("./errors");
const base64url_1 = require("./base64url");
@@ -31,14 +24,22 @@ function expiresAt(expiresIn) {
const timeNow = Math.round(Date.now() / 1000);
return timeNow + expiresIn;
}
exports.expiresAt = expiresAt;
function uuid() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
const r = (Math.random() * 16) | 0, v = c == 'x' ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
/**
* Generates a unique identifier for internal callback subscriptions.
*
* This function uses JavaScript Symbols to create guaranteed-unique identifiers
* for auth state change callbacks. Symbols are ideal for this use case because:
* - They are guaranteed unique by the JavaScript runtime
* - They work in all environments (browser, SSR, Node.js)
* - They avoid issues with Next.js 16 deterministic rendering requirements
* - They are perfect for internal, non-serializable identifiers
*
* Note: This function is only used for internal subscription management,
* not for security-critical operations like session tokens.
*/
function generateCallbackId() {
return Symbol('auth-callback');
}
exports.uuid = uuid;
const isBrowser = () => typeof window !== 'undefined' && typeof document !== 'undefined';
exports.isBrowser = isBrowser;
const localStorageWriteTests = {
@@ -103,19 +104,11 @@ function parseParametersFromURL(href) {
});
return result;
}
exports.parseParametersFromURL = parseParametersFromURL;
const resolveFetch = (customFetch) => {
let _fetch;
if (customFetch) {
_fetch = customFetch;
return (...args) => customFetch(...args);
}
else if (typeof fetch === 'undefined') {
_fetch = (...args) => Promise.resolve().then(() => __importStar(require('@supabase/node-fetch'))).then(({ default: fetch }) => fetch(...args));
}
else {
_fetch = fetch;
}
return (...args) => _fetch(...args);
return (...args) => fetch(...args);
};
exports.resolveFetch = resolveFetch;
const looksLikeFetchResponse = (maybeResponse) => {
@@ -191,7 +184,6 @@ function decodeJWT(token) {
};
return data;
}
exports.decodeJWT = decodeJWT;
/**
* Creates a promise that resolves to null after some time.
*/
@@ -200,7 +192,6 @@ async function sleep(time) {
setTimeout(() => accept(null), time);
});
}
exports.sleep = sleep;
/**
* Converts the provided async function into a retryable function. Each result
* or thrown error is sent to the isRetryable function which should return true
@@ -230,7 +221,6 @@ function retryable(fn, isRetryable) {
});
return promise;
}
exports.retryable = retryable;
function dec2hex(dec) {
return ('0' + dec.toString(16)).substr(-2);
}
@@ -250,7 +240,6 @@ function generatePKCEVerifier() {
crypto.getRandomValues(array);
return Array.from(array, dec2hex).join('');
}
exports.generatePKCEVerifier = generatePKCEVerifier;
async function sha256(randomString) {
const encoder = new TextEncoder();
const encodedData = encoder.encode(randomString);
@@ -271,7 +260,6 @@ async function generatePKCEChallenge(verifier) {
const hashed = await sha256(verifier);
return btoa(hashed).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
}
exports.generatePKCEChallenge = generatePKCEChallenge;
async function getCodeChallengeAndMethod(storage, storageKey, isPasswordRecovery = false) {
const codeVerifier = generatePKCEVerifier();
let storedCodeVerifier = codeVerifier;
@@ -283,7 +271,6 @@ async function getCodeChallengeAndMethod(storage, storageKey, isPasswordRecovery
const codeChallengeMethod = codeVerifier === codeChallenge ? 'plain' : 's256';
return [codeChallenge, codeChallengeMethod];
}
exports.getCodeChallengeAndMethod = getCodeChallengeAndMethod;
/** Parses the API version which is 2YYY-MM-DD. */
const API_VERSION_REGEX = /^2[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|1[0-9]|2[0-9]|3[0-1])$/i;
function parseResponseAPIVersion(response) {
@@ -302,7 +289,6 @@ function parseResponseAPIVersion(response) {
return null;
}
}
exports.parseResponseAPIVersion = parseResponseAPIVersion;
function validateExp(exp) {
if (!exp) {
throw new Error('Missing exp claim');
@@ -312,7 +298,6 @@ function validateExp(exp) {
throw new Error('JWT has expired');
}
}
exports.validateExp = validateExp;
function getAlgorithm(alg) {
switch (alg) {
case 'RS256':
@@ -330,14 +315,12 @@ function getAlgorithm(alg) {
throw new Error('Invalid alg claim');
}
}
exports.getAlgorithm = getAlgorithm;
const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
function validateUUID(str) {
if (!UUID_REGEX.test(str)) {
throw new Error('@supabase/auth-js: Expected parameter to be UUID but is not');
}
}
exports.validateUUID = validateUUID;
function userNotAvailableProxy() {
const proxyTarget = {};
return new Proxy(proxyTarget, {
@@ -366,7 +349,42 @@ function userNotAvailableProxy() {
},
});
}
exports.userNotAvailableProxy = userNotAvailableProxy;
/**
* Creates a proxy around a user object that warns when properties are accessed on the server.
* This is used to alert developers that using user data from getSession() on the server is insecure.
*
* @param user The actual user object to wrap
* @param suppressWarningRef An object with a 'value' property that controls warning suppression
* @returns A proxied user object that warns on property access
*/
function insecureUserWarningProxy(user, suppressWarningRef) {
return new Proxy(user, {
get: (target, prop, receiver) => {
// Allow internal checks without warning
if (prop === '__isInsecureUserWarningProxy') {
return true;
}
// Preventative check for common problematic symbols during cloning/inspection
// These symbols might be accessed by structuredClone or other internal mechanisms
if (typeof prop === 'symbol') {
const sProp = prop.toString();
if (sProp === 'Symbol(Symbol.toPrimitive)' ||
sProp === 'Symbol(Symbol.toStringTag)' ||
sProp === 'Symbol(util.inspect.custom)' ||
sProp === 'Symbol(nodejs.util.inspect.custom)') {
// Return the actual value for these symbols to allow proper inspection
return Reflect.get(target, prop, receiver);
}
}
// Emit warning on first property access
if (!suppressWarningRef.value && typeof prop === 'string') {
console.warn('Using the user object as returned from supabase.auth.getSession() or from some supabase.auth.onAuthStateChange() events could be insecure! This value comes directly from the storage medium (usually cookies on the server) and may not be authentic. Use supabase.auth.getUser() instead which authenticates the data by contacting the Supabase Auth server.');
suppressWarningRef.value = true;
}
return Reflect.get(target, prop, receiver);
},
});
}
/**
* Deep clones a JSON-serializable object using JSON.parse(JSON.stringify(obj)).
* Note: Only works for JSON-safe data.
@@ -374,5 +392,4 @@ exports.userNotAvailableProxy = userNotAvailableProxy;
function deepClone(obj) {
return JSON.parse(JSON.stringify(obj));
}
exports.deepClone = deepClone;
//# sourceMappingURL=helpers.js.map
@@ -1,6 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.memoryLocalStorageAdapter = void 0;
exports.memoryLocalStorageAdapter = memoryLocalStorageAdapter;
/**
* Returns a localStorage-like object that stores the key-value pairs in
* memory.
@@ -18,5 +18,4 @@ function memoryLocalStorageAdapter(store = {}) {
},
};
}
exports.memoryLocalStorageAdapter = memoryLocalStorageAdapter;
//# sourceMappingURL=local-storage.js.map
+50 -4
View File
@@ -1,6 +1,8 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.processLock = exports.navigatorLock = exports.ProcessLockAcquireTimeoutError = exports.NavigatorLockAcquireTimeoutError = exports.LockAcquireTimeoutError = exports.internals = void 0;
exports.ProcessLockAcquireTimeoutError = exports.NavigatorLockAcquireTimeoutError = exports.LockAcquireTimeoutError = exports.internals = void 0;
exports.navigatorLock = navigatorLock;
exports.processLock = processLock;
const helpers_1 = require("./helpers");
/**
* @experimental
@@ -18,6 +20,17 @@ exports.internals = {
* An error thrown when a lock cannot be acquired after some amount of time.
*
* Use the {@link #isAcquireTimeout} property instead of checking with `instanceof`.
*
* @example
* ```ts
* import { LockAcquireTimeoutError } from '@supabase/auth-js'
*
* class CustomLockError extends LockAcquireTimeoutError {
* constructor() {
* super('Lock timed out')
* }
* }
* ```
*/
class LockAcquireTimeoutError extends Error {
constructor(message) {
@@ -26,9 +39,29 @@ class LockAcquireTimeoutError extends Error {
}
}
exports.LockAcquireTimeoutError = LockAcquireTimeoutError;
/**
* Error thrown when the browser Navigator Lock API fails to acquire a lock.
*
* @example
* ```ts
* import { NavigatorLockAcquireTimeoutError } from '@supabase/auth-js'
*
* throw new NavigatorLockAcquireTimeoutError('Lock timed out')
* ```
*/
class NavigatorLockAcquireTimeoutError extends LockAcquireTimeoutError {
}
exports.NavigatorLockAcquireTimeoutError = NavigatorLockAcquireTimeoutError;
/**
* Error thrown when the process-level lock helper cannot acquire a lock.
*
* @example
* ```ts
* import { ProcessLockAcquireTimeoutError } from '@supabase/auth-js'
*
* throw new ProcessLockAcquireTimeoutError('Lock timed out')
* ```
*/
class ProcessLockAcquireTimeoutError extends LockAcquireTimeoutError {
}
exports.ProcessLockAcquireTimeoutError = ProcessLockAcquireTimeoutError;
@@ -56,6 +89,12 @@ exports.ProcessLockAcquireTimeoutError = ProcessLockAcquireTimeoutError;
* will time out after so many milliseconds. An error is
* a timeout if it has `isAcquireTimeout` set to true.
* @param fn The operation to run once the lock is acquired.
* @example
* ```ts
* await navigatorLock('sync-user', 1000, async () => {
* await refreshSession()
* })
* ```
*/
async function navigatorLock(name, acquireTimeout, fn) {
if (exports.internals.debug) {
@@ -127,7 +166,6 @@ async function navigatorLock(name, acquireTimeout, fn) {
}
}));
}
exports.navigatorLock = navigatorLock;
const PROCESS_LOCKS = {};
/**
* Implements a global exclusive lock that works only in the current process.
@@ -142,6 +180,12 @@ const PROCESS_LOCKS = {};
* will time out after so many milliseconds. An error is
* a timeout if it has `isAcquireTimeout` set to true.
* @param fn The operation to run once the lock is acquired.
* @example
* ```ts
* await processLock('migrate', 5000, async () => {
* await runMigration()
* })
* ```
*/
async function processLock(name, acquireTimeout, fn) {
var _a;
@@ -154,7 +198,10 @@ async function processLock(name, acquireTimeout, fn) {
acquireTimeout >= 0
? new Promise((_, reject) => {
setTimeout(() => {
reject(new ProcessLockAcquireTimeoutError(`Acquring process lock with name "${name}" timed out`));
console.warn(`@supabase/gotrue-js: Lock "${name}" acquisition timed out after ${acquireTimeout}ms. ` +
'This may be caused by another operation holding the lock. ' +
'Consider increasing lockAcquireTimeout or checking for stuck operations.');
reject(new ProcessLockAcquireTimeoutError(`Acquiring process lock with name "${name}" timed out`));
}, acquireTimeout);
})
: null,
@@ -183,5 +230,4 @@ async function processLock(name, acquireTimeout, fn) {
// error or with an acquire timeout error
return await currentOperation;
}
exports.processLock = processLock;
//# sourceMappingURL=locks.js.map
+1 -2
View File
@@ -1,6 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.polyfillGlobalThis = void 0;
exports.polyfillGlobalThis = polyfillGlobalThis;
/**
* https://mathiasbynens.be/notes/globalthis
*/
@@ -26,5 +26,4 @@ function polyfillGlobalThis() {
}
}
}
exports.polyfillGlobalThis = polyfillGlobalThis;
//# sourceMappingURL=polyfills.js.map
+18
View File
@@ -1,5 +1,23 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SIGN_OUT_SCOPES = void 0;
const WeakPasswordReasons = ['length', 'characters', 'pwned'];
const AMRMethods = [
'password',
'otp',
'oauth',
'totp',
'mfa/totp',
'mfa/phone',
'mfa/webauthn',
'anonymous',
'sso/saml',
'magiclink',
'web3',
'oauth_provider/authorization_code',
];
const FactorTypes = ['totp', 'phone', 'webauthn'];
const FactorVerificationStatuses = ['verified', 'unverified'];
const MFATOTPChannels = ['sms', 'whatsapp'];
exports.SIGN_OUT_SCOPES = ['global', 'local', 'others'];
//# sourceMappingURL=types.js.map
+7 -1
View File
@@ -1,5 +1,11 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.version = void 0;
exports.version = '2.71.1';
// Generated automatically during releases by scripts/update-version-files.ts
// This file provides runtime access to the package version for:
// - HTTP request headers (e.g., X-Client-Info header for API requests)
// - Debugging and support (identifying which version is running)
// - Telemetry and logging (version reporting in errors/analytics)
// - Ensuring build artifacts match the published package version
exports.version = '2.93.3';
//# sourceMappingURL=version.js.map
@@ -0,0 +1,66 @@
"use strict";
// types and functions copied over from viem so this library doesn't depend on it
Object.defineProperty(exports, "__esModule", { value: true });
exports.getAddress = getAddress;
exports.fromHex = fromHex;
exports.toHex = toHex;
exports.createSiweMessage = createSiweMessage;
function getAddress(address) {
if (!/^0x[a-fA-F0-9]{40}$/.test(address)) {
throw new Error(`@supabase/auth-js: Address "${address}" is invalid.`);
}
return address.toLowerCase();
}
function fromHex(hex) {
return parseInt(hex, 16);
}
function toHex(value) {
const bytes = new TextEncoder().encode(value);
const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, '0')).join('');
return ('0x' + hex);
}
/**
* Creates EIP-4361 formatted message.
*/
function createSiweMessage(parameters) {
var _a;
const { chainId, domain, expirationTime, issuedAt = new Date(), nonce, notBefore, requestId, resources, scheme, uri, version, } = parameters;
// Validate fields
{
if (!Number.isInteger(chainId))
throw new Error(`@supabase/auth-js: Invalid SIWE message field "chainId". Chain ID must be a EIP-155 chain ID. Provided value: ${chainId}`);
if (!domain)
throw new Error(`@supabase/auth-js: Invalid SIWE message field "domain". Domain must be provided.`);
if (nonce && nonce.length < 8)
throw new Error(`@supabase/auth-js: Invalid SIWE message field "nonce". Nonce must be at least 8 characters. Provided value: ${nonce}`);
if (!uri)
throw new Error(`@supabase/auth-js: Invalid SIWE message field "uri". URI must be provided.`);
if (version !== '1')
throw new Error(`@supabase/auth-js: Invalid SIWE message field "version". Version must be '1'. Provided value: ${version}`);
if ((_a = parameters.statement) === null || _a === void 0 ? void 0 : _a.includes('\n'))
throw new Error(`@supabase/auth-js: Invalid SIWE message field "statement". Statement must not include '\\n'. Provided value: ${parameters.statement}`);
}
// Construct message
const address = getAddress(parameters.address);
const origin = scheme ? `${scheme}://${domain}` : domain;
const statement = parameters.statement ? `${parameters.statement}\n` : '';
const prefix = `${origin} wants you to sign in with your Ethereum account:\n${address}\n\n${statement}`;
let suffix = `URI: ${uri}\nVersion: ${version}\nChain ID: ${chainId}${nonce ? `\nNonce: ${nonce}` : ''}\nIssued At: ${issuedAt.toISOString()}`;
if (expirationTime)
suffix += `\nExpiration Time: ${expirationTime.toISOString()}`;
if (notBefore)
suffix += `\nNot Before: ${notBefore.toISOString()}`;
if (requestId)
suffix += `\nRequest ID: ${requestId}`;
if (resources) {
let content = '\nResources:';
for (const resource of resources) {
if (!resource || typeof resource !== 'string')
throw new Error(`@supabase/auth-js: Invalid SIWE message field "resources". Every resource must be a valid string. Provided value: ${resource}`);
content += `\n- ${resource}`;
}
suffix += content;
}
return `${prefix}\n${suffix}`;
}
//# sourceMappingURL=ethereum.js.map
@@ -0,0 +1,265 @@
"use strict";
/* eslint-disable @typescript-eslint/ban-ts-comment */
Object.defineProperty(exports, "__esModule", { value: true });
exports.WebAuthnUnknownError = exports.WebAuthnError = void 0;
exports.isWebAuthnError = isWebAuthnError;
exports.identifyRegistrationError = identifyRegistrationError;
exports.identifyAuthenticationError = identifyAuthenticationError;
const webauthn_1 = require("./webauthn");
/**
* A custom Error used to return a more nuanced error detailing _why_ one of the eight documented
* errors in the spec was raised after calling `navigator.credentials.create()` or
* `navigator.credentials.get()`:
*
* - `AbortError`
* - `ConstraintError`
* - `InvalidStateError`
* - `NotAllowedError`
* - `NotSupportedError`
* - `SecurityError`
* - `TypeError`
* - `UnknownError`
*
* Error messages were determined through investigation of the spec to determine under which
* scenarios a given error would be raised.
*/
class WebAuthnError extends Error {
constructor({ message, code, cause, name, }) {
var _a;
// @ts-ignore: help Rollup understand that `cause` is okay to set
super(message, { cause });
this.__isWebAuthnError = true;
this.name = (_a = name !== null && name !== void 0 ? name : (cause instanceof Error ? cause.name : undefined)) !== null && _a !== void 0 ? _a : 'Unknown Error';
this.code = code;
}
}
exports.WebAuthnError = WebAuthnError;
/**
* Error class for unknown WebAuthn errors.
* Wraps unexpected errors that don't match known WebAuthn error conditions.
*/
class WebAuthnUnknownError extends WebAuthnError {
constructor(message, originalError) {
super({
code: 'ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY',
cause: originalError,
message,
});
this.name = 'WebAuthnUnknownError';
this.originalError = originalError;
}
}
exports.WebAuthnUnknownError = WebAuthnUnknownError;
/**
* Type guard to check if an error is a WebAuthnError.
* @param {unknown} error - The error to check
* @returns {boolean} True if the error is a WebAuthnError
*/
function isWebAuthnError(error) {
return typeof error === 'object' && error !== null && '__isWebAuthnError' in error;
}
/**
* Attempt to intuit _why_ an error was raised after calling `navigator.credentials.create()`.
* Maps browser errors to specific WebAuthn error codes for better debugging.
* @param {Object} params - Error identification parameters
* @param {Error} params.error - The error thrown by the browser
* @param {CredentialCreationOptions} params.options - The options passed to credentials.create()
* @returns {WebAuthnError} A WebAuthnError with a specific error code
* @see {@link https://w3c.github.io/webauthn/#sctn-createCredential W3C WebAuthn Spec - Create Credential}
*/
function identifyRegistrationError({ error, options, }) {
var _a, _b, _c;
const { publicKey } = options;
if (!publicKey) {
throw Error('options was missing required publicKey property');
}
if (error.name === 'AbortError') {
if (options.signal instanceof AbortSignal) {
// https://www.w3.org/TR/webauthn-2/#sctn-createCredential (Step 16)
return new WebAuthnError({
message: 'Registration ceremony was sent an abort signal',
code: 'ERROR_CEREMONY_ABORTED',
cause: error,
});
}
}
else if (error.name === 'ConstraintError') {
if (((_a = publicKey.authenticatorSelection) === null || _a === void 0 ? void 0 : _a.requireResidentKey) === true) {
// https://www.w3.org/TR/webauthn-2/#sctn-op-make-cred (Step 4)
return new WebAuthnError({
message: 'Discoverable credentials were required but no available authenticator supported it',
code: 'ERROR_AUTHENTICATOR_MISSING_DISCOVERABLE_CREDENTIAL_SUPPORT',
cause: error,
});
}
else if (
// @ts-ignore: `mediation` doesn't yet exist on CredentialCreationOptions but it's possible as of Sept 2024
options.mediation === 'conditional' &&
((_b = publicKey.authenticatorSelection) === null || _b === void 0 ? void 0 : _b.userVerification) === 'required') {
// https://w3c.github.io/webauthn/#sctn-createCredential (Step 22.4)
return new WebAuthnError({
message: 'User verification was required during automatic registration but it could not be performed',
code: 'ERROR_AUTO_REGISTER_USER_VERIFICATION_FAILURE',
cause: error,
});
}
else if (((_c = publicKey.authenticatorSelection) === null || _c === void 0 ? void 0 : _c.userVerification) === 'required') {
// https://www.w3.org/TR/webauthn-2/#sctn-op-make-cred (Step 5)
return new WebAuthnError({
message: 'User verification was required but no available authenticator supported it',
code: 'ERROR_AUTHENTICATOR_MISSING_USER_VERIFICATION_SUPPORT',
cause: error,
});
}
}
else if (error.name === 'InvalidStateError') {
// https://www.w3.org/TR/webauthn-2/#sctn-createCredential (Step 20)
// https://www.w3.org/TR/webauthn-2/#sctn-op-make-cred (Step 3)
return new WebAuthnError({
message: 'The authenticator was previously registered',
code: 'ERROR_AUTHENTICATOR_PREVIOUSLY_REGISTERED',
cause: error,
});
}
else if (error.name === 'NotAllowedError') {
/**
* Pass the error directly through. Platforms are overloading this error beyond what the spec
* defines and we don't want to overwrite potentially useful error messages.
*/
return new WebAuthnError({
message: error.message,
code: 'ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY',
cause: error,
});
}
else if (error.name === 'NotSupportedError') {
const validPubKeyCredParams = publicKey.pubKeyCredParams.filter((param) => param.type === 'public-key');
if (validPubKeyCredParams.length === 0) {
// https://www.w3.org/TR/webauthn-2/#sctn-createCredential (Step 10)
return new WebAuthnError({
message: 'No entry in pubKeyCredParams was of type "public-key"',
code: 'ERROR_MALFORMED_PUBKEYCREDPARAMS',
cause: error,
});
}
// https://www.w3.org/TR/webauthn-2/#sctn-op-make-cred (Step 2)
return new WebAuthnError({
message: 'No available authenticator supported any of the specified pubKeyCredParams algorithms',
code: 'ERROR_AUTHENTICATOR_NO_SUPPORTED_PUBKEYCREDPARAMS_ALG',
cause: error,
});
}
else if (error.name === 'SecurityError') {
const effectiveDomain = window.location.hostname;
if (!(0, webauthn_1.isValidDomain)(effectiveDomain)) {
// https://www.w3.org/TR/webauthn-2/#sctn-createCredential (Step 7)
return new WebAuthnError({
message: `${window.location.hostname} is an invalid domain`,
code: 'ERROR_INVALID_DOMAIN',
cause: error,
});
}
else if (publicKey.rp.id !== effectiveDomain) {
// https://www.w3.org/TR/webauthn-2/#sctn-createCredential (Step 8)
return new WebAuthnError({
message: `The RP ID "${publicKey.rp.id}" is invalid for this domain`,
code: 'ERROR_INVALID_RP_ID',
cause: error,
});
}
}
else if (error.name === 'TypeError') {
if (publicKey.user.id.byteLength < 1 || publicKey.user.id.byteLength > 64) {
// https://www.w3.org/TR/webauthn-2/#sctn-createCredential (Step 5)
return new WebAuthnError({
message: 'User ID was not between 1 and 64 characters',
code: 'ERROR_INVALID_USER_ID_LENGTH',
cause: error,
});
}
}
else if (error.name === 'UnknownError') {
// https://www.w3.org/TR/webauthn-2/#sctn-op-make-cred (Step 1)
// https://www.w3.org/TR/webauthn-2/#sctn-op-make-cred (Step 8)
return new WebAuthnError({
message: 'The authenticator was unable to process the specified options, or could not create a new credential',
code: 'ERROR_AUTHENTICATOR_GENERAL_ERROR',
cause: error,
});
}
return new WebAuthnError({
message: 'a Non-Webauthn related error has occurred',
code: 'ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY',
cause: error,
});
}
/**
* Attempt to intuit _why_ an error was raised after calling `navigator.credentials.get()`.
* Maps browser errors to specific WebAuthn error codes for better debugging.
* @param {Object} params - Error identification parameters
* @param {Error} params.error - The error thrown by the browser
* @param {CredentialRequestOptions} params.options - The options passed to credentials.get()
* @returns {WebAuthnError} A WebAuthnError with a specific error code
* @see {@link https://w3c.github.io/webauthn/#sctn-getAssertion W3C WebAuthn Spec - Get Assertion}
*/
function identifyAuthenticationError({ error, options, }) {
const { publicKey } = options;
if (!publicKey) {
throw Error('options was missing required publicKey property');
}
if (error.name === 'AbortError') {
if (options.signal instanceof AbortSignal) {
// https://www.w3.org/TR/webauthn-2/#sctn-createCredential (Step 16)
return new WebAuthnError({
message: 'Authentication ceremony was sent an abort signal',
code: 'ERROR_CEREMONY_ABORTED',
cause: error,
});
}
}
else if (error.name === 'NotAllowedError') {
/**
* Pass the error directly through. Platforms are overloading this error beyond what the spec
* defines and we don't want to overwrite potentially useful error messages.
*/
return new WebAuthnError({
message: error.message,
code: 'ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY',
cause: error,
});
}
else if (error.name === 'SecurityError') {
const effectiveDomain = window.location.hostname;
if (!(0, webauthn_1.isValidDomain)(effectiveDomain)) {
// https://www.w3.org/TR/webauthn-2/#sctn-discover-from-external-source (Step 5)
return new WebAuthnError({
message: `${window.location.hostname} is an invalid domain`,
code: 'ERROR_INVALID_DOMAIN',
cause: error,
});
}
else if (publicKey.rpId !== effectiveDomain) {
// https://www.w3.org/TR/webauthn-2/#sctn-discover-from-external-source (Step 6)
return new WebAuthnError({
message: `The RP ID "${publicKey.rpId}" is invalid for this domain`,
code: 'ERROR_INVALID_RP_ID',
cause: error,
});
}
}
else if (error.name === 'UnknownError') {
// https://www.w3.org/TR/webauthn-2/#sctn-op-get-assertion (Step 1)
// https://www.w3.org/TR/webauthn-2/#sctn-op-get-assertion (Step 12)
return new WebAuthnError({
message: 'The authenticator was unable to process the specified options, or could not create a new assertion signature',
code: 'ERROR_AUTHENTICATOR_GENERAL_ERROR',
cause: error,
});
}
return new WebAuthnError({
message: 'a Non-Webauthn related error has occurred',
code: 'ERROR_PASSTHROUGH_SEE_CAUSE_PROPERTY',
cause: error,
});
}
//# sourceMappingURL=webauthn.errors.js.map
+706
View File
@@ -0,0 +1,706 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.WebAuthnApi = exports.DEFAULT_REQUEST_OPTIONS = exports.DEFAULT_CREATION_OPTIONS = exports.webAuthnAbortService = exports.WebAuthnAbortService = exports.identifyAuthenticationError = exports.identifyRegistrationError = exports.isWebAuthnError = exports.WebAuthnError = void 0;
exports.deserializeCredentialCreationOptions = deserializeCredentialCreationOptions;
exports.deserializeCredentialRequestOptions = deserializeCredentialRequestOptions;
exports.serializeCredentialCreationResponse = serializeCredentialCreationResponse;
exports.serializeCredentialRequestResponse = serializeCredentialRequestResponse;
exports.isValidDomain = isValidDomain;
exports.createCredential = createCredential;
exports.getCredential = getCredential;
exports.mergeCredentialCreationOptions = mergeCredentialCreationOptions;
exports.mergeCredentialRequestOptions = mergeCredentialRequestOptions;
const tslib_1 = require("tslib");
const base64url_1 = require("./base64url");
const errors_1 = require("./errors");
const helpers_1 = require("./helpers");
const webauthn_errors_1 = require("./webauthn.errors");
Object.defineProperty(exports, "identifyAuthenticationError", { enumerable: true, get: function () { return webauthn_errors_1.identifyAuthenticationError; } });
Object.defineProperty(exports, "identifyRegistrationError", { enumerable: true, get: function () { return webauthn_errors_1.identifyRegistrationError; } });
Object.defineProperty(exports, "isWebAuthnError", { enumerable: true, get: function () { return webauthn_errors_1.isWebAuthnError; } });
Object.defineProperty(exports, "WebAuthnError", { enumerable: true, get: function () { return webauthn_errors_1.WebAuthnError; } });
/**
* WebAuthn abort service to manage ceremony cancellation.
* Ensures only one WebAuthn ceremony is active at a time to prevent "operation already in progress" errors.
*
* @experimental This class is experimental and may change in future releases
* @see {@link https://w3c.github.io/webauthn/#sctn-automation-webdriver-capability W3C WebAuthn Spec - Aborting Ceremonies}
*/
class WebAuthnAbortService {
/**
* Create an abort signal for a new WebAuthn operation.
* Automatically cancels any existing operation.
*
* @returns {AbortSignal} Signal to pass to navigator.credentials.create() or .get()
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal MDN - AbortSignal}
*/
createNewAbortSignal() {
// Abort any existing calls to navigator.credentials.create() or navigator.credentials.get()
if (this.controller) {
const abortError = new Error('Cancelling existing WebAuthn API call for new one');
abortError.name = 'AbortError';
this.controller.abort(abortError);
}
const newController = new AbortController();
this.controller = newController;
return newController.signal;
}
/**
* Manually cancel the current WebAuthn operation.
* Useful for cleaning up when user cancels or navigates away.
*
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/AbortController/abort MDN - AbortController.abort}
*/
cancelCeremony() {
if (this.controller) {
const abortError = new Error('Manually cancelling existing WebAuthn API call');
abortError.name = 'AbortError';
this.controller.abort(abortError);
this.controller = undefined;
}
}
}
exports.WebAuthnAbortService = WebAuthnAbortService;
/**
* Singleton instance to ensure only one WebAuthn ceremony is active at a time.
* This prevents "operation already in progress" errors when retrying WebAuthn operations.
*
* @experimental This instance is experimental and may change in future releases
*/
exports.webAuthnAbortService = new WebAuthnAbortService();
/**
* Convert base64url encoded strings in WebAuthn credential creation options to ArrayBuffers
* as required by the WebAuthn browser API.
* Supports both native WebAuthn Level 3 parseCreationOptionsFromJSON and manual fallback.
*
* @param {ServerCredentialCreationOptions} options - JSON options from server with base64url encoded fields
* @returns {PublicKeyCredentialCreationOptionsFuture} Options ready for navigator.credentials.create()
* @see {@link https://w3c.github.io/webauthn/#sctn-parseCreationOptionsFromJSON W3C WebAuthn Spec - parseCreationOptionsFromJSON}
*/
function deserializeCredentialCreationOptions(options) {
if (!options) {
throw new Error('Credential creation options are required');
}
// Check if the native parseCreationOptionsFromJSON method is available
if (typeof PublicKeyCredential !== 'undefined' &&
'parseCreationOptionsFromJSON' in PublicKeyCredential &&
typeof PublicKeyCredential
.parseCreationOptionsFromJSON === 'function') {
// Use the native WebAuthn Level 3 method
return PublicKeyCredential.parseCreationOptionsFromJSON(
/** we assert the options here as typescript still doesn't know about future webauthn types */
options);
}
// Fallback to manual parsing for browsers that don't support the native method
// Destructure to separate fields that need transformation
const { challenge: challengeStr, user: userOpts, excludeCredentials } = options, restOptions = tslib_1.__rest(options
// Convert challenge from base64url to ArrayBuffer
, ["challenge", "user", "excludeCredentials"]);
// Convert challenge from base64url to ArrayBuffer
const challenge = (0, base64url_1.base64UrlToUint8Array)(challengeStr).buffer;
// Convert user.id from base64url to ArrayBuffer
const user = Object.assign(Object.assign({}, userOpts), { id: (0, base64url_1.base64UrlToUint8Array)(userOpts.id).buffer });
// Build the result object
const result = Object.assign(Object.assign({}, restOptions), { challenge,
user });
// Only add excludeCredentials if it exists
if (excludeCredentials && excludeCredentials.length > 0) {
result.excludeCredentials = new Array(excludeCredentials.length);
for (let i = 0; i < excludeCredentials.length; i++) {
const cred = excludeCredentials[i];
result.excludeCredentials[i] = Object.assign(Object.assign({}, cred), { id: (0, base64url_1.base64UrlToUint8Array)(cred.id).buffer, type: cred.type || 'public-key',
// Cast transports to handle future transport types like "cable"
transports: cred.transports });
}
}
return result;
}
/**
* Convert base64url encoded strings in WebAuthn credential request options to ArrayBuffers
* as required by the WebAuthn browser API.
* Supports both native WebAuthn Level 3 parseRequestOptionsFromJSON and manual fallback.
*
* @param {ServerCredentialRequestOptions} options - JSON options from server with base64url encoded fields
* @returns {PublicKeyCredentialRequestOptionsFuture} Options ready for navigator.credentials.get()
* @see {@link https://w3c.github.io/webauthn/#sctn-parseRequestOptionsFromJSON W3C WebAuthn Spec - parseRequestOptionsFromJSON}
*/
function deserializeCredentialRequestOptions(options) {
if (!options) {
throw new Error('Credential request options are required');
}
// Check if the native parseRequestOptionsFromJSON method is available
if (typeof PublicKeyCredential !== 'undefined' &&
'parseRequestOptionsFromJSON' in PublicKeyCredential &&
typeof PublicKeyCredential
.parseRequestOptionsFromJSON === 'function') {
// Use the native WebAuthn Level 3 method
return PublicKeyCredential.parseRequestOptionsFromJSON(options);
}
// Fallback to manual parsing for browsers that don't support the native method
// Destructure to separate fields that need transformation
const { challenge: challengeStr, allowCredentials } = options, restOptions = tslib_1.__rest(options
// Convert challenge from base64url to ArrayBuffer
, ["challenge", "allowCredentials"]);
// Convert challenge from base64url to ArrayBuffer
const challenge = (0, base64url_1.base64UrlToUint8Array)(challengeStr).buffer;
// Build the result object
const result = Object.assign(Object.assign({}, restOptions), { challenge });
// Only add allowCredentials if it exists
if (allowCredentials && allowCredentials.length > 0) {
result.allowCredentials = new Array(allowCredentials.length);
for (let i = 0; i < allowCredentials.length; i++) {
const cred = allowCredentials[i];
result.allowCredentials[i] = Object.assign(Object.assign({}, cred), { id: (0, base64url_1.base64UrlToUint8Array)(cred.id).buffer, type: cred.type || 'public-key',
// Cast transports to handle future transport types like "cable"
transports: cred.transports });
}
}
return result;
}
/**
* Convert a registration/enrollment credential response to server format.
* Serializes binary fields to base64url for JSON transmission.
* Supports both native WebAuthn Level 3 toJSON and manual fallback.
*
* @param {RegistrationCredential} credential - Credential from navigator.credentials.create()
* @returns {RegistrationResponseJSON} JSON-serializable credential for server
* @see {@link https://w3c.github.io/webauthn/#dom-publickeycredential-tojson W3C WebAuthn Spec - toJSON}
*/
function serializeCredentialCreationResponse(credential) {
var _a;
// Check if the credential instance has the toJSON method
if ('toJSON' in credential && typeof credential.toJSON === 'function') {
// Use the native WebAuthn Level 3 method
return credential.toJSON();
}
const credentialWithAttachment = credential;
return {
id: credential.id,
rawId: credential.id,
response: {
attestationObject: (0, base64url_1.bytesToBase64URL)(new Uint8Array(credential.response.attestationObject)),
clientDataJSON: (0, base64url_1.bytesToBase64URL)(new Uint8Array(credential.response.clientDataJSON)),
},
type: 'public-key',
clientExtensionResults: credential.getClientExtensionResults(),
// Convert null to undefined and cast to AuthenticatorAttachment type
authenticatorAttachment: ((_a = credentialWithAttachment.authenticatorAttachment) !== null && _a !== void 0 ? _a : undefined),
};
}
/**
* Convert an authentication/verification credential response to server format.
* Serializes binary fields to base64url for JSON transmission.
* Supports both native WebAuthn Level 3 toJSON and manual fallback.
*
* @param {AuthenticationCredential} credential - Credential from navigator.credentials.get()
* @returns {AuthenticationResponseJSON} JSON-serializable credential for server
* @see {@link https://w3c.github.io/webauthn/#dom-publickeycredential-tojson W3C WebAuthn Spec - toJSON}
*/
function serializeCredentialRequestResponse(credential) {
var _a;
// Check if the credential instance has the toJSON method
if ('toJSON' in credential && typeof credential.toJSON === 'function') {
// Use the native WebAuthn Level 3 method
return credential.toJSON();
}
// Fallback to manual conversion for browsers that don't support toJSON
// Access authenticatorAttachment via type assertion to handle TypeScript version differences
// @simplewebauthn/types includes this property but base TypeScript 4.7.4 doesn't
const credentialWithAttachment = credential;
const clientExtensionResults = credential.getClientExtensionResults();
const assertionResponse = credential.response;
return {
id: credential.id,
rawId: credential.id, // W3C spec expects rawId to match id for JSON format
response: {
authenticatorData: (0, base64url_1.bytesToBase64URL)(new Uint8Array(assertionResponse.authenticatorData)),
clientDataJSON: (0, base64url_1.bytesToBase64URL)(new Uint8Array(assertionResponse.clientDataJSON)),
signature: (0, base64url_1.bytesToBase64URL)(new Uint8Array(assertionResponse.signature)),
userHandle: assertionResponse.userHandle
? (0, base64url_1.bytesToBase64URL)(new Uint8Array(assertionResponse.userHandle))
: undefined,
},
type: 'public-key',
clientExtensionResults,
// Convert null to undefined and cast to AuthenticatorAttachment type
authenticatorAttachment: ((_a = credentialWithAttachment.authenticatorAttachment) !== null && _a !== void 0 ? _a : undefined),
};
}
/**
* A simple test to determine if a hostname is a properly-formatted domain name.
* Considers localhost valid for development environments.
*
* A "valid domain" is defined here: https://url.spec.whatwg.org/#valid-domain
*
* Regex sourced from here:
* https://www.oreilly.com/library/view/regular-expressions-cookbook/9781449327453/ch08s15.html
*
* @param {string} hostname - The hostname to validate
* @returns {boolean} True if valid domain or localhost
* @see {@link https://url.spec.whatwg.org/#valid-domain WHATWG URL Spec - Valid Domain}
*/
function isValidDomain(hostname) {
return (
// Consider localhost valid as well since it's okay wrt Secure Contexts
hostname === 'localhost' || /^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$/i.test(hostname));
}
/**
* Determine if the browser is capable of WebAuthn.
* Checks for necessary Web APIs: PublicKeyCredential and Credential Management.
*
* @returns {boolean} True if browser supports WebAuthn
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/PublicKeyCredential#browser_compatibility MDN - PublicKeyCredential Browser Compatibility}
*/
function browserSupportsWebAuthn() {
var _a, _b;
return !!((0, helpers_1.isBrowser)() &&
'PublicKeyCredential' in window &&
window.PublicKeyCredential &&
'credentials' in navigator &&
typeof ((_a = navigator === null || navigator === void 0 ? void 0 : navigator.credentials) === null || _a === void 0 ? void 0 : _a.create) === 'function' &&
typeof ((_b = navigator === null || navigator === void 0 ? void 0 : navigator.credentials) === null || _b === void 0 ? void 0 : _b.get) === 'function');
}
/**
* Create a WebAuthn credential using the browser's credentials API.
* Wraps navigator.credentials.create() with error handling.
*
* @param {CredentialCreationOptions} options - Options including publicKey parameters
* @returns {Promise<RequestResult<RegistrationCredential, WebAuthnError>>} Created credential or error
* @see {@link https://w3c.github.io/webauthn/#sctn-createCredential W3C WebAuthn Spec - Create Credential}
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/CredentialsContainer/create MDN - credentials.create}
*/
async function createCredential(options) {
try {
const response = await navigator.credentials.create(
/** we assert the type here until typescript types are updated */
options);
if (!response) {
return {
data: null,
error: new webauthn_errors_1.WebAuthnUnknownError('Empty credential response', response),
};
}
if (!(response instanceof PublicKeyCredential)) {
return {
data: null,
error: new webauthn_errors_1.WebAuthnUnknownError('Browser returned unexpected credential type', response),
};
}
return { data: response, error: null };
}
catch (err) {
return {
data: null,
error: (0, webauthn_errors_1.identifyRegistrationError)({
error: err,
options,
}),
};
}
}
/**
* Get a WebAuthn credential using the browser's credentials API.
* Wraps navigator.credentials.get() with error handling.
*
* @param {CredentialRequestOptions} options - Options including publicKey parameters
* @returns {Promise<RequestResult<AuthenticationCredential, WebAuthnError>>} Retrieved credential or error
* @see {@link https://w3c.github.io/webauthn/#sctn-getAssertion W3C WebAuthn Spec - Get Assertion}
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/CredentialsContainer/get MDN - credentials.get}
*/
async function getCredential(options) {
try {
const response = await navigator.credentials.get(
/** we assert the type here until typescript types are updated */
options);
if (!response) {
return {
data: null,
error: new webauthn_errors_1.WebAuthnUnknownError('Empty credential response', response),
};
}
if (!(response instanceof PublicKeyCredential)) {
return {
data: null,
error: new webauthn_errors_1.WebAuthnUnknownError('Browser returned unexpected credential type', response),
};
}
return { data: response, error: null };
}
catch (err) {
return {
data: null,
error: (0, webauthn_errors_1.identifyAuthenticationError)({
error: err,
options,
}),
};
}
}
exports.DEFAULT_CREATION_OPTIONS = {
hints: ['security-key'],
authenticatorSelection: {
authenticatorAttachment: 'cross-platform',
requireResidentKey: false,
/** set to preferred because older yubikeys don't have PIN/Biometric */
userVerification: 'preferred',
residentKey: 'discouraged',
},
attestation: 'direct',
};
exports.DEFAULT_REQUEST_OPTIONS = {
/** set to preferred because older yubikeys don't have PIN/Biometric */
userVerification: 'preferred',
hints: ['security-key'],
attestation: 'direct',
};
function deepMerge(...sources) {
const isObject = (val) => val !== null && typeof val === 'object' && !Array.isArray(val);
const isArrayBufferLike = (val) => val instanceof ArrayBuffer || ArrayBuffer.isView(val);
const result = {};
for (const source of sources) {
if (!source)
continue;
for (const key in source) {
const value = source[key];
if (value === undefined)
continue;
if (Array.isArray(value)) {
// preserve array reference, including unions like AuthenticatorTransport[]
result[key] = value;
}
else if (isArrayBufferLike(value)) {
result[key] = value;
}
else if (isObject(value)) {
const existing = result[key];
if (isObject(existing)) {
result[key] = deepMerge(existing, value);
}
else {
result[key] = deepMerge(value);
}
}
else {
result[key] = value;
}
}
}
return result;
}
/**
* Merges WebAuthn credential creation options with overrides.
* Sets sensible defaults for authenticator selection and extensions.
*
* @param {PublicKeyCredentialCreationOptionsFuture} baseOptions - The base options from the server
* @param {PublicKeyCredentialCreationOptionsFuture} overrides - Optional overrides to apply
* @param {string} friendlyName - Optional friendly name for the credential
* @returns {PublicKeyCredentialCreationOptionsFuture} Merged credential creation options
* @see {@link https://w3c.github.io/webauthn/#dictdef-authenticatorselectioncriteria W3C WebAuthn Spec - AuthenticatorSelectionCriteria}
*/
function mergeCredentialCreationOptions(baseOptions, overrides) {
return deepMerge(exports.DEFAULT_CREATION_OPTIONS, baseOptions, overrides || {});
}
/**
* Merges WebAuthn credential request options with overrides.
* Sets sensible defaults for user verification and hints.
*
* @param {PublicKeyCredentialRequestOptionsFuture} baseOptions - The base options from the server
* @param {PublicKeyCredentialRequestOptionsFuture} overrides - Optional overrides to apply
* @returns {PublicKeyCredentialRequestOptionsFuture} Merged credential request options
* @see {@link https://w3c.github.io/webauthn/#dictdef-publickeycredentialrequestoptions W3C WebAuthn Spec - PublicKeyCredentialRequestOptions}
*/
function mergeCredentialRequestOptions(baseOptions, overrides) {
return deepMerge(exports.DEFAULT_REQUEST_OPTIONS, baseOptions, overrides || {});
}
/**
* WebAuthn API wrapper for Supabase Auth.
* Provides methods for enrolling, challenging, verifying, authenticating, and registering WebAuthn credentials.
*
* @experimental This API is experimental and may change in future releases
* @see {@link https://w3c.github.io/webauthn/ W3C WebAuthn Specification}
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Web_Authentication_API MDN - Web Authentication API}
*/
class WebAuthnApi {
constructor(client) {
this.client = client;
// Bind all methods so they can be destructured
this.enroll = this._enroll.bind(this);
this.challenge = this._challenge.bind(this);
this.verify = this._verify.bind(this);
this.authenticate = this._authenticate.bind(this);
this.register = this._register.bind(this);
}
/**
* Enroll a new WebAuthn factor.
* Creates an unverified WebAuthn factor that must be verified with a credential.
*
* @experimental This method is experimental and may change in future releases
* @param {Omit<MFAEnrollWebauthnParams, 'factorType'>} params - Enrollment parameters (friendlyName required)
* @returns {Promise<AuthMFAEnrollWebauthnResponse>} Enrolled factor details or error
* @see {@link https://w3c.github.io/webauthn/#sctn-registering-a-new-credential W3C WebAuthn Spec - Registering a New Credential}
*/
async _enroll(params) {
return this.client.mfa.enroll(Object.assign(Object.assign({}, params), { factorType: 'webauthn' }));
}
/**
* Challenge for WebAuthn credential creation or authentication.
* Combines server challenge with browser credential operations.
* Handles both registration (create) and authentication (request) flows.
*
* @experimental This method is experimental and may change in future releases
* @param {MFAChallengeWebauthnParams & { friendlyName?: string; signal?: AbortSignal }} params - Challenge parameters including factorId
* @param {Object} overrides - Allows you to override the parameters passed to navigator.credentials
* @param {PublicKeyCredentialCreationOptionsFuture} overrides.create - Override options for credential creation
* @param {PublicKeyCredentialRequestOptionsFuture} overrides.request - Override options for credential request
* @returns {Promise<RequestResult>} Challenge response with credential or error
* @see {@link https://w3c.github.io/webauthn/#sctn-credential-creation W3C WebAuthn Spec - Credential Creation}
* @see {@link https://w3c.github.io/webauthn/#sctn-verifying-assertion W3C WebAuthn Spec - Verifying Assertion}
*/
async _challenge({ factorId, webauthn, friendlyName, signal, }, overrides) {
var _a;
try {
// Get challenge from server using the client's MFA methods
const { data: challengeResponse, error: challengeError } = await this.client.mfa.challenge({
factorId,
webauthn,
});
if (!challengeResponse) {
return { data: null, error: challengeError };
}
const abortSignal = signal !== null && signal !== void 0 ? signal : exports.webAuthnAbortService.createNewAbortSignal();
/** webauthn will fail if either of the name/displayname are blank */
if (challengeResponse.webauthn.type === 'create') {
const { user } = challengeResponse.webauthn.credential_options.publicKey;
if (!user.name) {
// Preserve original format: use friendlyName if provided, otherwise fetch fallback
// This maintains backward compatibility with the ${user.id}:${name} format
const nameToUse = friendlyName;
if (!nameToUse) {
// Only fetch user data if friendlyName is not provided (bug fix for null friendlyName)
const currentUser = await this.client.getUser();
const userData = currentUser.data.user;
const fallbackName = ((_a = userData === null || userData === void 0 ? void 0 : userData.user_metadata) === null || _a === void 0 ? void 0 : _a.name) || (userData === null || userData === void 0 ? void 0 : userData.email) || (userData === null || userData === void 0 ? void 0 : userData.id) || 'User';
user.name = `${user.id}:${fallbackName}`;
}
else {
user.name = `${user.id}:${nameToUse}`;
}
}
if (!user.displayName) {
user.displayName = user.name;
}
}
switch (challengeResponse.webauthn.type) {
case 'create': {
const options = mergeCredentialCreationOptions(challengeResponse.webauthn.credential_options.publicKey, overrides === null || overrides === void 0 ? void 0 : overrides.create);
const { data, error } = await createCredential({
publicKey: options,
signal: abortSignal,
});
if (data) {
return {
data: {
factorId,
challengeId: challengeResponse.id,
webauthn: {
type: challengeResponse.webauthn.type,
credential_response: data,
},
},
error: null,
};
}
return { data: null, error };
}
case 'request': {
const options = mergeCredentialRequestOptions(challengeResponse.webauthn.credential_options.publicKey, overrides === null || overrides === void 0 ? void 0 : overrides.request);
const { data, error } = await getCredential(Object.assign(Object.assign({}, challengeResponse.webauthn.credential_options), { publicKey: options, signal: abortSignal }));
if (data) {
return {
data: {
factorId,
challengeId: challengeResponse.id,
webauthn: {
type: challengeResponse.webauthn.type,
credential_response: data,
},
},
error: null,
};
}
return { data: null, error };
}
}
}
catch (error) {
if ((0, errors_1.isAuthError)(error)) {
return { data: null, error };
}
return {
data: null,
error: new errors_1.AuthUnknownError('Unexpected error in challenge', error),
};
}
}
/**
* Verify a WebAuthn credential with the server.
* Completes the WebAuthn ceremony by sending the credential to the server for verification.
*
* @experimental This method is experimental and may change in future releases
* @param {Object} params - Verification parameters
* @param {string} params.challengeId - ID of the challenge being verified
* @param {string} params.factorId - ID of the WebAuthn factor
* @param {MFAVerifyWebauthnParams<T>['webauthn']} params.webauthn - WebAuthn credential response
* @returns {Promise<AuthMFAVerifyResponse>} Verification result with session or error
* @see {@link https://w3c.github.io/webauthn/#sctn-verifying-assertion W3C WebAuthn Spec - Verifying an Authentication Assertion}
* */
async _verify({ challengeId, factorId, webauthn, }) {
return this.client.mfa.verify({
factorId,
challengeId,
webauthn: webauthn,
});
}
/**
* Complete WebAuthn authentication flow.
* Performs challenge and verification in a single operation for existing credentials.
*
* @experimental This method is experimental and may change in future releases
* @param {Object} params - Authentication parameters
* @param {string} params.factorId - ID of the WebAuthn factor to authenticate with
* @param {Object} params.webauthn - WebAuthn configuration
* @param {string} params.webauthn.rpId - Relying Party ID (defaults to current hostname)
* @param {string[]} params.webauthn.rpOrigins - Allowed origins (defaults to current origin)
* @param {AbortSignal} params.webauthn.signal - Optional abort signal
* @param {PublicKeyCredentialRequestOptionsFuture} overrides - Override options for navigator.credentials.get
* @returns {Promise<RequestResult<AuthMFAVerifyResponseData, WebAuthnError | AuthError>>} Authentication result
* @see {@link https://w3c.github.io/webauthn/#sctn-authentication W3C WebAuthn Spec - Authentication Ceremony}
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/PublicKeyCredentialRequestOptions MDN - PublicKeyCredentialRequestOptions}
*/
async _authenticate({ factorId, webauthn: { rpId = typeof window !== 'undefined' ? window.location.hostname : undefined, rpOrigins = typeof window !== 'undefined' ? [window.location.origin] : undefined, signal, } = {}, }, overrides) {
if (!rpId) {
return {
data: null,
error: new errors_1.AuthError('rpId is required for WebAuthn authentication'),
};
}
try {
if (!browserSupportsWebAuthn()) {
return {
data: null,
error: new errors_1.AuthUnknownError('Browser does not support WebAuthn', null),
};
}
// Get challenge and credential
const { data: challengeResponse, error: challengeError } = await this.challenge({
factorId,
webauthn: { rpId, rpOrigins },
signal,
}, { request: overrides });
if (!challengeResponse) {
return { data: null, error: challengeError };
}
const { webauthn } = challengeResponse;
// Verify credential
return this._verify({
factorId,
challengeId: challengeResponse.challengeId,
webauthn: {
type: webauthn.type,
rpId,
rpOrigins,
credential_response: webauthn.credential_response,
},
});
}
catch (error) {
if ((0, errors_1.isAuthError)(error)) {
return { data: null, error };
}
return {
data: null,
error: new errors_1.AuthUnknownError('Unexpected error in authenticate', error),
};
}
}
/**
* Complete WebAuthn registration flow.
* Performs enrollment, challenge, and verification in a single operation for new credentials.
*
* @experimental This method is experimental and may change in future releases
* @param {Object} params - Registration parameters
* @param {string} params.friendlyName - User-friendly name for the credential
* @param {string} params.rpId - Relying Party ID (defaults to current hostname)
* @param {string[]} params.rpOrigins - Allowed origins (defaults to current origin)
* @param {AbortSignal} params.signal - Optional abort signal
* @param {PublicKeyCredentialCreationOptionsFuture} overrides - Override options for navigator.credentials.create
* @returns {Promise<RequestResult<AuthMFAVerifyResponseData, WebAuthnError | AuthError>>} Registration result
* @see {@link https://w3c.github.io/webauthn/#sctn-registering-a-new-credential W3C WebAuthn Spec - Registration Ceremony}
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/PublicKeyCredentialCreationOptions MDN - PublicKeyCredentialCreationOptions}
*/
async _register({ friendlyName, webauthn: { rpId = typeof window !== 'undefined' ? window.location.hostname : undefined, rpOrigins = typeof window !== 'undefined' ? [window.location.origin] : undefined, signal, } = {}, }, overrides) {
if (!rpId) {
return {
data: null,
error: new errors_1.AuthError('rpId is required for WebAuthn registration'),
};
}
try {
if (!browserSupportsWebAuthn()) {
return {
data: null,
error: new errors_1.AuthUnknownError('Browser does not support WebAuthn', null),
};
}
// Enroll factor
const { data: factor, error: enrollError } = await this._enroll({
friendlyName,
});
if (!factor) {
await this.client.mfa
.listFactors()
.then((factors) => {
var _a;
return (_a = factors.data) === null || _a === void 0 ? void 0 : _a.all.find((v) => v.factor_type === 'webauthn' &&
v.friendly_name === friendlyName &&
v.status !== 'unverified');
})
.then((factor) => (factor ? this.client.mfa.unenroll({ factorId: factor === null || factor === void 0 ? void 0 : factor.id }) : void 0));
return { data: null, error: enrollError };
}
// Get challenge and create credential
const { data: challengeResponse, error: challengeError } = await this._challenge({
factorId: factor.id,
friendlyName: factor.friendly_name,
webauthn: { rpId, rpOrigins },
signal,
}, {
create: overrides,
});
if (!challengeResponse) {
return { data: null, error: challengeError };
}
return this._verify({
factorId: factor.id,
challengeId: challengeResponse.challengeId,
webauthn: {
rpId,
rpOrigins,
type: challengeResponse.webauthn.type,
credential_response: challengeResponse.webauthn.credential_response,
},
});
}
catch (error) {
if ((0, errors_1.isAuthError)(error)) {
return { data: null, error };
}
return {
data: null,
error: new errors_1.AuthUnknownError('Unexpected error in register', error),
};
}
}
}
exports.WebAuthnApi = WebAuthnApi;
//# sourceMappingURL=webauthn.js.map