stacks-archive / blockstack-browser

The Blockstack Browser
Mozilla Public License 2.0
1.12k stars 199 forks source link

Proposal: Per-app Gaia hubs in the profile #1489

Open jcnelson opened 6 years ago

jcnelson commented 6 years ago

TL;DR

Make it so the user's profile object points to both read and write URLs for Gaia hubs on a per-app basis, and pass the write URL as hubUrl in the authResponse token.

Background

Blockstack's promise to users it that they own their own data. Part of that promise means that a user's data gets hosted wherever the user wants. Arguably, this also means the user can do so at the granularity of specific applications, since the nature of the data determines how it is stored. This entails making it so users have the ability to select which Gaia hub an application will use to store its data.

Design and Implementation

I think the shortest-path-to-success is somewhat straightforward: Extend the apps object in the user's profile to contain both Gaia read and write URLs. Right now, it only contains the read URL (and even then, it's not honored---see https://github.com/blockstack/blockstack-browser/issues/1488).

When the user completes the sign in (in app/js/auth/index.js, at completeAuthResponse()), the authResponse JWT returned to the application should contain a hubUrl value that corresponds to the user's chosen Gaia hub for that application.

What is still needed is a way to store the write URL in the profile in a backwards compatible way. I recommend the following scheme. Instead of this:

"apps": {
   "https://app.origin": "https://user.gaia.hub/hub/1APPADDRESSxxxxxx/"
}

We have this instead:

"apps": {
   "https://app.origin": "https://user.gaia.hub/hub/1APPADDRESSxxxxxxx/"
}
"hubUrls": {
   "https://app.origin": "XXXXXEncryptedWriteURLXXXXXX"
}

The hubUrls object encodes the encrypted write URL for an application. It should be encrypted with the app public key because knowledge of the write endpoint is only pertinent to the writer. We can use the usual encryptECIES method to generate this ciphertext, and decryptECIES to decrypt it.

If the hubUrls object exists in the profile and has an entry for the application, the completeAuthResponse() method should use that instead of the default this.props.api.gaiaHubConfig.server URL). Otherwise, it uses the default this.props.api.gaiaHubConfig.server URL.

Backwards Compatibility

When the user signs into an app for the first time, then an entry is added to the hubUrls in addition to the apps objects.

If the user signs in and they don't have a hubUrls entry, they use the this.props.api.gaiaHubConfig.server value as before.

EDIT: For single-player apps, we do not want to expose either the read or write URLs. To achieve this, when a user signs into a single-player app for the first time, the Browser would insert the following:

"hubUrls": {
   "XXXXXEncryptedAppOriginXXXXX": "XXXXXEncryptedGaiaHubUrlXXXXX"
}

Both the origin and the write URL will be encrypted. No read URL will be inserted into the apps object (since this information can be obtained from the write URL).

Execution

I'm happy to take the lead on all of the above. However, I think someone more versed in non-CLI user experiences should take the lead on adding a GUI for managing your hubUrls.

Thoughts? @kantai @yknl @larrysalibra

yknl commented 6 years ago

I like this proposal. Does this mean that single player apps will now also be published in the user profile? Maybe we should encrypt the app origin as well for single player apps.

jcnelson commented 6 years ago

Yeah, that's a good idea for single player apps. I'll update the proposal.

kantai commented 6 years ago

I like this proposal and wouldn't object to going forward with it, but there's another (very similar) option which might be a little less error prone in the event of a synchronization issue between those two structure (apps and hubUrls):

Basically, you still use apps and hubUrls, but hubUrls is a list of (encrypted) hubs, rather than a dictionary. Then, when logging in (or can be cached), the browser checks to see which of the hubs is capable of writing to the appropriate readURL and then uses that. It's functionally pretty similar, but it doesn't have a parallel structure that could get out of sync (let's say I'm manually editing my profile, and forget/don't know what the hubUrls entries are for).

jcnelson commented 6 years ago

Basically, you still use apps and hubUrls, but hubUrls is a list of (encrypted) hubs, rather than a dictionary. Then, when logging in (or can be cached), the browser checks to see which of the hubs is capable of writing to the appropriate readURL and then uses that. It's functionally pretty similar, but it doesn't have a parallel structure that could get out of sync (let's say I'm manually editing my profile, and forget/don't know what the hubUrls entries are for).

As long as there is a well-defined approach to pairing Gaia hub to its read URL, then I'm fine with this solution. I'm assuming the steps are (1) get the /hub_info route for each Gaia hub in hubUrls, and (2) match the read_url_prefix. However, I still have questions pertaining to how we handle the following plausible scenarios:

Having a hubUrls list gives us a way to unequivocally bind an application to a (hub, read URL) pair, so that if the hub stops corresponding to its read URL, the browser can both update the read URL and prompt/inform the user that it needs to do so on the next sign-in.

kantai commented 6 years ago

Having a hubUrls list gives us a way to unequivocally bind an application to a (hub, read URL) pair, so that if the hub stops corresponding to its read URL, the browser can both update the read URL and prompt/inform the user that it needs to do so on the next sign-in.

Cool, I'm convinced.

jcnelson commented 6 years ago

As a stepping stone to implementing this, I think it's fine for now if the user can simply change their Gaia hub in the settings page. As long as the user's Gaia hub URL in the apps field of their profile gets updated and uploaded to their zone file's Gaia hub on their next sign-in, then users can select where their application data lives and multi-reader storage can continue to work. I'll be able to give a proof-of-concept demo in the CLI shortly.

jcnelson commented 6 years ago

I have this working in the CLI authenticator. You pass your desired hubUrl on the CLI, and your profile gets updated with the read URL of that hub whenever you sign into an app.

markmhendrickson commented 5 years ago

@jcnelson it seems this issue should be closed in favor of https://github.com/blockstack/blockstack.js/issues/540?