@toruslabs/customauth
Version:
CustomAuth login with torus to get user private key
216 lines (212 loc) • 8.37 kB
JavaScript
;
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;