When we first designed the API for blockstack.js, we made it so that state related to a user signing in to a Blockstack app was automatically stored to and read from the browser’s localStorage
key-value store.
While this made it super easy to get started, it made things confusing for developers who didn’t realize that this was happening. Reliance on APIs only available in the browser also have using this in non-browser environments such as node problematic. It also makes it difficult to use the library in apps such as the Browser or a server app that generate their own private keys and configuration data outside of the normal Sign in With Blockstack process.
Below is pseudo code that outlines my proposal to address this issue.
I’d love your feedback on this. I hope to get started on this early next week.
New Classes
Below are some pseudocode signatures for new classes.
// modeled after what we have in Android https://github.com/blockstack/blockstack-android/blob/d70864acdc12f50bd90fb276853c523845e04e22/blockstack-sdk/src/main/java/org/blockstack/android/sdk/BlockstackConfig.kt#L14
class AppConfig {
appDomain: string
scopes: Array<string> = DEFAULT_SCOPE,
redirectPath: string = '/redirect', // this needs to be on appDomain so only accept paths
manifestPath: string = '/manifest.json', // this needs to be on appDomain so only accept paths
coreNode: string = null // if null, use node passed by auth token v1.3 or otherwise core.blockstack.org
}
class BlockstackSession {
appConfig: ?AppConfig
transitKey: ?string
appPrivateKey: ?string
username: ?string
identityAddress: ?string
coreNode: ?string
gaiaHubConfig: ?GaiaHubConfig
version: string
constructor({ appPrivateKey, username, coreNode, hubUrl }) // used by apps like that generate their own keys
{}
}
// existing functionality would be exposed through this class
class Blockstack {
constructor( { appConfig } ) {}
constructor( { session } ) {}
}
Usage in a web app
This is how using this in a typical web app might look.
Initiating sign in
import Blockstack, { AppConfig } from 'blockstack'
const myAppDomain = window.location.origin
const appConfig = new AppConfig(myAppDomain)
const blockstack = new Blockstack({ appConfig })
const session: BlockstackSession = blockstack.getSession()
/*
session.transitKey is an ephermal key
everything else but version is null
*/
// app developer manages persistance of session state
localStorage.set('my-cool-app-session', session)
blockstack.redirectToSignIn()
Handling sign in response
The user approves or rejects the sign in request in his authenticator (eg. the Blockstack browser) and is redirected to a page hosted on the app with the following code:
import Blockstack from 'blockstack'
// app developer is responsible for retrieving session state
const session: BlockstackSession = localStorage.get('my-cool-app-session')
const blockstack = new Blockstack({ session })
if (blockstack.isSignInPending()) {
blockstack.handleSignIn(authResponseToken: string = getAuthResponseToken())
.then(session: BlockstackSession => {
// do custom app stuff when sign in succeeds
// app developer should update their persisted copy
// of session state
localStorage.set('my-cool-app-session', session)
})
.catch(error => {
// user didn't approve sign in
// or other error occurred
})
}
const user: User = blockstack.getUser()
Using this in an authenticator or node app
Some apps, like the Blockstack Browser, the unofficial web extension and other authenticator type apps generate their own keys in a process that doesn’t involve the typical authentication process.
Here’s how that might look:
import Blockstack, { BlockstackSession } from 'blockstack'
const identityKey = 'abc'
const options = { appPrivateKey: identityKey, username, coreNode, hubUrl }
const session = new BlockstackSession(options)
const blockstack = new Blockstack({ session })
blockstack.putProfile(profile)
blockstack.putFile('avatar.jpg', photo, { encrypt: false })
Tracking issue for this is: https://github.com/blockstack/blockstack.js/issues/531