To-dos app
In this tutorial, you'll learn about Stacks authentication and storage by installing, running, and reviewing the code for a "To-dos" app built on Stacks authentication and storage.
This app highlights the following platform capabilities:
- Generate Secret Key with associated BNS username to authenticate app
- Add, edit, and delete encrypted application data with Gaia
- Decrypt data on Gaia for public sharing by URL
- De-authenticate and re-authenticate app using Secret Key
Try the app or view its code on GitHub.
Existing familiarity with React is recommended for reviewing this app's code.
#
Install and run the appYou must have recent versions of Git and Node.js (v12.10.0 or greater) installed already.
#
Step 1: Install the code and its dependenciesgit clone https://github.com/hirosystems/todos && cd todosnpm install
#
Step 2: Run the app:npm run start
You should see output similar to the following:
Compiled successfully
You can now view to-dos in the browser.
http://localhost:3000/
Note that the development build is not optimized.To create a production build, use yarn build.
#
Step 3: Review local buildYou should see the app's landing page:
#
Onboard into your first Stacks app#
Step 1: Choose Get started to start onboarding to the applicationThe app displays a standardized introductory modal using the @stacks/connect
library.
This modal is displayed using the authenticate
function exported by the src/auth.js
module, which organizes all Stacks resources needed for authentication in the app:
// src/auth.js
import { AppConfig, UserSession, showConnect } from "@stacks/connect";import { Person } from "@stacks/profile";
const appConfig = new AppConfig(["store_write", "publish_data"]);
export const userSession = new UserSession({ appConfig });
export function authenticate() { showConnect({ appDetails: { name: "Todos", icon: window.location.origin + "/logo.svg", }, redirectTo: "/", finished: () => { window.location.reload(); }, userSession: userSession, });}
export function getUserData() { return userSession.loadUserData();}
export function getPerson() { return new Person(getUserData().profile);}
The authenticate
function implements the showConnect
function imported from the connect
package of Stacks.js.
showConnect
triggers the display of a modal that initiates the authentication process for users. Then, authentication is done with a Secret Key that's used to encrypt their private data.
The showConnect
function accepts a number of properties within a parameter object such as:
- The app's
name
andicon
: provided as strings comprising theappDetails
object property. - The
redirectTo
string: used to provide a URL to which the user should be redirected upon successful authentication. TheonFinish
callback serves a similar purpose by handling successful authentication within a context of a popup window. - The
userSession
object: used to pass the scopes needed by the app.
Note how the userSession
object is created at the beginning of this module by leveraging an AppConfig
object that's first initiated with all relevant scopes.
The UserSession
and AppConfig
classes are themselves imported from the @stacks/auth
library.
In the separate src/components/App.jsx
component, you can see how
componentDidMount
loads the user's data into the app's state, whether upon redirect post-authentication with userSession.handlePendingSignIn()
or upon detection of an existing session with userSession.isUserSignedIn()
:
// src/components/App.jsx
import { userSession } from '../auth';
...
componentDidMount() { if (userSession.isSignInPending()) { userSession.handlePendingSignIn().then((userData) => { window.history.replaceState({}, document.title, "/") this.setState({ userData: userData}) }); } else if (userSession.isUserSignedIn()) { this.setState({ userData: userSession.loadUserData() }); }}
#
Step 2: Choose Get started to generate a Secret Key.The app triggers a popup window in which Stacks Web Wallet
loads from app.blockstack.org
and begins generating a new Secret Key.
#
Step 3: Choose Copy Secret Key to copy your Secret Key to the clipboard.The Secret Key is a unique 12-word mnemonic phrase that empowers the user not only to access Stacks apps securely and independently. It's also used to encrypt all of the private data they create and manage with Stacks apps.
Secret Keys are like strong passwords. However, they can never be recovered if lost or reset if stolen. As such, it's paramount that users handle them with great care.
#
Step 4: Choose I've saved it to confirm you've secured your Secret Key in a suitable place.#
Step 5: Enter a username value and choose ContinueThe username will be used by the app to generate a URL for sharing your to-dos, should you choose to make them public.
It is registered on the Stacks blockchain with BNS and associated with your Secret Key.
#
Done: You've now completed onboarding into the app#
Add, edit and delete to-dos privatelyOnce you've authenticated the app, you can start adding to-dos by entering values into the "Write your to do" field and hitting "Enter."
The data for all to-dos are saved as JSON to the Gaia hub linked to your Secret Key using the putFile
method of the storage
object in the src/storage.js
module, which manages all data storage for the app:
// src/storage.js
import { userSession } from './auth';import { Storage } from '@stacks/storage';
const storage = new Storage({ userSession });
...
export const saveTasks = async (userSession, tasks, isPublic) => { await storage.putFile(TASKS_FILENAME, JSON.stringify({ tasks, isPublic }), { encrypt: !isPublic, });};
These to-dos are subsequently loaded using the getFile
method of the same object in the same module:
// src/storage.js
import { userSession } from './auth';import { Storage } from '@stacks/storage';
const storage = new Storage({ userSession });
...
export const fetchTasks = async (userSession, username) => { const tasksJSON = await storage.getFile(TASKS_FILENAME, { decrypt: false, username: username || undefined, }); ...};
The storage
object is instantiated with the Storage
class of the @stacks/storage
library and userSession
to ensure that all storage calls are made with the user's Gaia hub.
By default, the putFile
and getFile
methods automatically encrypt data when saved and decrypt it when retrieved,
using the user's Secret Key. This ensures that only the user has the ability to view this data.
When deleting a todo, the same putFile
method is used to save a new JSON array of to-dos that excludes the deleted todo.
#
Publish your to-dos publiclySelect "Make public" to make your to-dos accessible to the public for sharing via URL.
This will call saveTasks
with the isPublic
parameter set to true
, which is used to disable encryption when using putFile
.
The app will now show all of your to-dos to anyone who visits the URL displayed with your Stacks username as a suffix.
#
Sign out and see your public tasksSelect "Sign out" to deauthenticate the app with your Stacks account.
This calls the signUserOut
method
of the userSession
object within src/components/Header.jsx
.
Now visit the URL that was provided to you when you made your tasks public. This URL has the format /todos/:username
, so if your username were janedoe.id.blockstack
, the URL would be localhost:3000/todos/janedoe.id.blockstack
.
When you visit this page, the TodoList.jsx
component detects that there is a username in the URL.
When there is a username, it calls fetchTasks
, this time providing the username
argument. This username
option is then passed to getFile
, which will look up where that user's tasks are stored.
#
Sign back inAt this point, you will be logged out of the app, but you'll still have an active session with the Stacks app itself on app.blockstack.org. Navigate to app.blockstack.org and select "Sign out" if you want to deauthenticate the Stacks app as well.
Once you're signed out, select "Sign In" to sign back in with your Secret Key.
If you've previously deauthenticated the Stacks app, you'll see a prompt to enter your Secret Key:
The preceding screen is omitted if you have an active session with the Stacks app already.
Then you'll be presented with the option to select an existing username associated with your Secret Key or create a new one if you wish to authenticate the app with a different identity and data set:
You'll now see your to-dos as an authenticated user for the username you've chosen.
#
Learn moreRead the Stacks.js reference to learn more about the libraries used in this tutorial.