blockstack
Version:
The Blockstack Javascript library for identity and authentication.
270 lines (235 loc) • 10.5 kB
JavaScript
;
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;
}
}