blockstack
Version:
The Blockstack Javascript library for identity and authentication.
1,492 lines (1,296 loc) • 2.15 MB
JavaScript
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.blockstack = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
'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;
}
}
},{"../index":11,"../profiles":13,"../storage":36,"../utils":37,"./authConstants":2,"./authMessages":3,"./index":7,"custom-protocol-detection-blockstack":232,"jsontokens":312,"query-string":401}],2:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var BLOCKSTACK_HANDLER = exports.BLOCKSTACK_HANDLER = 'blockstack';
var BLOCKSTACK_STORAGE_LABEL = exports.BLOCKSTACK_STORAGE_LABEL = 'blockstack';
var DEFAULT_BLOCKSTACK_HOST = exports.DEFAULT_BLOCKSTACK_HOST = 'https://blockstack.org/auth';
var DEFAULT_SCOPE = exports.DEFAULT_SCOPE = ['store_write'];
var BLOCKSTACK_APP_PRIVATE_KEY_LABEL = exports.BLOCKSTACK_APP_PRIVATE_KEY_LABEL = 'blockstack-transit-private-key';
var BLOCKSTACK_DEFAULT_GAIA_HUB_URL = exports.BLOCKSTACK_DEFAULT_GAIA_HUB_URL = 'https://hub.blockstack.org';
},{}],3:[function(require,module,exports){
(function (Buffer){
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.makeAuthRequest = makeAuthRequest;
exports.encryptPrivateKey = encryptPrivateKey;
exports.decryptPrivateKey = decryptPrivateKey;
exports.makeAuthResponse = makeAuthResponse;
var _jsontokens = require('jsontokens');
var _index = require('../index');
var _authConstants = require('./authConstants');
var _encryption = require('../encryption');
require('isomorphic-fetch');
var VERSION = '1.1.0';
/**
* Generates an authentication request that can be sent to the Blockstack
* browser for the user to approve sign in.
* @param {String} [transitPrivateKey=generateAndStoreTransitKey()] - hex encoded transit
* private key
* @param {String} redirectURI - location to redirect user to after sign in approval
* @param {String} manifestURI - location of this app's manifest file
* @param {Array<String>} scopes - the permissions this app is requesting
* @param {String} appDomain - the origin of this app
* @param {Number} expiresAt - the time at which this request is no longer valid
* @return {String} the authentication request
*/
function makeAuthRequest() {
var transitPrivateKey = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : (0, _index.generateAndStoreTransitKey)();
var redirectURI = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : window.location.origin + '/';
var manifestURI = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : window.location.origin + '/manifest.json';
var scopes = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : _authConstants.DEFAULT_SCOPE;
var appDomain = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : window.location.origin;
var expiresAt = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : (0, _index.nextHour)().getTime();
/* Create the payload */
var payload = {
jti: (0, _index.makeUUID4)(),
iat: Math.floor(new Date().getTime() / 1000), // JWT times are in seconds
exp: Math.floor(expiresAt / 1000), // JWT times are in seconds
iss: null,
public_keys: [],
domain_name: appDomain,
manifest_uri: manifestURI,
redirect_uri: redirectURI,
version: VERSION,
do_not_include_profile: true,
supports_hub_url: true,
scopes: scopes
};
console.log('blockstack.js: generating v' + VERSION + ' auth request');
/* Convert the private key to a public key to an issuer */
var publicKey = _jsontokens.SECP256K1Client.derivePublicKey(transitPrivateKey);
payload.public_keys = [publicKey];
var address = (0, _index.publicKeyToAddress)(publicKey);
payload.iss = (0, _index.makeDIDFromAddress)(address);
/* Sign and return the token */
var tokenSigner = new _jsontokens.TokenSigner('ES256k', transitPrivateKey);
var token = tokenSigner.sign(payload);
return token;
}
function encryptPrivateKey(publicKey, privateKey) {
var encryptedObj = (0, _encryption.encryptECIES)(publicKey, privateKey);
var encryptedJSON = JSON.stringify(encryptedObj);
return new Buffer(encryptedJSON).toString('hex');
}
function decryptPrivateKey(privateKey, hexedEncrypted) {
var unhexedString = new Buffer(hexedEncrypted, 'hex').toString();
var encryptedObj = JSON.parse(unhexedString);
return (0, _encryption.decryptECIES)(privateKey, encryptedObj);
}
function makeAuthResponse(privateKey) {
var profile = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var username = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
var metadata = arguments[3];
var coreToken = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : null;
var appPrivateKey = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : null;
var expiresAt = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : (0, _index.nextMonth)().getTime();
var transitPublicKey = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : null;
var hubUrl = arguments.length > 8 && arguments[8] !== undefined ? arguments[8] : null;
/* Convert the private key to a public key to an issuer */
var publicKey = _jsontokens.SECP256K1Client.derivePublicKey(privateKey);
var address = (0, _index.publicKeyToAddress)(publicKey);
/* See if we should encrypt with the transit key */
var privateKeyPayload = appPrivateKey;
var coreTokenPayload = coreToken;
var additionalProperties = {};
if (appPrivateKey !== undefined && appPrivateKey !== null) {
console.log('blockstack.js: generating v' + VERSION + ' auth response');
if (transitPublicKey !== undefined && transitPublicKey !== null) {
privateKeyPayload = encryptPrivateKey(transitPublicKey, appPrivateKey);
if (coreToken !== undefined && coreToken !== null) {
coreTokenPayload = encryptPrivateKey(transitPublicKey, coreToken);
}
}
additionalProperties = {
email: metadata.email ? metadata.email : null,
profile_url: metadata.profileUrl ? metadata.profileUrl : null,
hubUrl: hubUrl,
version: VERSION
};
} else {
console.log('blockstack.js: generating legacy auth response');
}
/* Create the payload */
var payload = Object.assign({}, {
jti: (0, _index.makeUUID4)(),
iat: Math.floor(new Date().getTime() / 1000), // JWT times are in seconds
exp: Math.floor(expiresAt / 1000), // JWT times are in seconds
iss: (0, _index.makeDIDFromAddress)(address),
private_key: privateKeyPayload,
public_keys: [publicKey],
profile: profile,
username: username,
core_token: coreTokenPayload
}, additionalProperties);
/* Sign and return the token */
var tokenSigner = new _jsontokens.TokenSigner('ES256k', privateKey);
return tokenSigner.sign(payload);
}
}).call(this,require("buffer").Buffer)
},{"../encryption":9,"../index":11,"./authConstants":2,"buffer":135,"isomorphic-fetch":305,"jsontokens":312}],4:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getAuthRequestFromURL = getAuthRequestFromURL;
exports.fetchAppManifest = fetchAppManifest;
exports.redirectUserToApp = redirectUserToApp;
var _queryString = require('query-string');
var _queryString2 = _interopRequireDefault(_queryString);
var _jsontokens = require('jsontokens');
var _index = require('../index');
var _utils = require('../utils');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function getAuthRequestFromURL() {
var queryDict = _queryString2.default.parse(location.search);
if (queryDict.authRequest !== null && queryDict.authRequest !== undefined) {
return queryDict.authRequest.split(_utils.BLOCKSTACK_HANDLER + ':').join('');
} else {
return null;
}
}
function fetchAppManifest(authRequest) {
return new Promise(function (resolve, reject) {
if (!authRequest) {
reject('Invalid auth request');
} else {
var payload = (0, _jsontokens.decodeToken)(authRequest).payload;
var manifestURI = payload.manifest_uri;
try {
fetch(manifestURI).then(function (response) {
return response.text();
}).then(function (responseText) {
return JSON.parse(responseText);
}).then(function (responseJSON) {
resolve(responseJSON);
}).catch(function (e) {
console.log(e.stack);
reject('URI request couldn\'t be completed');
});
} catch (e) {
console.log(e.stack);
reject('URI request couldn\'t be completed');
}
}
});
}
function redirectUserToApp(authRequest, authResponse) {
var payload = (0, _jsontokens.decodeToken)(authRequest).payload;
var redirectURI = payload.redirect_uri;
console.log(redirectURI);
if (redirectURI) {
redirectURI = (0, _index.updateQueryStringParameter)(redirectURI, 'authResponse', authResponse);
} else {
throw new Error('Invalid redirect URI');
}
window.location = redirectURI;
}
},{"../index":11,"../utils":37,"jsontokens":312,"query-string":401}],5:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.makeCoreSessionRequest = makeCoreSessionRequest;
exports.sendCoreSessionRequest = sendCoreSessionRequest;
exports.getCoreSession = getCoreSession;
var _jsontokens = require('jsontokens');
var _isomorphicFetch = require('isomorphic-fetch');
var _isomorphicFetch2 = _interopRequireDefault(_isomorphicFetch);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/*
* Create an authentication token to be sent to the Core API server
* in order to generate a Core session JWT.
*
* @param appDomain (String) The unique application identifier (e.g. foo.app, www.foo.com, etc).
* @param appMethods (Array) The list of API methods this application will need.
* @param appPrivateKey (String) The application-specific private key
* @param blockchainId (String|null) This is the blockchain ID of the requester,
*
* @returns a JWT signed by the app's private key
* @private
*/
function makeCoreSessionRequest(appDomain, appMethods, appPrivateKey) {
var blockchainID = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;
var thisDevice = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : null;
if (thisDevice === null) {
thisDevice = '.default';
}
// TODO: multi-device
var appPublicKey = _jsontokens.SECP256K1Client.derivePublicKey(appPrivateKey);
var appPublicKeys = [{
public_key: appPublicKey,
device_id: thisDevice
}];
var authBody = {
version: 1,
blockchain_id: blockchainID,
app_private_key: appPrivateKey,
app_domain: appDomain,
methods: appMethods,
app_public_keys: appPublicKeys,
device_id: thisDevice
// make token
};var tokenSigner = new _jsontokens.TokenSigner('ES256k', appPrivateKey);
var token = tokenSigner.sign(authBody);
return token;
}
/*
* Send Core a request for a session token.
*
* @param coreAuthRequest (String) a signed JWT encoding the authentication request
* @param apiPassword (String) the API password for Core
*
* Returns a JWT signed with the Core API server's private key that authorizes the bearer
* to carry out the requested operations.
* @private
*/
function sendCoreSessionRequest(coreHost, corePort, coreAuthRequest, apiPassword) {
return new Promise(function (resolve, reject) {
if (!apiPassword) {
reject('Missing API password');
return null;
}
var options = {
headers: {
Authorization: 'bearer ' + apiPassword
}
};
var url = 'http://' + coreHost + ':' + corePort + '/v1/auth?authRequest=' + coreAuthRequest;
return (0, _isomorphicFetch2.default)(url, options).then(function (response) {
if (!response.ok) {
reject('HTTP status not OK');
return null;
}
return response.text();
}).then(function (responseText) {
return JSON.parse(responseText);
}).then(function (responseJson) {
var token = responseJson.token;
if (!token) {
reject('Failed to get Core session token');
return null;
}
resolve(token);
return token;
}).catch(function (error) {
console.error(error);
reject('Invalid Core response: not JSON');
});
});
}
/*
* Get a core session token. Generate an auth request, sign it, send it to Core,
* and get back a session token.
*
* @param coreHost (String) Core API server's hostname
* @param corePort (Integer) Core API server's port number
* @param appPrivateKey (String) Application's private key
* @param blockchainId (String|null) blockchain ID of the user signing in.
* `null` if user has no blockchain ID
*
* Returns a Promise that resolves to a Core session token.
* @private
*/
function getCoreSession(coreHost, corePort, apiPassword, appPrivateKey) {
var blockchainId = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : null;
var authRequest = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : null;
var deviceId = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : '0';
if (!authRequest) {
return Promise.reject('No authRequest provided');
}
var payload = null;
var authRequestObject = null;
try {
authRequestObject = (0, _jsontokens.decodeToken)(authRequest);
if (!authRequestObject) {
return Promise.reject('Invalid authRequest in URL query string');
}
if (!authRequestObject.payload) {
return Promise.reject('Invalid authRequest in URL query string');
}
payload = authRequestObject.payload;
} catch (e) {
console.error(e.stack);
return Promise.reject('Failed to parse authRequest in URL');
}
var appDomain = payload.domain_name;
if (!appDomain) {
return Promise.reject('No domain_name in authRequest');
}
var appMethods = payload.scopes;
var coreAuthRequest = makeCoreSessionRequest(appDomain, appMethods, appPrivateKey, blockchainId, deviceId);
return sendCoreSessionRequest(coreHost, corePort, coreAuthRequest, apiPassword);
}
},{"isomorphic-fetch":305,"jsontokens":312}],6:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.doSignaturesMatchPublicKeys = doSignaturesMatchPublicKeys;
exports.doPublicKeysMatchIssuer = doPublicKeysMatchIssuer;
exports.doPublicKeysMatchUsername = doPublicKeysMatchUsername;
exports.isIssuanceDateValid = isIssuanceDateValid;
exports.isExpirationDateValid = isExpirationDateValid;
exports.isManifestUriValid = isManifestUriValid;
exports.isRedirectUriValid = isRedirectUriValid;
exports.verifyAuthRequest = verifyAuthRequest;
exports.verifyAuthRequestAndLoadManifest = verifyAuthRequestAndLoadManifest;
exports.verifyAuthResponse = verifyAuthResponse;
var _jsontokens = require('jsontokens');
var _index = require('../index');
function doSignaturesMatchPublicKeys(token) {
var payload = (0, _jsontokens.decodeToken)(token).payload;
var publicKeys = payload.public_keys;
if (publicKeys.length === 1) {
var publicKey = publicKeys[0];
try {
var tokenVerifier = new _jsontokens.TokenVerifier('ES256k', publicKey);
var signatureVerified = tokenVerifier.verify(token);
if (signatureVerified) {
return true;
} else {
return false;
}
} catch (e) {
return false;
}
} else {
throw new Error('Multiple public keys are not supported');
}
}
function doPublicKeysMatchIssuer(token) {
var payload = (0, _jsontokens.decodeToken)(token).payload;
var publicKeys = payload.public_keys;
var addressFromIssuer = (0, _index.getAddressFromDID)(payload.iss);
if (publicKeys.length === 1) {
var addressFromPublicKeys = (0, _index.publicKeyToAddress)(publicKeys[0]);
if (addressFromPublicKeys === addressFromIssuer) {
return true;
}
} else {
throw new Error('Multiple public keys are not supported');
}
return false;
}
function doPublicKeysMatchUsername(token, nameLookupURL) {
return new Promise(function (resolve) {
var payload = (0, _jsontokens.decodeToken)(token).payload;
if (!payload.username) {
resolve(true);
return;
}
if (payload.username === null) {
resolve(true);
return;
}
if (nameLookupURL === null) {
resolve(false);
return;
}
var username = payload.username;
var url = nameLookupURL.replace(/\/$/, '') + '/' + username;
try {
fetch(url).then(function (response) {
return response.text();
}).then(function (responseText) {
return JSON.parse(responseText);
}).then(function (responseJSON) {
if (responseJSON.hasOwnProperty('address')) {
var nameOwningAddress = responseJSON.address;
var addressFromIssuer = (0, _index.getAddressFromDID)(payload.iss);
if (nameOwningAddress === addressFromIssuer) {
resolve(true);
} else {
resolve(false);
}
} else {
resolve(false);
}
}).catch(function () {
resolve(false);
});
} catch (e) {
resolve(false);
}
});
}
function isIssuanceDateValid(token) {
var payload = (0, _jsontokens.decodeToken)(token).payload;
if (payload.iat) {
if (typeof payload.iat !== 'number') {
return false;
}
var issuedAt = new Date(payload.iat * 1000); // JWT times are in seconds
if (new Date().getTime() < issuedAt.getTime()) {
return false;
} else {
return true;
}
} else {
return true;
}
}
function isExpirationDateValid(token) {
var payload = (0, _jsontokens.decodeToken)(token).payload;
if (payload.exp) {
if (typeof payload.exp !== 'number') {
return false;
}
var expiresAt = new Date(payload.exp * 1000); // JWT times are in seconds
if (new Date().getTime() > expiresAt.getTime()) {
return false;
} else {
return true;
}
} else {
return true;
}
}
function isManifestUriValid(token) {
var payload = (0, _jsontokens.decodeToken)(token).payload;
return (0, _index.isSameOriginAbsoluteUrl)(payload.domain_name, payload.manifest_uri);
}
function isRedirectUriValid(token) {
var payload = (0, _jsontokens.decodeToken)(token).payload;
return (0, _index.isSameOriginAbsoluteUrl)(payload.domain_name, payload.redirect_uri);
}
/**
* Verify authentication request is valid
* @param {String} token [description]
* @return {Promise} that resolves to true if the auth request
* is valid and false if it does not
* @private
*/
function verifyAuthRequest(token) {
return new Promise(function (resolve, reject) {
if ((0, _jsontokens.decodeToken)(token).header.alg === 'none') {
reject('Token must be signed in order to be verified');
}
Promise.all([isExpirationDateValid(token), isIssuanceDateValid(token), doSignaturesMatchPublicKeys(token), doPublicKeysMatchIssuer(token), isManifestUriValid(token), isRedirectUriValid(token)]).then(function (values) {
if (values.every(Boolean)) {
resolve(true);
} else {
resolve(false);
}
});
});
}
/**
* Verify the authentication response is valid and
* fetch the app manifest file if valid. Otherwise, reject the promise.
* @param {String} token the authentication request token
* @return {Promise} that resolves to the app manifest file in JSON format
* or rejects if the auth request or app manifest file is invalid
* @private
*/
function verifyAuthRequestAndLoadManifest(token) {
return new Promise(function (resolve, reject) {
return verifyAuthRequest(token).then(function (valid) {
if (valid) {
return (0, _index.fetchAppManifest)(token).then(function (appManifest) {
resolve(appManifest);
});
} else {
reject();
return Promise.reject();
}
});
});
}
/**
* Verify the authentication response is valid
* @param {String} token the authentication response token
* @param {String} nameLookupURL the url use to verify owner of a username
* @return {Promise} that resolves to true if auth response
* is valid and false if it does not
*/
function verifyAuthResponse(token, nameLookupURL) {
return new Promise(function (resolve) {
Promise.all([isExpirationDateValid(token), isIssuanceDateValid(token), doSignaturesMatchPublicKeys(token), doPublicKeysMatchIssuer(token), doPublicKeysMatchUsername(token, nameLookupURL)]).then(function (values) {
if (values.every(Boolean)) {
resolve(true);
} else {
resolve(false);
}
});
});
}
},{"../index":11,"jsontokens":312}],7:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _authApp = require('./authApp');
Object.defineProperty(exports, 'isUserSignedIn', {
enumerable: true,
get: function get() {
return _authApp.isUserSignedIn;
}
});
Object.defineProperty(exports, 'redirectToSignIn', {
enumerable: true,
get: function get() {
return _authApp.redirectToSignIn;
}
});
Object.defineProperty(exports, 'redirectToSignInWithAuthRequest', {
enumerable: true,
get: function get() {
return _authApp.redirectToSignInWithAuthRequest;
}
});
Object.defineProperty(exports, 'getAuthResponseToken', {
enumerable: true,
get: function get() {
return _authApp.getAuthResponseToken;
}
});
Object.defineProperty(exports, 'isSignInPending', {
enumerable: true,
get: function get() {
return _authApp.isSignInPending;
}
});
Object.defineProperty(exports, 'handlePendingSignIn', {
enumerable: true,
get: function get() {
return _authApp.handlePendingSignIn;
}
});
Object.defineProperty(exports, 'loadUserData', {
enumerable: true,
get: function get() {
return _authApp.loadUserData;
}
});
Object.defineProperty(exports, 'signUserOut', {
enumerable: true,
get: function get() {
return _authApp.signUserOut;
}
});
Object.defineProperty(exports, 'generateAndStoreTransitKey', {
enumerable: true,
get: function get() {
return _authApp.generateAndStoreTransitKey;
}
});
Object.defineProperty(exports, 'getTransitKey', {
enumerable: true,
get: function get() {
return _authApp.getTransitKey;
}
});
var _authMessages = require('./authMessages');
Object.defineProperty(exports, 'makeAuthRequest', {
enumerable: true,
get: function get() {
return _authMessages.makeAuthRequest;
}
});
Object.defineProperty(exports, 'makeAuthResponse', {
enumerable: true,
get: function get() {
return _authMessages.makeAuthResponse;
}
});
var _authProvider = require('./authProvider');
Object.defineProperty(exports, 'getAuthRequestFromURL', {
enumerable: true,
get: function get() {
return _authProvider.getAuthRequestFromURL;
}
});
Object.defineProperty(exports, 'fetchAppManifest', {
enumerable: true,
get: function get() {
return _authProvider.fetchAppManifest;
}
});
Object.defineProperty(exports, 'redirectUserToApp', {
enumerable: true,
get: function get() {
return _authProvider.redirectUserToApp;
}
});
var _authSession = require('./authSession');
Object.defineProperty(exports, 'makeCoreSessionRequest', {
enumerable: true,
get: function get() {
return _authSession.makeCoreSessionRequest;
}
});
Object.defineProperty(exports, 'sendCoreSessionRequest', {
enumerable: true,
get: function get() {
return _authSession.sendCoreSessionRequest;
}
});
Object.defineProperty(exports, 'getCoreSession', {
enumerable: true,
get: function get() {
return _authSession.getCoreSession;
}
});
var _authVerification = require('./authVerification');
Object.defineProperty(exports, 'verifyAuthRequest', {
enumerable: true,
get: function get() {
return _authVerification.verifyAuthRequest;
}
});
Object.defineProperty(exports, 'verifyAuthResponse', {
enumerable: true,
get: function get() {
return _authVerification.verifyAuthResponse;
}
});
Object.defineProperty(exports, 'isExpirationDateValid', {
enumerable: true,
get: function get() {
return _authVerification.isExpirationDateValid;
}
});
Object.defineProperty(exports, 'isIssuanceDateValid', {
enumerable: true,
get: function get() {
return _authVerification.isIssuanceDateValid;
}
});
Object.defineProperty(exports, 'doPublicKeysMatchUsername', {
enumerable: true,
get: function get() {
return _authVerification.doPublicKeysMatchUsername;
}
});
Object.defineProperty(exports, 'doPublicKeysMatchIssuer', {
enumerable: true,
get: function get() {
return _authVerification.doPublicKeysMatchIssuer;
}
});
Object.defineProperty(exports, 'doSignaturesMatchPublicKeys', {
enumerable: true,
get: function get() {
return _authVerification.doSignaturesMatchPublicKeys;
}
});
Object.defineProperty(exports, 'isManifestUriValid', {
enumerable: true,
get: function get() {
return _authVerification.isManifestUriValid;
}
});
Object.defineProperty(exports, 'isRedirectUriValid', {
enumerable: true,
get: function get() {
return _authVerification.isRedirectUriValid;
}
});
Object.defineProperty(exports, 'verifyAuthRequestAndLoadManifest', {
enumerable: true,
get: function get() {
return _authVerification.verifyAuthRequestAndLoadManifest;
}
});
},{"./authApp":1,"./authMessages":3,"./authProvider":4,"./authSession":5,"./authVerification":6}],8:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.makeDIDFromAddress = makeDIDFromAddress;
exports.makeDIDFromPublicKey = makeDIDFromPublicKey;
exports.getDIDType = getDIDType;
exports.getAddressFromDID = getAddressFromDID;
var _errors = require('./errors');
function makeDIDFromAddress(address) {
return 'did:btc-addr:' + address;
}
function makeDIDFromPublicKey(publicKey) {
return 'did:ecdsa-pub:' + publicKey;
}
function getDIDType(decentralizedID) {
var didParts = decentralizedID.split(':');
if (didParts.length !== 3) {
throw new _errors.InvalidDIDError('Decentralized IDs must have 3 parts');
}
if (didParts[0].toLowerCase() !== 'did') {
throw new _errors.InvalidDIDError('Decentralized IDs must start with "did"');
}
return didParts[1].toLowerCase();
}
function getAddressFromDID(decentralizedID) {
var didType = getDIDType(decentralizedID);
if (didType === 'btc-addr') {
return decentralizedID.split(':')[2];
} else {
return null;
}
}
/*
export function getPublicKeyOrAddressFromDID(decentralizedID) {
const didParts = decentralizedID.split(':')
if (didParts.length !== 3) {
throw new InvalidDIDError('Decentralized IDs must have 3 parts')
}
if (didParts[0].toLowerCase() !== 'did') {
throw new InvalidDIDError('Decentralized IDs must start with "did"')
}
if (didParts[1].toLowerCase() === 'ecdsa-pub') {
return didParts[2]
} else if (didParts[1].toLowerCase() === 'btc-addr') {
return didParts[2]
} else {
throw new InvalidDIDError('Decentralized ID format not supported')
}
}
*/
},{"./errors":10}],9:[function(require,module,exports){
(function (Buffer){
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getHexFromBN = getHexFromBN;
exports.encryptECIES = encryptECIES;
exports.decryptECIES = decryptECIES;
var _elliptic = require('elliptic');
var _crypto = require('crypto');
var _crypto2 = _interopRequireDefault(_crypto);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var ecurve = new _elliptic.ec('secp256k1');
function aes256CbcEncrypt(iv, key, plaintext) {
var cipher = _crypto2.default.createCipheriv('aes-256-cbc', key, iv);
return Buffer.concat([cipher.update(plaintext), cipher.final()]);
}
function aes256CbcDecrypt(iv, key, ciphertext) {
var cipher = _crypto2.default.createDecipheriv('aes-256-cbc', key, iv);
return Buffer.concat([cipher.update(ciphertext), cipher.final()]);
}
function hmacSha256(key, content) {
return _crypto2.default.createHmac('sha256', key).update(content).digest();
}
function equalConstTime(b1, b2) {
if (b1.length !== b2.length) {
return false;
}
var res = 0;
for (var i = 0; i < b1.length; i++) {
res |= b1[i] ^ b2[i]; // jshint ignore:line
}
return res === 0;
}
function sharedSecretToKeys(sharedSecret) {
// generate mac and encryption key from shared secret
var hashedSecret = _crypto2.default.createHash('sha512').update(sharedSecret).digest();
return { encryptionKey: hashedSecret.slice(0, 32),
hmacKey: hashedSecret.slice(32) };
}
function getHexFromBN(bnInput) {
var hexOut = bnInput.toString('hex');
if (hexOut.length === 64) {
return hexOut;
} else if (hexOut.length < 64) {
// pad with leading zeros
// the padStart function would require node 9
var padding = '0'.repeat(64 - hexOut.length);
return '' + padding + hexOut;
} else {
throw new Error('Generated a > 32-byte BN for encryption. Failing.');
}
}
/**
* Encrypt content to elliptic curve publicKey using ECIES
* @param {String} publicKey - secp256k1 public key hex string
* @param {String | Buffer} content - content to encrypt
* @return {Object} Object containing (hex encoded):
* iv (initialization vector), cipherText (cipher text),
* mac (message authentication code), ephemeral public key
* wasString (boolean indicating with or not to return a buffer or string on decrypt)
*/
function encryptECIES(publicKey, content) {
var isString = typeof content === 'string';
var plainText = new Buffer(content); // always copy to buffer
var ecPK = ecurve.keyFromPublic(publicKey, 'hex').getPublic();
var ephemeralSK = ecurve.genKeyPair();
var ephemeralPK = ephemeralSK.getPublic();
var sharedSecret = ephemeralSK.derive(ecPK);
var sharedSecretHex = getHexFromBN(sharedSecret);
var sharedKeys = sharedSecretToKeys(new Buffer(sharedSecretHex, 'hex'));
var initializationVector = _crypto2.default.randomBytes(16);
var cipherText = aes256CbcEncrypt(initializationVector, sharedKeys.encryptionKey, plainText);
var macData = Buffer.concat([initializationVector, new Buffer(ephemeralPK.encodeCompressed()), cipherText]);
var mac = hmacSha256(sharedKeys.hmacKey, macData);
return { iv: initializationVector.toString('hex'),
ephemeralPK: ephemeralPK.encodeCompressed('hex'),
cipherText: cipherText.toString('hex'),
mac: mac.toString('hex'),
wasString: isString };
}
/**
* Decrypt content encrypted using ECIES
* @param {String} privateKey - secp256k1 private key hex string
* @param {Object} cipherObject - object to decrypt, should contain:
* iv (initialization vector), cipherText (cipher text),
* mac (message authentication code), ephemeralPublicKey
* wasString (boolean indicating with or not to return a buffer or string on decrypt)
* @return {Buffer} plaintext, or false if error
*/
function decryptECIES(privateKey, cipherObject) {
var ecSK = ecurve.keyFromPrivate(privateKey, 'hex');
var ephemeralPK = ecurve.keyFromPublic(cipherObject.ephemeralPK, 'hex').getPublic();
var sharedSecret = ecSK.derive(ephemeralPK);
var sharedSecretBuffer = new Buffer(getHexFromBN(sharedSecret), 'hex');
var sharedKeys = sharedSecretToKeys(sharedSecretBuffer);
var ivBuffer = new Buffer(cipherObject.iv, 'hex');
var cipherTextBuffer = new Buffer(cipherObject.cipherText, 'hex');
var macData = Buffer.concat([ivBuffer, new Buffer(ephemeralPK.encodeCompressed()), cipherTextBuffer]);
var actualMac = hmacSha256(sharedKeys.hmacKey, macData);
var expectedMac = new Buffer(cipherObject.mac, 'hex');
if (!equalConstTime(expectedMac, actualMac)) {
throw new Error('Decryption failed: failure in MAC check');
}
var plainText = aes256CbcDecrypt(ivBuffer, sharedKeys.encryptionKey, cipherTextBuffer);
if (cipherObject.wasString) {
return plainText.toString();
} else {
return plainText;
}
}
}).call(this,require("buffer").Buffer)
},{"buffer":135,"crypto":144,"elliptic":251}],10:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var MissingParametersError = exports.MissingParametersError = function (_Error) {
_inherits(MissingParametersError, _Error);
function MissingParametersError() {
var message = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
_classCallCheck(this, MissingParametersError);
var _this = _possibleConstructorReturn(this, (MissingParametersError.__proto__ || Object.getPrototypeOf(MissingParametersError)).call(this));
_this.name = 'MissingParametersError';
_this.message = message;
return _this;
}
return MissingParametersError;
}(Error);
var InvalidDIDError = exports.InvalidDIDError = function (_Error2) {
_inherits(InvalidDIDError, _Error2);
function InvalidDIDError() {
var message = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
_classCallCheck(this, InvalidDIDError);
var _this2 = _possibleConstructorReturn(this, (InvalidDIDError.__proto__ || Object.getPrototypeOf(InvalidDIDError)).call(this));
_this2.name = 'InvalidDIDError';
_this2.message = message;
return _this2;
}
return InvalidDIDError;
}(Error);
},{}],11:[function(require,module,exports){
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _auth = require('./auth');
Object.keys(_auth).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function get() {
return _auth[key];
}
});
});
var _profiles = require('./profiles');
Object.keys(_profiles).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function get() {
return _profiles[key];
}
});
});
var _storage = require('./storage');
Object.keys(_storage).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function get() {
return _storage[key];
}
});
});
var _dids = require('./dids');
Object.defineProperty(exports, 'makeDIDFromAddress', {
enumerable: true,
get: function get() {
return _dids.makeDIDFromAddress;
}
});
Object.defineProperty(exports, 'makeDIDFromPublicKey', {
enumerable: true,
get: function get() {
return _dids.makeDIDFromPublicKey;
}
});
Object.defineProperty(exports, 'getDIDType', {
enumerable: true,
get: function get() {
return _dids.getDIDType;
}
});
Object.defineProperty(exports, 'getAddressFromDID', {
enumerable: true,
get: function get() {
return _dids.getAddressFromDID;
}
});
var _keys = require('./keys');
Object.defineProperty(exports, 'getEntropy', {
enumerable: true,
get: function get() {
return _keys.getEntropy;
}
});
Object.defineProperty(exports, 'makeECPrivateKey', {
enumerable: true,
get: function get() {
return _keys.makeECPrivateKey;
}
});
Object.defineProperty(exports, 'publicKeyToAddress', {
enumerable: true,
get: function get() {
return _keys.publicKeyToAddress;
}
});
Object.defineProperty(exports, 'getPublicKeyFromPrivate', {
enumerable: true,
get: function get() {
return _keys.getPublicKeyFromPrivate;
}
});
var _utils = require('./utils');
Object.defineProperty(exports, 'nextYear', {
enumerable: true,
get: function get() {
return _utils.nextYear;
}
});
Object.defineProperty(exports, 'nextMonth', {
enumerable: true,
get: function get() {
return _utils.nextMonth;
}
});
Object.defineProperty(exports, 'nextHour', {
enumerable: true,
get: function get() {
return _utils.nextHour;
}
});
Object.defineProperty(exports, 'makeUUID4', {
enumerable: true,
get: function get() {
return _utils.makeUUID4;
}
});
Object.defineProperty(exports, 'hasprop', {
enumerable: true,
get: function get() {
return _utils.hasprop;
}
});
Object.defineProperty(exports, 'updateQueryStringParameter', {
enumerable: true,
get: function get() {
return _utils.updateQueryStringParameter;
}
});
Object.defineProperty(exports, 'isLaterVersion', {
enumerable: true,
get: function get() {
return _utils.isLaterVersion;
}
});
Object.defineProperty(exports, 'isSameOriginAbsoluteUrl', {
enumerable: true,
get: function get() {
return _utils.isSameOriginAbsoluteUrl;
}
});
var _jsontokens = require('jsontokens');
Object.defineProperty(exports, 'decodeToken', {
enumerable: true,
get: function get() {
return _jsontokens.decodeToken;
}
});
},{"./auth":7,"./dids":8,"./keys":12,"./profiles":13,"./storage":36,"./utils":37,"jsontokens":312}],12:[function(require,module,exports){
(function (Buffer){
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getEntropy = getEntropy;
exports.makeECPrivateKey = makeECPrivateKey;
exports.publicKeyToAddress = publicKeyToAddress;
exports.getPublicKeyFromPrivate = getPublicKeyFromPrivate;
var _crypto = require('crypto');
var _bitcoinjsLib = require('bitcoinjs-lib');
var _bigi = require('bigi');
var _bigi2 = _interopRequireDefault(_bigi);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function getEntropy(numberOfBytes) {
if (!numberOfBytes) {
numberOfBytes = 32;
}
return (0, _crypto.randomBytes)(numberOfBytes);
}
function makeECPrivateKey() {
var keyPair = new _bitcoinjsLib.ECPair.makeRandom({ rng: getEntropy });
return keyPair.d.toBu