UNPKG

3box

Version:
156 lines (140 loc) 5.73 kB
const { fetchText, getMessageConsent } = require('./index') const didJWT = require('did-jwt') const { verifyMessage } = require('@ethersproject/wallet') const config = require('../config.js') const utils = require('./index') const { Resolver } = require('did-resolver') const get3IdResolver = require('3id-resolver').getResolver const getMuportResolver = require('muport-did-resolver').getResolver const getHttpsResolver = require('https-did-resolver').default const PROFILE_SERVER_URL = config.profile_server_url // Mocks ipfs obj for 3id resolve, to resolve through api, until ipfs instance is available const ipfs = { dag: { get: async (cid) => { const req = `${PROFILE_SERVER_URL}/did-doc?cid=${encodeURIComponent(cid)}` return utils.fetchJson(req) } } } const resolver = new Resolver({ ...get3IdResolver(ipfs, { pin: false }), ...getMuportResolver(), ...getHttpsResolver() }) module.exports = { /** * Verifies that the gist contains the given 3ID and returns the users github username. * Throws an error otherwise. * * @param {String} did The 3ID of the user (or array of equivalent dids) * @param {Object} gistUrl URL of the proof * @return {Object} Object containing username, and proof */ verifyGithub: async (did, gistUrl) => { const dids = typeof did === 'string' ? [did] : did if (!gistUrl || gistUrl.trim() === '') { return null } let gistFileContent = await fetchText(gistUrl) const includeDid = dids.reduce((acc, val) => (acc || gistFileContent.indexOf(val) !== -1), false) if (!includeDid) { throw new Error('Gist File provided does not contain the correct DID of the user') } const username = gistUrl.split('/')[3] return { username, proof: gistUrl } }, /** * Verifies that the tweet contains the given 3ID and returns the users twitter username. * Throws an error otherwise. * * @param {String} did The 3ID of the user (or array of equivalent dids) * @param {String} claim A did-JWT with claim * @return {Object} Object containing username, proof, and the verifier */ verifyTwitter: async (did, claim) => { const dids = typeof did === 'string' ? [did] : did if (!claim) return null const verified = await didJWT.verifyJWT(claim, { resolver }) if (!dids.includes(verified.payload.sub)) { throw new Error('Verification not valid for given user') } const claimData = verified.payload.claim if (!claimData.twitter_handle || !claimData.twitter_proof) { throw new Error('The claim for your twitter is not correct') } return { username: claimData.twitter_handle, proof: claimData.twitter_proof, verifiedBy: verified.payload.iss } }, /** * Verifies that the code entered by the user is the same one that was sent via email. * Throws an error otherwise. * * @param {String} did The 3ID of the user (or array of equivalent dids) * @param {String} claim A did-JWT with claim * @return {Object} Object containing username, proof, and the verifier */ verifyEmail: async (did, claim) => { const dids = typeof did === 'string' ? [did] : did if (!claim) return null const verified = await didJWT.verifyJWT(claim, { resolver }) if (!dids.includes(verified.payload.sub)) { throw new Error('Verification not valid for given user') } const claimData = verified.payload.claim if (!claimData.email_address) { throw new Error('The claim for your email address is not correct') } return { email_address: claimData.email_address, verifiedBy: verified.payload.iss } }, /** * Verifies that the proof for a did is correct * * @param {String} claim A did-JWT with claim * @return {String} The DID of the user */ verifyDID: async (claim) => { const verified = await didJWT.verifyJWT(claim, { resolver }) const muport = verified.payload.muport const res = {} if (muport) { const muportDID = (await didJWT.verifyJWT(muport, { resolver })).payload.iss res.muport = muportDID } res.did = verified.payload.iss return res }, /** * Verifies that the proof for an ethereum address is correct * * @param {Object} ethProof The claim generated by getLinkConsent * @param {string} ethProof.consent_msg * @param {string} ethProof.consent_signature * @param {string} ethProof.linked_did * @param {String} did The box' did * @return {String} The ethereum address used to sign the message */ verifyEthereum: async (ethProof, did) => { const dids = typeof did === 'string' ? [did] : did // TODO - is this function needed? Can it be removed in // favour of proofs that are in the rootstore? const consentMsg = ethProof.version ? ethProof.message : ethProof['consent_msg'] const consentSig = ethProof.version ? ethProof.signature : ethProof['consent_signature'] // Make sure the message matches our expectation const expected = getMessageConsent(did) if (consentMsg !== expected) { throw new Error(`Invalid consent message, got: "${consentMsg}", expected: "${expected}"`) } // Validate the signature return verifyMessage(consentMsg, consentSig) } }