UNPKG

@oraichain/customauth

Version:

CustomAuth login with torus to get user private key

214 lines (185 loc) 7.52 kB
import Bowser from "bowser"; import { serializeError } from "serialize-error"; import { Auth0UserInfo, MultifactorsGenericObject } from "../handlers/interfaces"; import { LOGIN, LOGIN_TYPE, REDIRECT_PARAMS_STORAGE_METHOD_TYPE } from "./enums"; import log from "./loglevel"; interface CustomMessageEvent extends MessageEvent { error: string; } interface EventListener { (evt: CustomMessageEvent): void; } type EmitterType = { addEventListener(type: string, handler: EventListener): void; removeEventListener(type: string, handler: EventListener): void }; export function eventToPromise<T>(emitter: EmitterType): Promise<T> { return new Promise<T>((resolve, reject) => { const handler = (ev: CustomMessageEvent) => { const { error = "", data } = ev; emitter.removeEventListener("message", handler); if (error) return reject(new Error(error)); return resolve(data as T); }; emitter.addEventListener("message", handler); }); } // These are the connection names used by auth0 export const loginToConnectionMap = { [LOGIN.APPLE]: "apple", [LOGIN.GITHUB]: "github", [LOGIN.LINKEDIN]: "linkedin", [LOGIN.TWITTER]: "twitter", [LOGIN.WEIBO]: "weibo", [LOGIN.LINE]: "line", [LOGIN.EMAIL_PASSWORD]: "Username-Password-Authentication", [LOGIN.PASSWORDLESS]: "email", }; export const padUrlString = (url: URL): string => (url.href.endsWith("/") ? url.href : `${url.href}/`); /** * Returns a random number. Don't use for cryptographic purposes. * @returns a random number */ export const randomId = (): string => Math.random().toString(36).slice(2); export const broadcastChannelOptions = { // type: 'localstorage', // (optional) enforce a type, oneOf['native', 'idb', 'localstorage', 'node'] webWorkerSupport: false, // (optional) set this to false if you know that your channel will never be used in a WebWorker (increases performance) }; function caseSensitiveField(field: string, isCaseSensitive?: boolean): string { return isCaseSensitive ? field : field.toLowerCase(); } export const getVerifierId = ( userInfo: Auth0UserInfo, typeOfLogin: LOGIN_TYPE, verifierIdField?: string, isVerifierIdCaseSensitive = true ): string => { const { name, email } = userInfo; if (verifierIdField) return caseSensitiveField(userInfo[verifierIdField], isVerifierIdCaseSensitive); switch (typeOfLogin) { case LOGIN.PASSWORDLESS: case LOGIN.EMAIL_PASSWORD: return caseSensitiveField(name, isVerifierIdCaseSensitive); case LOGIN.WEIBO: case LOGIN.GITHUB: case LOGIN.TWITTER: case LOGIN.APPLE: case LOGIN.LINKEDIN: case LOGIN.LINE: case LOGIN.JWT: return caseSensitiveField(email, isVerifierIdCaseSensitive); default: throw new Error("Invalid login type"); } }; export const handleRedirectParameters = ( hash: string, queryParameters: MultifactorsGenericObject ): { error: string; instanceParameters: MultifactorsGenericObject; hashParameters: MultifactorsGenericObject } => { const hashParameters: MultifactorsGenericObject = hash.split("&").reduce((result, item) => { const [part0, part1] = item.split("="); result[part0] = part1; return result; }, {}); log.info(hashParameters, queryParameters); let instanceParameters: MultifactorsGenericObject = {}; let error = ""; if (Object.keys(hashParameters).length > 0 && hashParameters.state) { instanceParameters = JSON.parse(atob(decodeURIComponent(decodeURIComponent(hashParameters.state)))) || {}; error = hashParameters.error_description || hashParameters.error || error; } else if (Object.keys(queryParameters).length > 0 && queryParameters.state) { instanceParameters = JSON.parse(atob(decodeURIComponent(decodeURIComponent(queryParameters.state)))) || {}; if (queryParameters.error) error = queryParameters.error; } return { error, instanceParameters, hashParameters }; }; export function storageAvailable(type: REDIRECT_PARAMS_STORAGE_METHOD_TYPE): boolean { let storage: Storage; try { storage = window[type]; const x = "__storage_test__"; storage.setItem(x, x); storage.removeItem(x); return true; } catch (e) { return ( e && // everything except Firefox (e.code === 22 || // Firefox e.code === 1014 || // test name field too, because code might not be present // everything except Firefox e.name === "QuotaExceededError" || // Firefox e.name === "NS_ERROR_DOM_QUOTA_REACHED") && // acknowledge QuotaExceededError only if there's something already stored storage && storage.length !== 0 ); } } export function getPopupFeatures(): string { // Fixes dual-screen position Most browsers Firefox const dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : window.screenX; const dualScreenTop = window.screenTop !== undefined ? window.screenTop : window.screenY; const w = 1200; const h = 700; const width = window.innerWidth ? window.innerWidth : document.documentElement.clientWidth ? document.documentElement.clientWidth : window.screen.width; const height = window.innerHeight ? window.innerHeight : document.documentElement.clientHeight ? document.documentElement.clientHeight : window.screen.height; const systemZoom = 1; // No reliable estimate const left = Math.abs((width - w) / 2 / systemZoom + dualScreenLeft); const top = Math.abs((height - h) / 2 / systemZoom + dualScreenTop); const features = `titlebar=0,toolbar=0,status=0,location=0,menubar=0,height=${h / systemZoom},width=${w / systemZoom},top=${top},left=${left}`; return features; } export const isFirefox = (): boolean => window?.navigator?.userAgent.toLowerCase().indexOf("firefox") > -1 || false; export function constructURL(params: { baseURL: string; query?: Record<string, unknown>; hash?: Record<string, unknown> }): string { const { baseURL, query, hash } = params; const url = new URL(baseURL); if (query) { Object.keys(query).forEach((key) => { url.searchParams.append(key, query[key] as string); }); } if (hash) { const h = new URL(constructURL({ baseURL, query: hash })).searchParams.toString(); url.hash = h; } return url.toString(); } export function are3PCSupported(): boolean { const browserInfo = Bowser.parse(navigator.userAgent); log.info(JSON.stringify(browserInfo), "current browser info"); let thirdPartyCookieSupport = true; // brave if ((navigator as unknown as { brave: boolean })?.brave) { thirdPartyCookieSupport = false; } // All webkit & gecko engine instances use itp (intelligent tracking prevention - // https://webkit.org/tracking-prevention/#intelligent-tracking-prevention-itp) if (browserInfo.engine.name === Bowser.ENGINE_MAP.WebKit || browserInfo.engine.name === Bowser.ENGINE_MAP.Gecko) { thirdPartyCookieSupport = false; } return thirdPartyCookieSupport; } export const validateAndConstructUrl = (domain: string): URL => { try { const url = new URL(decodeURIComponent(domain)); return url; } catch (error) { throw new Error(`${error?.message || ""}, Note: Your jwt domain: (i.e ${domain}) must have http:// or https:// prefix`); } }; export const wait = (s: number) => new Promise<void>((resolve) => { setTimeout(resolve, s * 1000); }); export function stringtifyError(error: any): string { return JSON.stringify(serializeError(error)); }