UNPKG

@toruslabs/customauth

Version:

CustomAuth login with torus to get user private key

216 lines (212 loc) 8.37 kB
'use strict'; var base64url = require('base64url'); var Bowser = require('bowser'); var enums = require('./enums.js'); var loglevel = require('./loglevel.js'); function eventToPromise(emitter) { return new Promise((resolve, reject) => { const handler = ev => { const { error = "", data } = ev; emitter.removeEventListener("message", handler); if (error) return reject(new Error(error)); return resolve(data); }; emitter.addEventListener("message", handler); }); } // These are the default connection names used by auth0 const loginToConnectionMap = { [enums.AUTH_CONNECTION.APPLE]: "apple", [enums.AUTH_CONNECTION.GITHUB]: "github", [enums.AUTH_CONNECTION.LINKEDIN]: "linkedin", [enums.AUTH_CONNECTION.TWITTER]: "twitter", [enums.AUTH_CONNECTION.LINE]: "line", [enums.AUTH_CONNECTION.EMAIL_PASSWORDLESS]: "email", [enums.AUTH_CONNECTION.SMS_PASSWORDLESS]: "sms" }; const padUrlString = url => url.href.endsWith("/") ? url.href : `${url.href}/`; /** * Returns a random number. Don't use for cryptographic purposes. * @returns a random number */ const randomId = () => Math.random().toString(36).slice(2); 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, isCaseSensitive) { return isCaseSensitive ? field : field.toLowerCase(); } const getUserId = (userInfo, authConnection, userIdField, isUserIdCaseSensitive = true) => { const { name, sub } = userInfo; if (userIdField) return caseSensitiveField(userInfo[userIdField], isUserIdCaseSensitive); switch (authConnection) { case enums.AUTH_CONNECTION.EMAIL_PASSWORDLESS: case enums.AUTH_CONNECTION.SMS_PASSWORDLESS: case enums.AUTH_CONNECTION.AUTHENTICATOR: return caseSensitiveField(name, isUserIdCaseSensitive); case enums.AUTH_CONNECTION.GITHUB: case enums.AUTH_CONNECTION.TWITTER: case enums.AUTH_CONNECTION.APPLE: case enums.AUTH_CONNECTION.LINKEDIN: case enums.AUTH_CONNECTION.LINE: case enums.AUTH_CONNECTION.WECHAT: case enums.AUTH_CONNECTION.KAKAO: case enums.AUTH_CONNECTION.FARCASTER: case enums.AUTH_CONNECTION.CUSTOM: return caseSensitiveField(sub, isUserIdCaseSensitive); default: throw new Error("Invalid login type to get auth connection id"); } }; const handleRedirectParameters = (hash, queryParameters) => { const hashParameters = hash.split("&").reduce((result, item) => { const [part0, part1] = item.split("="); result[part0] = part1; return result; }, {}); loglevel.info(hashParameters, queryParameters); let instanceParameters = {}; let error = ""; if (Object.keys(hashParameters).length > 0 && hashParameters.state) { instanceParameters = JSON.parse(base64url.decode(decodeURIComponent(decodeURIComponent(hashParameters.state)))) || {}; error = hashParameters.error_description || hashParameters.error || error; } else if (Object.keys(queryParameters).length > 0 && queryParameters.state) { instanceParameters = JSON.parse(base64url.decode(decodeURIComponent(decodeURIComponent(queryParameters.state)))) || {}; if (queryParameters.error) error = queryParameters.error; } return { error, instanceParameters, hashParameters }; }; function storageAvailable(type) { let storage; try { storage = window[type]; const x = "__storage_test__"; storage.setItem(x, x); storage.removeItem(x); return true; } catch (error) { const e = error; 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; } } function getPopupFeatures() { // 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; } const isFirefox = () => { var _window; return ((_window = window) === null || _window === void 0 || (_window = _window.navigator) === null || _window === void 0 ? void 0 : _window.userAgent.toLowerCase().indexOf("firefox")) > -1 || false; }; function constructURL(params) { const { baseURL, query, hash } = params; const url = new URL(baseURL); if (query) { Object.keys(query).forEach(key => { url.searchParams.append(key, query[key]); }); } 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; // } const validateAndConstructUrl = domain => { try { const url = new URL(decodeURIComponent(domain)); return url; } catch (error) { throw new Error(`${(error === null || error === void 0 ? void 0 : error.message) || ""}, Note: Your jwt domain: (i.e ${domain}) must have http:// or https:// prefix`); } }; const objectToAuthDataMap = tgAuthenticationResult => { return JSON.parse(base64url.decode(tgAuthenticationResult)); }; function isMobileOrTablet() { const browser = Bowser.getParser(navigator.userAgent); const platform = browser.getPlatform(); return platform.type === Bowser.PLATFORMS_MAP.tablet || platform.type === Bowser.PLATFORMS_MAP.mobile; } function getTimeout(authConnection) { if ((authConnection === enums.AUTH_CONNECTION.FACEBOOK || authConnection === enums.AUTH_CONNECTION.LINE) && isMobileOrTablet()) { return 1000 * 30; // 30 seconds to finish the login } return 1000 * 1; // 1 second } function decodeToken(token) { const [header, payload] = token.split("."); return { header: JSON.parse(base64url.decode(header)), payload: JSON.parse(base64url.decode(payload)) }; } exports.broadcastChannelOptions = broadcastChannelOptions; exports.constructURL = constructURL; exports.decodeToken = decodeToken; exports.eventToPromise = eventToPromise; exports.getPopupFeatures = getPopupFeatures; exports.getTimeout = getTimeout; exports.getUserId = getUserId; exports.handleRedirectParameters = handleRedirectParameters; exports.isFirefox = isFirefox; exports.isMobileOrTablet = isMobileOrTablet; exports.loginToConnectionMap = loginToConnectionMap; exports.objectToAuthDataMap = objectToAuthDataMap; exports.padUrlString = padUrlString; exports.randomId = randomId; exports.storageAvailable = storageAvailable; exports.validateAndConstructUrl = validateAndConstructUrl;