{"datePublished":"2018-10-29T14:08:07-07:00","url":"http://localhost:4000/browser/todo-list.html","dateModified":"2018-10-29T14:08:07-07:00","mainEntityOfPage":{"@type":"WebPage","@id":"http://localhost:4000/browser/todo-list.html"},"author":{"@type":"Person","name":"Blockstack"},"description":"Todo List Application Tutorial","@type":"BlogPosting","headline":"Todo List Application Tutorial","@context":"http://schema.org"}</script>
<p>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).</p>
<ulid="markdown-toc">
<li><ahref="#about-this-tutorial-and-the-prerequisites-you-need"id="markdown-toc-about-this-tutorial-and-the-prerequisites-you-need">About this tutorial and the prerequisites you need</a></li>
<li><ahref="#install-the-application-code-and-retrieve-the-dependencies"id="markdown-toc-install-the-application-code-and-retrieve-the-dependencies">Install the application code and retrieve the dependencies</a></li>
<li><ahref="#sign-into-the-application"id="markdown-toc-sign-into-the-application">Sign into the application</a></li>
<li><ahref="#understand-the-sign-in-process"id="markdown-toc-understand-the-sign-in-process">Understand the sign in process</a></li>
<li><ahref="#undder-the-covers-in-the-sign-in-code"id="markdown-toc-undder-the-covers-in-the-sign-in-code">Undder the covers in the sign in code</a></li>
<li><ahref="#working-with-the-application"id="markdown-toc-working-with-the-application">Working with the application</a></li>
<p>If you prefer a video, you can view <ahref="https://www.youtube.com/embed/oyvg-h0obFw"target="\_blank">a video of the tutorial</a>.</p>
<h2id="about-this-tutorial-and-the-prerequisites-you-need">About this tutorial and the prerequisites you need</h2>
<p>For this tutorial, we will use the following tools:</p>
<ul>
<li><codeclass="highlighter-rouge">npm</code> to manage dependencies and scripts</li>
<li><codeclass="highlighter-rouge">browserify</code> to compile node code into browser-ready code</li>
<li><codeclass="highlighter-rouge">blockstack.js</code> to authenticate the user and work with the user’s identity/profile information</li>
</ul>
<p>At minimum, Blockstack requires macOS High Sierra. This tutorial was written for a user running macOS High Sierra 10.13.4. The application you build is a React.js application that is completely decentralized and server-less. While not strictly required to follow along, basic familiarity with React.js is helpful.</p>
<p>When complete, the app is capable of the following:</p>
<ul>
<li>authenticating users using Blockstack</li>
<li>posting new statuses</li>
<li>displaying statuses in the user profile</li>
<li>looking up the profiles and statuses of other users</li>
</ul>
<p>The basic identity and storage services are provided by blockstack.js. To test the application, you need to have already registered a Blockstack ID.</p>
<p>The tutorial relies on the <codeclass="highlighter-rouge">npm</code> dependency manager. Before you begin, verify you have installed <codeclass="highlighter-rouge">npm</code> using the <codeclass="highlighter-rouge">which</code> command to verify.</p>
<p>If you don’t find <codeclass="highlighter-rouge">npm</code> in your system, <ahref="https://www.npmjs.com/get-npm">install it</a>.</p>
<p>Finally, make sure you have <ahref="{site.baseurl}}/browser/ids-introduction.html#create-an-initial-blockstack-id">created at least one Blockstack ID</a>.
You’ll use this ID to interact with the application.</p>
<h2id="install-the-application-code-and-retrieve-the-dependencies">Install the application code and retrieve the dependencies</h2>
<p>You can clone the source code with <codeclass="highlighter-rouge">git</code> or <ahref="https://github.com/blockstack/blockstack-todos/archive/master.zip">download and unzip the code from
<p>The Todo application has a basic Vue.js structure. There are several configuration files but the central programming files are in the <codeclass="highlighter-rouge">src</code> directory:</p>
<divclass="highlighter-rouge"><divclass="highlight"><preclass="highlight"><code> $ npm run start
</code></pre></div></div>
<p>You should see a simple application:</p>
<p><imgsrc="images/todo-sign-in.png"alt=""/></p>
</li>
<li>
<p>Choose <strong>Sign In with Blockstack</strong>.</p>
<p>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:</p>
<p><imgsrc="images/login-choice.png"alt=""/></p>
<p>If the login to the application is successful, the user is presented with the application:</p>
<p><imgsrc="images/todo-app.png"alt=""/></p>
</li>
</ol>
<h2id="understand-the-sign-in-process">Understand the sign in process</h2>
<p>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.</p>
<p>A Blockstack Core node also generates a public key token which is sent to the
browser as an <codeclass="highlighter-rouge">authRequest</code> 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
<li>The <codeclass="highlighter-rouge">iss</code> property is a decentralized identifier or <codeclass="highlighter-rouge">did</code>. This identifies you and your name to the application. The specific <codeclass="highlighter-rouge">did</code> is a <codeclass="highlighter-rouge">btc-addr</code>.</li>
<li>The Blockstack JWT implementation is different from other implementations because of the underlying cryptography we employ. There are libraries in <ahref="https://github.com/blockstack/jsontokens-js">Javascript</a> and <ahref="https://github.com/blockstack/ruby-jwt-blockstack">Ruby</a> available on the Blockstack Github to allow you to work with these tokens.</li>
</ul>
</blockquote>
</li>
</ol>
<p>When the Blockstack node receives the <codeclass="highlighter-rouge">authRequest</code>, it generates a session token
and returns an authentication response to the application. This response is
similar to the <codeclass="highlighter-rouge">authRequest</code> above in that the <codeclass="highlighter-rouge">authResponse</code> includes a private key
intended only for the application. This allows the application to encrypt data
on your personal Blockstack storage.</p>
<p>After the completion of this process, the user is logged in.</p>
<h2id="undder-the-covers-in-the-sign-in-code">Undder the covers in the sign in code</h2>
<p>Now, go to the underlying <codeclass="highlighter-rouge">blockstack-todo</code> code you cloned or downloaded. Sign
in and sign out is handled in each of these files:</p>
<p>The <codeclass="highlighter-rouge">src/components/Landing.vue</code> code calls a <ahref="https://blockstack.github.io/blockstack.js#redirectToSignIn"><codeclass="highlighter-rouge">redirectToSignIn()</code></a> function which generates the <codeclass="highlighter-rouge">authRequest</code> and redirects the user to the Blockstack authenticator:</p>
<p>Once the user authenticates, the application handles the <codeclass="highlighter-rouge">authResponse</code> in the <codeclass="highlighter-rouge">src/App.vue</code> file. :</p>
<p>If <ahref="https://blockstack.github.io/blockstack.js/#isusersignedin"><codeclass="highlighter-rouge">blockstack.isUserSignedIn()</code></a> is true, the user was previously signed in so Blockstack pulls the data from the browser and uses it in our application. If the check on <ahref="https://blockstack.github.io/blockstack.js/#issigninpending"><codeclass="highlighter-rouge">blockstack.isSignInPending()</code></a> is true, a previous <codeclass="highlighter-rouge">authResponse</code> was sent to the application but hasn’t been processed yet. The <codeclass="highlighter-rouge">handlePendingSignIn()</code> function processes any pending sign in.</p>
<p>Signout is handled in <codeclass="highlighter-rouge">src/components/Dashboard.vue</code>.</p>
</span><spanclass="s2">"text"</span><spanclass="p">:</span><spanclass="s2">"Software package manager secured by the blockchain"</span><spanclass="p">,</span><spanclass="w">
</span><spanclass="s2">"text"</span><spanclass="p">:</span><spanclass="s2">"Mutable torrents with human readable names"</span><spanclass="p">,</span><spanclass="w">
</span><spanclass="s2">"text"</span><spanclass="p">:</span><spanclass="s2">"Software package manager secured by the blockchain"</span><spanclass="p">,</span><spanclass="w">
<p>The code needs to read the Todo items from the storage with the <ahref="https://blockstack.github.io/blockstack.js/#getfile"><codeclass="highlighter-rouge">blockstack.getFile()</code></a> method which returns a promise:</p>
<spanclass="nx">blockstack</span><spanclass="p">.</span><spanclass="nx">getFile</span><spanclass="p">(</span><spanclass="nx">STORAGE_FILE</span><spanclass="p">)</span><spanclass="c1">// decryption is enabled by default</span>
<p>The <codeclass="highlighter-rouge">todos</code> data is retrieved from the promise.</p>
<h2id="summary">Summary</h2>
<p>You now have everything you need to construct complex applications complete with authentication and storage on the Decentralized Internet. Why not try coding <ahref="multi-player-storage.md">a sample application that accesses multiple profiles</a>.</p>
<p>If you would like to explore the Blockstack APIs, you can visit the <ahref="https://core.blockstack.org/">Blockstack Core API</a> documentation or the <ahref="https://blockstack.github.io/blockstack.js">Blockstack JS API</a>.</p>
/* A CSS selector that points to your search box */
searchBox: {
selector: '#searchBox'
},
results: {
embedConfig: undefined, // {'url':undefined,'contentBlock':'.page-content-body'}, // if url is given the page will change to that URL and look for the content block there to insert the results
fullScreenConfig: undefined, // {trigger: '#ss360-search-trigger', caption: 'Search this site'}, trigger is the CSS selector to the element that starts the search full screen overlay and searchCaption the caption on the full screen search page
caption: 'Found #COUNT# search results for \"#QUERY#\"', // the caption of the search results
group: true, // whether results should be grouped if content groups are available
filters: undefined,
num: 96, // the maximum number of search results to be shown
highlightQueryTerms: true, // whether to highlight the query terms in search results
moreResultsButton: "Show more results", // HTML for the more results button, all results will be shown if this is null
noResultsText: 'Sorry, we have not found any matches for your query.', // the text to show when there are no results
queryCorrectionText: 'Did you mean "#CORRECTION#"?',
searchQueryParamName: 'ss360Query', // the name of the search query parameter
linksOpenNewTab: false, // should clicking on the result links open a new tab/window?
showSearchBoxLayover: true, //whether to show search box in search result layover
moreResultsPagingSize: 12, // the number of new results to show each time the more results button is pressed (max: 24)
orderByRelevanceText: "Relevance" // the text to be shown in order select box to describe 'order by relevance' option
},
suggestions: {
show: true, // whether to show search suggestions
maxQuerySuggestions: 3, // the maximum number of query suggestions
querySuggestionHeadline: undefined, // the headline of the query suggestions, leave blank if no headline should be shown
emptyQuerySuggestions: undefined,
showImages: false, // show images in search suggestions
num: 6, // the maximum number of search suggestions to be shown
minChars: 3, // minimum number of characters before the suggestions shows, default: 3,
maxWidth: 'auto', // the maximum width of the suggest box, default: as wide as the input box, at least 275px
throttleTime: 300, // the number of milliseconds before the suggest is triggered after finished input, default: 300ms
extraHtml: undefined, // extra HTML code that is shown in each search suggest, you can even show values of datapoints here,
highlight: true, // whether matched words should be highlighted, default: true
},
smart404: { /* The caption of the search results. */
caption: 'These links might be useful', /* The string in the title that identifies the page as a 404 page. */
identifier: 'Page not found', /* A CSS selector that points to the area in which the alternative links should be shown. */