UNPKG

@supabase/gotrue-js

Version:

Official client library for Supabase Auth

324 lines 12.1 kB
"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.parseResponseAPIVersion = exports.getCodeChallengeAndMethod = exports.generatePKCEChallenge = exports.generatePKCEVerifier = exports.retryable = exports.sleep = exports.decodeJWTPayload = exports.Deferred = exports.decodeBase64URL = exports.removeItemAsync = exports.getItemAsync = exports.setItemAsync = exports.looksLikeFetchResponse = exports.resolveFetch = exports.parseParametersFromURL = exports.supportsLocalStorage = exports.isBrowser = exports.uuid = exports.expiresAt = void 0; const constants_1 = require("./constants"); 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); }); } exports.uuid = uuid; const isBrowser = () => typeof document !== 'undefined'; exports.isBrowser = isBrowser; const localStorageWriteTests = { tested: false, writable: false, }; /** * Checks whether localStorage is supported on this browser. */ const supportsLocalStorage = () => { if (!(0, exports.isBrowser)()) { return false; } try { if (typeof globalThis.localStorage !== 'object') { return false; } } catch (e) { // DOM exception when accessing `localStorage` return false; } if (localStorageWriteTests.tested) { return localStorageWriteTests.writable; } const randomKey = `lswt-${Math.random()}${Math.random()}`; try { globalThis.localStorage.setItem(randomKey, randomKey); globalThis.localStorage.removeItem(randomKey); localStorageWriteTests.tested = true; localStorageWriteTests.writable = true; } catch (e) { // localStorage can't be written to // https://www.chromium.org/for-testers/bug-reporting-guidelines/uncaught-securityerror-failed-to-read-the-localstorage-property-from-window-access-is-denied-for-this-document localStorageWriteTests.tested = true; localStorageWriteTests.writable = false; } return localStorageWriteTests.writable; }; exports.supportsLocalStorage = supportsLocalStorage; /** * Extracts parameters encoded in the URL both in the query and fragment. */ function parseParametersFromURL(href) { const result = {}; const url = new URL(href); if (url.hash && url.hash[0] === '#') { try { const hashSearchParams = new URLSearchParams(url.hash.substring(1)); hashSearchParams.forEach((value, key) => { result[key] = value; }); } catch (e) { // hash is not a query string } } // search parameters take precedence over hash parameters url.searchParams.forEach((value, key) => { result[key] = value; }); return result; } exports.parseParametersFromURL = parseParametersFromURL; const resolveFetch = (customFetch) => { let _fetch; if (customFetch) { _fetch = customFetch; } 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); }; exports.resolveFetch = resolveFetch; const looksLikeFetchResponse = (maybeResponse) => { return (typeof maybeResponse === 'object' && maybeResponse !== null && 'status' in maybeResponse && 'ok' in maybeResponse && 'json' in maybeResponse && typeof maybeResponse.json === 'function'); }; exports.looksLikeFetchResponse = looksLikeFetchResponse; // Storage helpers const setItemAsync = async (storage, key, data) => { await storage.setItem(key, JSON.stringify(data)); }; exports.setItemAsync = setItemAsync; const getItemAsync = async (storage, key) => { const value = await storage.getItem(key); if (!value) { return null; } try { return JSON.parse(value); } catch (_a) { return value; } }; exports.getItemAsync = getItemAsync; const removeItemAsync = async (storage, key) => { await storage.removeItem(key); }; exports.removeItemAsync = removeItemAsync; function decodeBase64URL(value) { const key = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; let base64 = ''; let chr1, chr2, chr3; let enc1, enc2, enc3, enc4; let i = 0; value = value.replace('-', '+').replace('_', '/'); while (i < value.length) { enc1 = key.indexOf(value.charAt(i++)); enc2 = key.indexOf(value.charAt(i++)); enc3 = key.indexOf(value.charAt(i++)); enc4 = key.indexOf(value.charAt(i++)); chr1 = (enc1 << 2) | (enc2 >> 4); chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); chr3 = ((enc3 & 3) << 6) | enc4; base64 = base64 + String.fromCharCode(chr1); if (enc3 != 64 && chr2 != 0) { base64 = base64 + String.fromCharCode(chr2); } if (enc4 != 64 && chr3 != 0) { base64 = base64 + String.fromCharCode(chr3); } } return base64; } exports.decodeBase64URL = decodeBase64URL; /** * A deferred represents some asynchronous work that is not yet finished, which * may or may not culminate in a value. * Taken from: https://github.com/mike-north/types/blob/master/src/async.ts */ class Deferred { constructor() { // eslint-disable-next-line @typescript-eslint/no-extra-semi ; this.promise = new Deferred.promiseConstructor((res, rej) => { // eslint-disable-next-line @typescript-eslint/no-extra-semi ; this.resolve = res; this.reject = rej; }); } } exports.Deferred = Deferred; Deferred.promiseConstructor = Promise; // Taken from: https://stackoverflow.com/questions/38552003/how-to-decode-jwt-token-in-javascript-without-using-a-library function decodeJWTPayload(token) { // Regex checks for base64url format const base64UrlRegex = /^([a-z0-9_-]{4})*($|[a-z0-9_-]{3}=?$|[a-z0-9_-]{2}(==)?$)$/i; const parts = token.split('.'); if (parts.length !== 3) { throw new Error('JWT is not valid: not a JWT structure'); } if (!base64UrlRegex.test(parts[1])) { throw new Error('JWT is not valid: payload is not in base64url format'); } const base64Url = parts[1]; return JSON.parse(decodeBase64URL(base64Url)); } exports.decodeJWTPayload = decodeJWTPayload; /** * Creates a promise that resolves to null after some time. */ async function sleep(time) { return await new Promise((accept) => { 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 * if the function should run again. */ function retryable(fn, isRetryable) { const promise = new Promise((accept, reject) => { // eslint-disable-next-line @typescript-eslint/no-extra-semi ; (async () => { for (let attempt = 0; attempt < Infinity; attempt++) { try { const result = await fn(attempt); if (!isRetryable(attempt, null, result)) { accept(result); return; } } catch (e) { if (!isRetryable(attempt, e)) { reject(e); return; } } } })(); }); return promise; } exports.retryable = retryable; function dec2hex(dec) { return ('0' + dec.toString(16)).substr(-2); } // Functions below taken from: https://stackoverflow.com/questions/63309409/creating-a-code-verifier-and-challenge-for-pkce-auth-on-spotify-api-in-reactjs function generatePKCEVerifier() { const verifierLength = 56; const array = new Uint32Array(verifierLength); if (typeof crypto === 'undefined') { const charSet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~'; const charSetLen = charSet.length; let verifier = ''; for (let i = 0; i < verifierLength; i++) { verifier += charSet.charAt(Math.floor(Math.random() * charSetLen)); } return verifier; } 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); const hash = await crypto.subtle.digest('SHA-256', encodedData); const bytes = new Uint8Array(hash); return Array.from(bytes) .map((c) => String.fromCharCode(c)) .join(''); } function base64urlencode(str) { return btoa(str).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''); } async function generatePKCEChallenge(verifier) { const hasCryptoSupport = typeof crypto !== 'undefined' && typeof crypto.subtle !== 'undefined' && typeof TextEncoder !== 'undefined'; if (!hasCryptoSupport) { console.warn('WebCrypto API is not supported. Code challenge method will default to use plain instead of sha256.'); return verifier; } const hashed = await sha256(verifier); return base64urlencode(hashed); } exports.generatePKCEChallenge = generatePKCEChallenge; async function getCodeChallengeAndMethod(storage, storageKey, isPasswordRecovery = false) { const codeVerifier = generatePKCEVerifier(); let storedCodeVerifier = codeVerifier; if (isPasswordRecovery) { storedCodeVerifier += '/PASSWORD_RECOVERY'; } await (0, exports.setItemAsync)(storage, `${storageKey}-code-verifier`, storedCodeVerifier); const codeChallenge = await generatePKCEChallenge(codeVerifier); 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) { const apiVersion = response.headers.get(constants_1.API_VERSION_HEADER_NAME); if (!apiVersion) { return null; } if (!apiVersion.match(API_VERSION_REGEX)) { return null; } try { const date = new Date(`${apiVersion}T00:00:00.0Z`); return date; } catch (e) { return null; } } exports.parseResponseAPIVersion = parseResponseAPIVersion; //# sourceMappingURL=helpers.js.map