golos-lib-js
Version:
Golos-js the JavaScript library with API for GOLOS blockchain
230 lines (228 loc) • 6.52 kB
JavaScript
;
var _debug = _interopRequireDefault(require("debug"));
var _config = _interopRequireDefault(require("../config"));
var _ecc = require("../auth/ecc");
var _fetchEx = _interopRequireDefault(require("../utils/fetchEx"));
var _keychain = require("./keychain");
var _misc = require("../utils/misc");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
let _apiHost;
let _uiHost;
let _clientId;
let _oauthNode, _oauthChainId;
const debug = (0, _debug.default)('golos:multiauth');
const AuthType = {
OAUTH: 1,
KEYCHAIN: 2
};
function apiHost() {
if (_apiHost) return _apiHost;
const KEY = 'oauth.host';
let host = _config.default.get(KEY);
if (!host) throw new Error(KEY + ' is not set in golos.config');
if (!host.startsWith('http://') && !host.startsWith('https://')) host = 'https://' + host;
try {
_apiHost = new URL(host).origin;
} catch (err) {
throw new Error(KEY + ' cannot be parsed as URL: ' + host);
}
return _apiHost;
}
function uiHost() {
if (_uiHost) return _uiHost;
const KEY = 'oauth.ui_host';
let host = _config.default.get(KEY);
if (!host) {
_uiHost = apiHost();
return _uiHost;
}
if (!host.startsWith('http://') && !host.startsWith('https://')) host = 'https://' + host;
try {
_uiHost = new URL(host).origin;
} catch (err) {
throw new Error(KEY + ' cannot be parsed as URL: ' + host);
}
return _uiHost;
}
function clientId() {
if (_clientId) return _clientId;
const KEY = 'oauth.client';
let client = _config.default.get(KEY);
if (!client) throw new Error(KEY + ' is not set in golos.config');
_clientId = client;
return _clientId;
}
// Keychain only signs transactions, but not sends them
// and we cannot use OAuth host if we are using KeyChain
// so we should use our node to send them
function setKeychainNodeIfCan() {
const signedNode = _config.default.get('signed.websocket');
if (signedNode) {
_oauthNode = _config.default.get('websocket');
_config.default.set('websocket', signedNode);
}
const signedCid = _config.default.get('signed.chain_id');
if (signedCid) {
_oauthChainId = _config.default.get('chain_id');
_config.default.set('chain_id', signedCid);
}
}
function switchBackToOAuth() {
if (_oauthNode) {
_config.default.set('websocket', _oauthNode);
}
if (_oauthChainId) {
_config.default.set('chain_id', _oauthChainId);
}
}
function _callApi(url, data) {
let getHost = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : apiHost;
let request = {
method: data ? 'post' : 'get',
credentials: 'include',
headers: {
Accept: 'application/json',
'Content-type': data ? 'application/json' : undefined
},
body: data ? JSON.stringify(data) : undefined
};
return (0, _fetchEx.default)(new URL(url, getHost()), request);
}
async function check() {
const resKC = await (0, _keychain.kcLoggedIn)();
if (resKC.authorized) {
setKeychainNodeIfCan();
return {
...resKC,
authType: AuthType.KEYCHAIN
};
}
let res = await _callApi('/api/oauth/check/' + clientId());
res = await res.json();
return {
...res,
authType: AuthType.OAUTH
};
}
async function checkReliable() {
let onError = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : undefined;
let retry = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1;
let res = null;
try {
res = await check();
} catch (err) {
if (onError && onError({
retry
})) {
throw err;
}
if (!onError) {
if (retry > 10) {
console.error('multiauth.check stopped retrying');
throw err;
}
console.error('multiauth.check error:', err, 'retrying...');
}
await (0, _misc.delay)(3000);
res = await checkReliable(onError, ++retry);
}
return res;
}
const waiters = new Set();
let waiterAutoInc = 0;
const stopped = (id, onFail) => {
if (!waiters.has(id)) {
onFail();
return true;
}
return false;
};
async function waitingLoop(id, onFinish, onFail) {
let retries = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 180;
let onRetry = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : undefined;
if (!retries) {
console.error('waiting for login is timeouted');
onFail();
return;
}
if (stopped(id, onFail)) return;
const res = await checkReliable();
if (stopped(id, onFail)) return;
if (res.authorized) {
onFinish(res);
} else {
if (onRetry && onRetry({
retriesLeft: retries
})) {
onFail();
return;
}
debug('waiting for login...');
setTimeout(() => {
waitingLoop(id, onFinish, onFail, --retries, onRetry);
}, 1000);
}
}
async function waitForLogin(onFinish, onFail) {
let retries = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 180;
let onRetry = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : undefined;
++waiterAutoInc;
waiters.clear();
waiters.add(waiterAutoInc);
await waitingLoop(waiterAutoInc, onFinish, onFail, --retries, onRetry);
}
async function login() {
let permissions = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
let {
type = AuthType.OAUTH,
extraParams = ''
} = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
if (typeof document === 'undefined') {
throw new Error('MultiAuth works only in browser environment (window should be defined)');
}
if (type === AuthType.KEYCHAIN) {
await (0, _keychain.assertKc)();
await (0, _keychain.kcLogin)();
return true;
}
return window.open(uiHost() + '/oauth/' + clientId() + '/' + permissions.join(',') + extraParams);
}
async function logout() {
if (!(await (0, _keychain.kcLogout)())) {
const res = await _callApi('/api/oauth/logout/' + clientId());
} else {
switchBackToOAuth();
}
}
function _hashOps(operations) {
let json = JSON.stringify(operations);
const idHash = _ecc.hash.sha256(json, 'hex');
return idHash;
}
async function keychainInfo() {
let info = {
installed: false,
loadingBroken: false
};
try {
info.installed = await (0, _keychain.waitForKc)();
} catch (err) {
console.error(err);
info.loadingBroken = true;
}
return info;
}
module.exports = {
clientId,
apiHost,
uiHost,
check,
checkReliable,
AuthType,
login,
waitForLogin,
logout,
keychainInfo,
_callApi,
_hashOps
};