Verifying that an Application Key is associated with the Owner Key

There’s not an easy way currently for an application to prove the association between the owner key (i.e., the address directly associated with the user’s Blockstack ID) and the app-specific key. However, this association is explicitly declared in the user’s profile object, which itself is signed, and so may be used as a way to verify that an application key is associated with the owner. So, in order to prove this relationship, you just need to fetch all that data and provide it as proof of the association.

import zonefile from 'zone-file'

// This will validate that a given username's owner address is associated with
//   the given app address 
function fetchProfileValidateAppAddress (username, appAddress, appOrigin) {
  return getProfileJWT(username)
    .then((response) => {
      const jwt = response.profileJWT
      const ownerAddress = response.owner
      // this verifies that the token is signed by the owner address
      const profile = blockstack.verifyProfileToken(jwt, ownerAddress)
            .payload.claim
      if (!profile.apps)
        throw new Error('No app entry in this profile! Validation fails.')
      if (!profile.apps[appOrigin])
        throw new Error(`No entry for ${appOrigin} in profile! Validation fails.`)
      const appUrl = profile.apps[appOrigin]
      // make sure that the address in the gaia url for the app matches
      //     the provided app address
      const matches = appUrl.match(/([13][a-km-zA-HJ-NP-Z0-9]{26,35})/)
      if (!matches)
        throw new Error('Failed to parse address out of app url!')

      const expectedAppAddress = matches[matches.length - 1]
      if (expectedAppAddress !== appAddress) {
        throw new Error(`Expected app address ${expectedAppAddress} does not match provided ${appAddress}`)
      }

      return { username, appAddress, appOrigin, profileJWT: jwt, ownerAddress }

    })
}

// Fetch the most recent profile object for a user.
//   Unfortunately, you need to do this manually, as lookupProfile() doesn't return the
//   full JSON web token, which you need to perform the verification.
function getProfileJWT(username) {
  return blockstack.config.network.getNameInfo(username)
    .then((responseJSON) => {
      if (responseJSON.zonefile && responseJSON.address) {
        const zoneFileJSON = zonefile.parseZoneFile(responseJSON.zonefile)
        const profileURL = blockstack.getTokenFileUrl(zoneFileJSON)
        return fetch(profileURL)
          .then((resp) => resp.json())
          .then((profileJWT) => ({ owner: responseJSON.address,
                                   profileJWT: profileJWT[0].token }))
      } else {
        throw new Error('Name information did not return zonefile.')
      }
    })
}
1 Like

Thanks @aaron

I get an error while trying to convert the text zone file to json.

Here is my zone file (notice the two \n\n at the end)

$ORIGIN nicktee.id.blockstack↵$TTL 3600↵_http._tcp	IN	URI	10	1	"https://gaia.blockstack.org/hub/1P4fSFi7L4GBVmVHJMir84VhnV4qpzYpT4/profile.json"↵↵

Error:

ERROR Error: Uncaught (in promise): TypeError: Cannot read property 'length' of undefined
TypeError: Cannot read property 'length' of undefined at flatten (parseZoneFile.js:33) 

It appears to be failing in the zone-file npm package in the flatten(text) function since captured is an empty array

Hmm — I don’t see that error when I try running the code with your name:

> getProfileJWT('nicktee.id.blockstack').then(console.log)
{ owner: '1P4fSFi7L4GBVmVHJMir84VhnV4qpzYpT4',
  profileJWT: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJqdGkiOiJiZmIwZDQ4ZC0xMWRjLTRkOTAtOTI1OC00NzgxZTdkNzMxNzQiLCJpYXQiOiIyMDE4LTA5LTI1VDE3OjUxOjEwLjU1NFoiLCJleHAiOiIyMDE5LTA5LTI1VDE3OjUxOjEwLjU1NFoiLCJzdWJqZWN0Ijp7InB1YmxpY0tleSI6IjAzNTM3NmViZDk1ZjgwMTczMWM5YWYxZmM0ZDFiMjMwZWVmZjRhMTI4NjA4YjBhNjI3MjE5NWU1Mjc3NTY5ZWQ1NCJ9LCJpc3N1ZXIiOnsicHVibGljS2V5IjoiMDM1Mzc2ZWJkOTVmODAxNzMxYzlhZjFmYzRkMWIyMzBlZWZmNGExMjg2MDhiMGE2MjcyMTk1ZTUyNzc1NjllZDU0In0sImNsYWltIjp7IkB0eXBlIjoiUGVyc29uIiwiQGNvbnRleHQiOiJodHRwOi8vc2NoZW1hLm9yZyIsImFwcHMiOnsiaHR0cHM6Ly9ibG9ja3VzaWduLmNvIjoiaHR0cHM6Ly9nYWlhLmJsb2Nrc3RhY2sub3JnL2h1Yi8xNmk4c1ZraHRHQTgzblpQcmpzaHd2S0NZZHVKRGRFaWdOLyIsImh0dHA6Ly9sb2NhbGhvc3Q6NTAwMCI6Imh0dHBzOi8vZ2FpYS5ibG9ja3N0YWNrLm9yZy9odWIvMUhiYkZacUhaVjE0MkhHdjhON1Y3eXVrMVJIREhFRkJlQy8iLCJodHRwOi8vbG9jYWxob3N0OjgxMDAiOiJodHRwczovL2dhaWEuYmxvY2tzdGFjay5vcmcvaHViLzFESmtkRlMxN3pTZ0hXa01INERhWWk4Ums2VmRCTmdGbVAvIn19fQ.KVdgxpkmUXtUZQCxdityYHTRY8nLqa6OQVSJDAG0TzQuMVHcXecmbb4eYWb8GOS4YIenwNEWPOk5RhG1Bs8ikQ' }

Are you using the most recent blockstack.js and zone-file libraries?

Is there any way to get app adress and app origin of a user assuming that we know the username?