aladinnetwork-blockstack
Version:
The Aladin Javascript library for authentication, identity, and storage.
326 lines • 15.1 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const query_string_1 = __importDefault(require("query-string"));
// @ts-ignore: Could not find a declaration file for module
const jsontokens_1 = require("jsontokens");
const authVerification_1 = require("./authVerification");
const utils_1 = require("../utils");
const fetchUtil_1 = require("../fetchUtil");
const dids_1 = require("../dids");
const errors_1 = require("../errors");
const authMessages_1 = require("./authMessages");
const authConstants_1 = require("./authConstants");
const profileTokens_1 = require("../profiles/profileTokens");
const userSession_1 = require("./userSession");
const config_1 = require("../config");
const logger_1 = require("../logger");
const protocolEchoDetection_1 = require("./protocolEchoDetection");
const protocolLaunch_1 = require("./protocolLaunch");
const DEFAULT_PROFILE = {
'@type': 'Person',
'@context': 'http://schema.org'
};
/**
* @deprecated
* #### v19 Use [[UserSession.isUserSignedIn]] instead.
*
* Check if a user is currently signed in.
* @return {Boolean} `true` if the user is signed in, `false` if not.
*/
function isUserSignedIn() {
console.warn('DEPRECATION WARNING: The static isUserSignedIn() function will be deprecated in '
+ 'the next major release of aladin.js. Create an instance of UserSession and call the '
+ 'instance method isUserSignedIn().');
const userSession = new userSession_1.UserSession();
return userSession.isUserSignedIn();
}
exports.isUserSignedIn = isUserSignedIn;
/**
*
*
* @deprecated
* #### v19 Use [[UserSession.isUserSignedIn]] instead.
*
* Generates an authentication request and redirects the user to the Aladin
* browser to approve the sign in request.
*
* Please note that this requires that the web browser properly handles the
* `aladin:` URL protocol handler.
*
* Most applications should use this
* method for sign in unless they require more fine grained control over how the
* authentication request is generated. If your app falls into this category,
* use `makeAuthRequest` and `redirectToSignInWithAuthRequest` to build your own sign in process.
*
* @param {String} [redirectURI=`${window.location.origin}/`]
* The location to which the identity provider will redirect the user after
* the user approves sign in.
* @param {String} [manifestURI=`${window.location.origin}/manifest.json`]
* Location of the manifest file.
* @param {Array} [scopes=DEFAULT_SCOPE] Defaults to requesting write access to
* this app's data store.
* An array of strings indicating which permissions this app is requesting.
* @return {void}
*/
function redirectToSignIn(redirectURI, manifestURI, scopes) {
console.warn('DEPRECATION WARNING: The static redirectToSignIn() function will be deprecated in the '
+ 'next major release of aladin.js. Create an instance of UserSession and call the '
+ 'instance method redirectToSignIn().');
const authRequest = authMessages_1.makeAuthRequest(null, redirectURI, manifestURI, scopes);
redirectToSignInWithAuthRequest(authRequest);
}
exports.redirectToSignIn = redirectToSignIn;
/**
* @deprecated
* #### v19 Use [[UserSession.isSignInPending]] instead.
*
* Check if there is a authentication request that hasn't been handled.
*
* Also checks for a protocol echo reply (which if detected then the page
* will be automatically redirected after this call).
*
* @return {Boolean} `true` if there is a pending sign in, otherwise `false`
*/
function isSignInPending() {
try {
const isProtocolEcho = protocolEchoDetection_1.protocolEchoReplyDetection();
if (isProtocolEcho) {
logger_1.Logger.info('protocolEchoReply detected from isSignInPending call, the page is about to redirect.');
return true;
}
}
catch (error) {
logger_1.Logger.error(`Error checking for protocol echo reply isSignInPending: ${error}`);
}
return !!getAuthResponseToken();
}
exports.isSignInPending = isSignInPending;
/**
* @deprecated
* #### v19 Use [[UserSession.getAuthResponseToken]] instead.
*
* Retrieve the authentication token from the URL query
* @return {String} the authentication token if it exists otherwise `null`
*/
function getAuthResponseToken() {
const search = utils_1.getGlobalObject('location', { throwIfUnavailable: true, usageDesc: 'getAuthResponseToken' }).search;
const queryDict = query_string_1.default.parse(search);
return queryDict.authResponse ? queryDict.authResponse : '';
}
exports.getAuthResponseToken = getAuthResponseToken;
/**
* @deprecated
* #### v19 Use [[UserSession.loadUserData]] instead.
*
* Retrieves the user data object. The user's profile is stored in the key `profile`.
* @return {Object} User data object.
*/
function loadUserData() {
console.warn('DEPRECATION WARNING: The static loadUserData() function will be deprecated in the '
+ 'next major release of aladin.js. Create an instance of UserSession and call the '
+ 'instance method loadUserData().');
const userSession = new userSession_1.UserSession();
return userSession.loadUserData();
}
exports.loadUserData = loadUserData;
/**
* @deprecated
* #### v19 Use [[UserSession.signUserOut]] instead.
*
* Sign the user out and optionally redirect to given location.
* @param redirectURL
* Location to redirect user to after sign out.
* Only used in environments with `window` available
*/
function signUserOut(redirectURL, caller) {
const userSession = caller || new userSession_1.UserSession();
userSession.store.deleteSessionData();
if (redirectURL) {
utils_1.getGlobalObject('location', { throwIfUnavailable: true, usageDesc: 'signUserOut' }).href = redirectURL;
}
}
exports.signUserOut = signUserOut;
/**
* @deprecated
* #### v19 Use [[UserSession.redirectToSignInWithAuthRequest]] instead.
*
* Redirects the user to the Aladin browser to approve the sign in request
* given.
*
* The user is redirected to the `aladinIDHost` if the `aladin:`
* protocol handler is not detected. Please note that the protocol handler detection
* does not work on all browsers.
* @param {String} authRequest - the authentication request generated by `makeAuthRequest`
* @param {String} aladinIDHost - the URL to redirect the user to if the aladin
* protocol handler is not detected
* @return {void}
*/
function redirectToSignInWithAuthRequest(authRequest, aladinIDHost = authConstants_1.DEFAULT_ALADIN_HOST) {
authRequest = authRequest || authMessages_1.makeAuthRequest();
const httpsURI = `${aladinIDHost}?authRequest=${authRequest}`;
const { navigator, location } = utils_1.getGlobalObjects(['navigator', 'location'], { throwIfUnavailable: true, usageDesc: 'redirectToSignInWithAuthRequest' });
// If they're on a mobile OS, always redirect them to HTTPS site
if (/Android|webOS|iPhone|iPad|iPod|Opera Mini/i.test(navigator.userAgent)) {
logger_1.Logger.info('detected mobile OS, sending to https');
location.href = httpsURI;
return;
}
function successCallback() {
logger_1.Logger.info('protocol handler detected');
// The detection function should open the link for us
}
function failCallback() {
logger_1.Logger.warn('protocol handler not detected');
location.href = httpsURI;
}
protocolLaunch_1.launchCustomProtocol(authRequest, successCallback, failCallback);
}
exports.redirectToSignInWithAuthRequest = redirectToSignInWithAuthRequest;
/**
* @deprecated
* #### v19 Use [[UserSession.handlePendingSignIn]] instead.
*
* Try to process any pending sign in request by returning a `Promise` that resolves
* to the user data object if the sign in succeeds.
*
* @param {String} nameLookupURL - the endpoint against which to verify public
* keys match claimed username
* @param {String} authResponseToken - the signed authentication response token
* @param {String} transitKey - the transit private key that corresponds to the transit public key
* that was provided in the authentication request
* @return {Promise} that resolves to the user data object if successful and rejects
* if handling the sign in request fails or there was no pending sign in request.
*/
function handlePendingSignIn(nameLookupURL = '', authResponseToken = getAuthResponseToken(), transitKey, caller) {
return __awaiter(this, void 0, void 0, function* () {
try {
const isProtocolEcho = protocolEchoDetection_1.protocolEchoReplyDetection();
if (isProtocolEcho) {
const msg = 'handlePendingSignIn called while protocolEchoReply was detected, and '
+ 'the page is about to redirect. This function will resolve with an error after '
+ 'several seconds, if the page was not redirected for some reason.';
logger_1.Logger.info(msg);
return new Promise((_resolve, reject) => {
setTimeout(() => {
logger_1.Logger.error('Page should have redirected by now. handlePendingSignIn will now throw.');
reject(msg);
}, 3000);
});
}
}
catch (error) {
logger_1.Logger.error(`Error checking for protocol echo reply handlePendingSignIn: ${error}`);
}
if (!caller) {
caller = new userSession_1.UserSession();
}
if (!transitKey) {
transitKey = caller.store.getSessionData().transitKey;
}
if (!nameLookupURL) {
const tokenPayload = jsontokens_1.decodeToken(authResponseToken).payload;
if (utils_1.isLaterVersion(tokenPayload.version, '1.3.0')
&& tokenPayload.aladinAPIUrl !== null && tokenPayload.aladinAPIUrl !== undefined) {
// override globally
logger_1.Logger.info(`Overriding ${config_1.config.network.aladinAPIUrl} `
+ `with ${tokenPayload.aladinAPIUrl}`);
config_1.config.network.aladinAPIUrl = tokenPayload.aladinAPIUrl;
}
nameLookupURL = `${config_1.config.network.aladinAPIUrl}${authConstants_1.NAME_LOOKUP_PATH}`;
}
const isValid = yield authVerification_1.verifyAuthResponse(authResponseToken, nameLookupURL);
if (!isValid) {
throw new errors_1.LoginFailedError('Invalid authentication response.');
}
const tokenPayload = jsontokens_1.decodeToken(authResponseToken).payload;
// TODO: real version handling
let appPrivateKey = tokenPayload.private_key;
let coreSessionToken = tokenPayload.core_token;
if (utils_1.isLaterVersion(tokenPayload.version, '1.1.0')) {
if (transitKey !== undefined && transitKey != null) {
if (tokenPayload.private_key !== undefined && tokenPayload.private_key !== null) {
try {
appPrivateKey = authMessages_1.decryptPrivateKey(transitKey, tokenPayload.private_key);
}
catch (e) {
logger_1.Logger.warn('Failed decryption of appPrivateKey, will try to use as given');
try {
utils_1.hexStringToECPair(tokenPayload.private_key);
}
catch (ecPairError) {
throw new errors_1.LoginFailedError('Failed decrypting appPrivateKey. Usually means'
+ ' that the transit key has changed during login.');
}
}
}
if (coreSessionToken !== undefined && coreSessionToken !== null) {
try {
coreSessionToken = authMessages_1.decryptPrivateKey(transitKey, coreSessionToken);
}
catch (e) {
logger_1.Logger.info('Failed decryption of coreSessionToken, will try to use as given');
}
}
}
else {
throw new errors_1.LoginFailedError('Authenticating with protocol > 1.1.0 requires transit'
+ ' key, and none found.');
}
}
let hubUrl = authConstants_1.ALADIN_DEFAULT_GAIA_HUB_URL;
let gaiaAssociationToken;
if (utils_1.isLaterVersion(tokenPayload.version, '1.2.0')
&& tokenPayload.hubUrl !== null && tokenPayload.hubUrl !== undefined) {
hubUrl = tokenPayload.hubUrl;
}
if (utils_1.isLaterVersion(tokenPayload.version, '1.3.0')
&& tokenPayload.associationToken !== null && tokenPayload.associationToken !== undefined) {
gaiaAssociationToken = tokenPayload.associationToken;
}
const userData = {
username: tokenPayload.username,
profile: tokenPayload.profile,
email: tokenPayload.email,
decentralizedID: tokenPayload.iss,
identityAddress: dids_1.getAddressFromDID(tokenPayload.iss),
appPrivateKey,
coreSessionToken,
authResponseToken,
hubUrl,
gaiaAssociationToken
};
const profileURL = tokenPayload.profile_url;
if (!userData.profile && profileURL) {
const response = yield fetchUtil_1.fetchPrivate(profileURL);
if (!response.ok) { // return blank profile if we fail to fetch
userData.profile = Object.assign({}, DEFAULT_PROFILE);
}
else {
const responseText = yield response.text();
const wrappedProfile = JSON.parse(responseText);
const profile = profileTokens_1.extractProfile(wrappedProfile[0].token);
userData.profile = profile;
}
}
else {
userData.profile = tokenPayload.profile;
}
const sessionData = caller.store.getSessionData();
sessionData.userData = userData;
caller.store.setSessionData(sessionData);
return userData;
});
}
exports.handlePendingSignIn = handlePendingSignIn;
//# sourceMappingURL=authApp.js.map
;