UNPKG

blockstack

Version:

The Blockstack Javascript library for identity and authentication.

1,492 lines (1,296 loc) 2.15 MB
(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