UNPKG

blockstack

Version:

The Blockstack Javascript library for identity and authentication.

270 lines (235 loc) 10.5 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.generateAndStoreTransitKey = generateAndStoreTransitKey; exports.getTransitKey = getTransitKey; exports.isUserSignedIn = isUserSignedIn; exports.redirectToSignInWithAuthRequest = redirectToSignInWithAuthRequest; exports.redirectToSignIn = redirectToSignIn; exports.getAuthResponseToken = getAuthResponseToken; exports.isSignInPending = isSignInPending; exports.handlePendingSignIn = handlePendingSignIn; exports.loadUserData = loadUserData; exports.signUserOut = signUserOut; var _queryString = require('query-string'); var _queryString2 = _interopRequireDefault(_queryString); var _jsontokens = require('jsontokens'); var _index = require('./index'); var _customProtocolDetectionBlockstack = require('custom-protocol-detection-blockstack'); var _customProtocolDetectionBlockstack2 = _interopRequireDefault(_customProtocolDetectionBlockstack); var _utils = require('../utils'); var _index2 = require('../index'); var _authMessages = require('./authMessages'); var _authConstants = require('./authConstants'); var _storage = require('../storage'); var _profiles = require('../profiles'); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var DEFAULT_PROFILE = { '@type': 'Person', '@context': 'http://schema.org' /** * Generates a ECDSA keypair and stores the hex value of the private key in * local storage. * @return {String} the hex encoded private key * @private */ };function generateAndStoreTransitKey() { var transitKey = (0, _index2.makeECPrivateKey)(); localStorage.setItem(_authConstants.BLOCKSTACK_APP_PRIVATE_KEY_LABEL, transitKey); return transitKey; } /** * Fetches the hex value of the transit private key from local storage. * @return {String} the hex encoded private key * @private */ function getTransitKey() { return localStorage.getItem(_authConstants.BLOCKSTACK_APP_PRIVATE_KEY_LABEL); } /** * Check if a user is currently signed in. * @return {Boolean} `true` if the user is signed in, `false` if not. */ function isUserSignedIn() { return !!window.localStorage.getItem(_authConstants.BLOCKSTACK_STORAGE_LABEL); } /** * Redirects the user to the Blockstack browser to approve the sign in request * given. * * The user is redirected to the `blockstackIDHost` if the `blockstack:` * 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} blockstackIDHost - the URL to redirect the user to if the blockstack * protocol handler is not detected * @return {void} */ function redirectToSignInWithAuthRequest() { var authRequest = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : (0, _index.makeAuthRequest)(); var blockstackIDHost = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _authConstants.DEFAULT_BLOCKSTACK_HOST; var protocolURI = _utils.BLOCKSTACK_HANDLER + ':' + authRequest; var httpsURI = blockstackIDHost + '?authRequest=' + authRequest; function successCallback() { console.log('protocol handler detected'); // protocolCheck should open the link for us } function failCallback() { console.log('protocol handler not detected'); window.location = httpsURI; } function unsupportedBrowserCallback() { // Safari is unsupported by protocolCheck console.log('can not detect custom protocols on this browser'); window.location = protocolURI; } (0, _customProtocolDetectionBlockstack2.default)(protocolURI, failCallback, successCallback, unsupportedBrowserCallback); } /** * Generates an authentication request and redirects the user to the Blockstack * browser to approve the sign in request. * * Please note that this requires that the web browser properly handles the * `blockstack:` 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 `generateAndStoreTransitKey`, `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() { var redirectURI = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : window.location.origin + '/'; var manifestURI = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : window.location.origin + '/manifest.json'; var scopes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _authConstants.DEFAULT_SCOPE; var authRequest = (0, _index.makeAuthRequest)(generateAndStoreTransitKey(), redirectURI, manifestURI, scopes); redirectToSignInWithAuthRequest(authRequest); } /** * Retrieve the authentication token from the URL query * @return {String} the authentication token if it exists otherwise `null` */ function getAuthResponseToken() { var queryDict = _queryString2.default.parse(location.search); return queryDict.authResponse ? queryDict.authResponse : null; } /** * Check if there is a authentication request that hasn't been handled. * @return {Boolean} `true` if there is a pending sign in, otherwise `false` */ function isSignInPending() { return !!getAuthResponseToken(); } /** * 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 * * @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() { var nameLookupURL = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'https://core.blockstack.org/v1/names/'; var authResponseToken = getAuthResponseToken(); return new Promise(function (resolve, reject) { (0, _index.verifyAuthResponse)(authResponseToken, nameLookupURL).then(function (isValid) { if (isValid) { var tokenPayload = (0, _jsontokens.decodeToken)(authResponseToken).payload; // TODO: real version handling var appPrivateKey = tokenPayload.private_key; var coreSessionToken = tokenPayload.core_token; if ((0, _utils.isLaterVersion)(tokenPayload.version, '1.1.0')) { var transitKey = getTransitKey(); if (transitKey !== undefined && transitKey != null) { if (appPrivateKey !== undefined && appPrivateKey !== null) { try { appPrivateKey = (0, _authMessages.decryptPrivateKey)(transitKey, appPrivateKey); } catch (e) { console.log('Failed decryption of appPrivateKey, will try to use as given'); } } if (coreSessionToken !== undefined && coreSessionToken !== null) { try { coreSessionToken = (0, _authMessages.decryptPrivateKey)(transitKey, coreSessionToken); } catch (e) { console.log('Failed decryption of coreSessionToken, will try to use as given'); } } } } var hubUrl = _authConstants.BLOCKSTACK_DEFAULT_GAIA_HUB_URL; if ((0, _utils.isLaterVersion)(tokenPayload.version, '1.2.0') && tokenPayload.hubUrl !== null && tokenPayload.hubUrl !== undefined) { hubUrl = tokenPayload.hubUrl; } var userData = { username: tokenPayload.username, profile: tokenPayload.profile, appPrivateKey: appPrivateKey, coreSessionToken: coreSessionToken, authResponseToken: authResponseToken, hubUrl: hubUrl }; var profileURL = tokenPayload.profile_url; if ((userData.profile === null || userData.profile === undefined) && profileURL !== undefined && profileURL !== null) { fetch(profileURL).then(function (response) { if (!response.ok) { // return blank profile if we fail to fetch userData.profile = Object.assign({}, DEFAULT_PROFILE); window.localStorage.setItem(_authConstants.BLOCKSTACK_STORAGE_LABEL, JSON.stringify(userData)); resolve(userData); } else { response.text().then(function (responseText) { return JSON.parse(responseText); }).then(function (wrappedProfile) { return (0, _profiles.extractProfile)(wrappedProfile[0].token); }).then(function (profile) { userData.profile = profile; window.localStorage.setItem(_authConstants.BLOCKSTACK_STORAGE_LABEL, JSON.stringify(userData)); resolve(userData); }); } }); } else { userData.profile = tokenPayload.profile; window.localStorage.setItem(_authConstants.BLOCKSTACK_STORAGE_LABEL, JSON.stringify(userData)); resolve(userData); } } else { reject(); } }); }); } /** * Retrieves the user data object. The user's profile is stored in the key `profile`. * @return {Object} User data object. */ function loadUserData() { return JSON.parse(window.localStorage.getItem(_authConstants.BLOCKSTACK_STORAGE_LABEL)); } /** * Sign the user out and optionally redirect to given location. * @param {String} [redirectURL=null] Location to redirect user to after sign out. * @return {void} */ function signUserOut() { var redirectURL = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; window.localStorage.removeItem(_authConstants.BLOCKSTACK_STORAGE_LABEL); window.localStorage.removeItem(_storage.BLOCKSTACK_GAIA_HUB_LABEL); if (redirectURL !== null) { window.location = redirectURL; } }