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.')
}
})
}