In this tutorial, you build the code for and run a single-page application (SPA) with Blockstack and Vue.js. Once the application is running, you take a tour through the applications’ Blockstack functionality. You’ll learn how it manages authentiation using a Blockstack ID and how it stores information associated with that ID using Blockstack Storage (Gaia).
- Prerequisites
- Install the applicaton code and retrieve the dependencies
- Sign into the application
- Understand the sign in process
- Under the covers in the sign in code
- Working with the application
- Implementing storage
- Summary
If you prefer a video, you can view a video of the tutorial.
Prerequisites
Make sure you have created at least one Blockstack ID. You’ll use this ID to interact with the Todo application.
The applicaton code relies on both the npm
and the yarn
package managers.
Before you begin, verify you have these tools npm
using the which
command to
verify.
$ which npm
/usr/local/bin/npm
$ which yarn
/usr/local/bin/yarn
Install npm, install yarn, or both as needed. You
While it stands alone, this tour relies on the information from the Hello Blockstack tutorial. If you haven’t worked through the hello-world tutorial, you may want to do that before continuing.
Install the applicaton code and retrieve the dependencies
Clone the source code with git
or
download and unzip the code from the repository.
These instructions assume you are cloning.
-
Install the code by cloning it.
$ git clone git@github.com:blockstack/blockstack-todos.git
-
Change to directory to the root of the code.
$ cd blockstack-todos
-
Use
yarn
to install the dependencies.$ yarn install yarn install v1.9.2 info No lockfile found. ... [4/5] 🔗 Linking dependencies... [5/5] 📃 Building fresh packages... success Saved lockfile. ✨ Done in 19.90s.
The Todo application has a basic Vue.js structure. There are several
configuration files but the crucial implementation files are in the src
directory:
File | Description |
---|---|
main.js |
Application initialization. |
App.vue |
Code for handling the authResponse . |
Landing.vue |
Code for the initial sign on page. |
Dashboard.vue |
Application data storage and user sign out. |
Sign into the application
The example application runs in a core node on your local host. In the next section, you start the application and interact with it.
-
Make sure you are in the root of the code base.
$ pwd /Users/moxiegirl/repos/blockstack-todos
-
Start the application.
$ npm run start
You should see a simple application:
-
Choose Sign In with Blockstack.
If you have already signed into Blockstack the application prompts you to select the ID to use. If you aren’t signed in, Blockstack prompts you to:
If the login to the application is successful, the user is presented with the application:
Understand the sign in process
Clicking the Sign In With Blockstack button brings up a modal that prompts you to sign in with an existing ID or create a new ID. When Blockstack is provided an ID, it generates an ephemeral key within the application. An ephemeral key is generated for each execution of a key establishment process. This key is just used for the particular instance of the application, in this case to sign a Sign In request.
A Blockstack Core node also generates a public key token which is sent to the browser
as an authRequest
from the browser to your core node.
The signed authentication request is sent to Blockstack through a JSON Web
Token (JWT). Blocktack passes the token in via a URL query string in the authRequest
parameter:
https://browser.blockstack.org/auth?authRequest=j902120cn829n1jnvoa...
To decode the token and see what information it holds:
- Copy the
authRequest
string from the URL. - Navigate to jwt.io.
-
Paste the full token there.
The output should look similar to below:
{ "jti": "3i96e3ad-0626-4e32-a316-b243154212e2", "iat": 1533136622, "exp": 1533140228, "iss": "did:btc-addr:1Nh8oQTunbEQWjrL666HBx2qMc81puLmMt", "public_keys": [ "0362173da080c6e1dec0653fa9a3eff5f5660546e387ce6c24u04a90c2fe1fdu73" ], "domain_name": "http://localhost:8080", "manifest_uri": "http://localhost:8080/manifest.json", "redirect_uri": "http://localhost:8080/", "version": "1.2.0", "do_not_include_profile": true, "supports_hub_url": true, "scopes": [ "store_write" ] }
Notes:
- The
iss
property is a decentralized identifier ordid
. This identifies you and your name to the application. The specificdid
is abtc-addr
.- The Blockstack JWT implementation is different from other implementations because of the underlying cryptography. There are libraries in Javascript and Ruby available on the Blockstack Github to allow you to work with these tokens.
When the Blockstack node receives the authRequest
, it generates a
session token and returns an authentication response to the application. This
response is similar to the authRequest
above in that the authResponse
includes a private key intended only for the application. This allows the
application to encrypt data on your personal Blockstack storage.
After the completion of this process, the user is loggged in.
Under the covers in the sign in code
Go to the underlying blockstack-todo
code you cloned or downloaded. Sign
in and sign out is handled in each of these files:
File | Description |
---|---|
App.vue |
Handles the authResponse . |
Landing.vue |
Generates the authRequest . |
Dashboard.vue |
Handles sign out. |
The src/components/Landing.vue
code calls a redirectToSignIn()
function which generates the authRequest
and redirects the user to the Blockstack browser:
signIn () {
const blockstack = this.blockstack
blockstack.redirectToSignIn()
}
Once the user authenticates, the application handles the authResponse
in the src/App.vue
file. :
if (blockstack.isUserSignedIn()) {
this.user = blockstack.loadUserData().profile
} else if (blockstack.isSignInPending()) {
blockstack.handlePendingSignIn()
.then((userData) => {
window.location = window.location.origin
})
}
If blockstack.isUserSignedIn()
is true, the user was previously signed in so Blockstack pulls the data from the browser and uses it in the application. If the check on blockstack.isSignInPending()
is true, a previous authResponse
was sent to the application but hasn’t been processed yet. The handlePendingSignIn()
function processes any pending sign in.
Sign out is handled in the src/components/Dashboard.vue
code.
signOut () {
this.blockstack.signUserOut(window.location.href)
}
The method allows the application creator to decide where to redirect the user after sign out.
Working with the application
Now, try adding a few items to the todo list. For example, try listing the applications you want to see built on top of Blockstack:
Each list is immediately stored in the Gaia Hub linked to your Blockstack ID.
For more information about the Gaia hub, see the hub
repository. You can fetch the todos.json
file you just added by opening the Javascript console and running the following
command:
blockstack.getFile("todos.json", { decrypt: true }).then((file) => {console.log(file)})
You should see a JSON with the todos you just added:
[
{
"id":2,
"text":"Software package manager secured by the blockchain",
"completed":false
},
{
"id":1,
"text":"Mutable torrents with human readable names",
"completed":false
},
{
"id":0,
"text":"Decentralized twitter",
"completed":false
}
]
Add another todo and check it off. When you fetch the newly generated file
using the Javascript console it will reflect the change look for "completed":true
:
[
{
"id":3,
"text":"Blockstack Todo",
"completed":true
},
{
"id":2,
"text":"Software package manager secured by the blockchain",
"completed":false
},
...
]
Now that you have seen the application in action, dig into how it works.
Implementing storage
Go to the underlying blockstack-todo
code you cloned or downloaded. The
application interactions with your Gaia Hub originate in the
src/components/Dashboard.vue
file. First, examine where the changes to the
list entries are processed:
todos: {
handler: function (todos) {
const blockstack = this.blockstack
// encryption is now enabled by default
return blockstack.putFile(STORAGE_FILE, JSON.stringify(todos))
},
deep: true
}
The todos
JSON object is passed in and the
blockstack.putFile()
method to store it in our Gaia Hub.
The code reads the items from storage with the
blockstack.getFile()
method which returns a promise:
fetchData () {
const blockstack = this.blockstack
blockstack.getFile(STORAGE_FILE) // decryption is enabled by default
.then((todosText) => {
var todos = JSON.parse(todosText || '[]')
todos.forEach(function (todo, index) {
todo.id = index
})
this.uidCount = todos.length
this.todos = todos
})
},
The todos
data is retrieved from the promise.
Summary
You now have everything you need to construct complex applications complete with authentication and storage on the Decentralized Internet. Why not try coding a sample application that accesses multiple profiles.
If you would like to explore the Blockstack APIs, you can visit the Blockstack Core API documentation or the Blockstack JS API.
Go forth and build!