From 35dd4e8a131f265bb21366909819aae0ca2d29df Mon Sep 17 00:00:00 2001
From: Tim Boeckmann <2022378+tboeckmann@users.noreply.github.com>
Date: Sun, 12 Feb 2023 18:38:45 +0000
Subject: [PATCH 1/9] Add Mailchain tutorial
---
books/tutorials/mailchain/README.md | 56 ++++++++++++++++
books/tutorials/mailchain/SUMMARY.md | 10 +++
books/tutorials/mailchain/book.json | 4 ++
books/tutorials/mailchain/configure.md | 90 ++++++++++++++++++++++++++
books/tutorials/mailchain/logout.md | 19 ++++++
books/tutorials/mailchain/prompt.md | 74 +++++++++++++++++++++
books/tutorials/mailchain/send.md | 33 ++++++++++
books/tutorials/mailchain/session.md | 65 +++++++++++++++++++
books/tutorials/mailchain/setup.md | 35 ++++++++++
books/tutorials/mailchain/verify.md | 74 +++++++++++++++++++++
10 files changed, 460 insertions(+)
create mode 100644 books/tutorials/mailchain/README.md
create mode 100644 books/tutorials/mailchain/SUMMARY.md
create mode 100644 books/tutorials/mailchain/book.json
create mode 100644 books/tutorials/mailchain/configure.md
create mode 100644 books/tutorials/mailchain/logout.md
create mode 100644 books/tutorials/mailchain/prompt.md
create mode 100644 books/tutorials/mailchain/send.md
create mode 100644 books/tutorials/mailchain/session.md
create mode 100644 books/tutorials/mailchain/setup.md
create mode 100644 books/tutorials/mailchain/verify.md
diff --git a/books/tutorials/mailchain/README.md b/books/tutorials/mailchain/README.md
new file mode 100644
index 00000000..ef01d9e2
--- /dev/null
+++ b/books/tutorials/mailchain/README.md
@@ -0,0 +1,56 @@
+# Introduction
+
+We will build a todo list app in this tutorial, complete with functionality that
+allows users to sign in with Mailchain, the communication layer for Web3 offering
+completely private Web3 email. By following along with this tutorial, you will
+learn how to use Passport for authentication.
+
+If you want to see where we are headed, here's an example of the final result:
+[https://github.com/passport/todos-express-mailchain](https://github.com/passport/todos-express-mailchain)
+
+Before we dive in, you'll need a working development environment with [Node.js](https://nodejs.org/)
+and [Git](https://git-scm.com/), as well as an editor and terminal of your
+choosing. Take a moment to set up these tools if you have not already done so.
+
+You'll also need a [Mailchain](https://mailchain.com/) account. If you don't
+already have one, it's free to sign up.
+
+Let's get started!
+
+We are going to start with a starter app, which has all the scaffolding needed
+to build a todo list. Let's clone the app:
+
+```sh
+$ git clone https://github.com/passport/todos-express-starter.git mailchain-tutorial
+```
+
+You now have a directory named `'mailchain-tutorial'`. Let's `cd` into it:
+
+```sh
+$ cd mailchain-tutorial
+```
+
+Take a moment browse through the files in the starter app. As we work through
+this tutorial, we'll be using [Express](https://expressjs.com/) as our web
+framework, along with [EJS](https://ejs.co/) as our template engine and [CSS](https://developer.mozilla.org/en-US/docs/Web/CSS)
+for styling. We will use [SQLite](https://github.com/mapbox/node-sqlite3) as
+our database for storing data. Don't worry if you are not familiar with these
+technologies -- the necessary code will be provided at each step.
+
+Now, let's install the dependencies:
+
+```sh
+$ npm install
+```
+
+And start the server:
+
+```
+$ npm start
+```
+
+Let's check to see if its working. Open [http://localhost:3000](http://localhost:3000)
+in your browser. You should be greeted with a page explaining how todos help
+you get things done.
+
+Next, we will [add a login page](prompt/) to the app.
diff --git a/books/tutorials/mailchain/SUMMARY.md b/books/tutorials/mailchain/SUMMARY.md
new file mode 100644
index 00000000..83a51ce4
--- /dev/null
+++ b/books/tutorials/mailchain/SUMMARY.md
@@ -0,0 +1,10 @@
+# Summary
+
+* [Introduction](README.md)
+* [Login Prompt](prompt.md)
+* [Set Up Mailchain](setup.md)
+* [Configure Strategy](configure.md)
+* [Send Mailchain Message](send.md)
+* [Verify Mailchain Address](verify.md)
+* [Establish Session](session.md)
+* [Log Out](logout.md)
diff --git a/books/tutorials/mailchain/book.json b/books/tutorials/mailchain/book.json
new file mode 100644
index 00000000..1d348e0e
--- /dev/null
+++ b/books/tutorials/mailchain/book.json
@@ -0,0 +1,4 @@
+{
+ "title": "Mailchain Magic Link Tutorial",
+ "description": "In this tutorial you will build an Express app that lets users log in using their Mailchain or web3 email address by clicking a magic link."
+}
diff --git a/books/tutorials/mailchain/configure.md b/books/tutorials/mailchain/configure.md
new file mode 100644
index 00000000..597a09b8
--- /dev/null
+++ b/books/tutorials/mailchain/configure.md
@@ -0,0 +1,90 @@
+# Configure Strategy
+
+Now that we've set up Mailchain, we are ready to configure Passport and the
+`passport-magic-link` strategy.
+
+Install the necessary dependencies:
+
+```sh
+$ npm install passport
+$ npm install passport-magic-link
+$ npm install @mailchain/sdk
+```
+
+Open `'routes/auth.js'` and `require` the newly installed packages at line 2,
+below where `express` is `require`'d:
+
+```js
+var express = require('express');
+var passport = require('passport');
+var MagicLinkStrategy = require('passport-magic-link').Strategy;
+var Mailchain = require('@mailchain/sdk').Mailchain;
+var db = require('../db');
+```
+
+The app's database is also `require`'d.
+
+Add the following code at line 8 to configure the `MagicLinkStrategy`.
+
+```
+var mailchain = Mailchain.fromSecretRecoveryPhrase(process.env.SECRET_RECOVERY_PHRASE);
+let fromAddress = process.env['FROM_ADDRESS'] || await mailchain.user().address;
+passport.use(new MagicLinkStrategy({
+ secret: 'keyboard cat', // change this to something secret
+ userFields: [ 'mailchain_address' ],
+ tokenField: 'token',
+ verifyUserAfterToken: true
+}, async function send(user, token) {
+ var link = 'http://localhost:3000/login/mailchain/verify?token=' + token;
+
+ var msg = {
+ to: [ user.mailchain_address ],
+ from: fromAddress,
+ subject: 'Sign in to Todos',
+ content: {
+ text: 'Hello! Click the link below to finish signing in to Todos.\r\n\r\n' + link,
+ html: '
Hello!
Click the link below to finish signing in to Todos.
',
+ }
+ };
+ return await mailchain.sendMail(msg);
+}, function verify(user) {
+ return new Promise(function(resolve, reject) {
+ db.get('SELECT * FROM users WHERE mailchain_address = ?', [
+ user.mailchain_address
+ ], function(err, row) {
+ if (err) { return reject(err); }
+ if (!row) {
+ db.run('INSERT INTO users (mailchain_address, mailchain_address_verified) VALUES (?, ?)', [
+ user.mailchain_address,
+ 1
+ ], function(err) {
+ if (err) { return reject(err); }
+ var id = this.lastID;
+ var obj = {
+ id: id,
+ mailchain_address: user.mailchain_address
+ };
+ return resolve(obj);
+ });
+ } else {
+ return resolve(row);
+ }
+ });
+ });
+}));
+```
+
+This configures the `MagicLinkStrategy` to send mails containing a magic link
+using Mailchain. When the user clicks on the magic link, the user record
+associated with the Mailchain address will be found. If a user record does not
+exist, one is created the first time someone signs in.
+
+We also need to update our database scheme. Open `'db.js'` and insert the following at line 16:
+```
+mailchain_address TEXT UNIQUE, \
+mailchain_address_verified INTEGER, \
+```
+
+
+The strategy is now configured. Next we need to [send the user a magic link](../send/)
+when they click "Sign in with Mailchain."
\ No newline at end of file
diff --git a/books/tutorials/mailchain/logout.md b/books/tutorials/mailchain/logout.md
new file mode 100644
index 00000000..56d76576
--- /dev/null
+++ b/books/tutorials/mailchain/logout.md
@@ -0,0 +1,19 @@
+# Log Out
+
+Now that users can sign in, they'll need a way to sign out.
+
+Open `'routes/auth.js'` and add this route at line 84, below the
+`'/login/mailchain/verify'` route:
+
+```js
+router.post('/logout', function(req, res, next) {
+ req.logout(function(err) {
+ if (err) { return next(err); }
+ res.redirect('/');
+ });
+});
+```
+
+Return to the app, where you should already be signed in, and click "Sign out."
+
+We've now got a working app where users can sign in and sign out!
diff --git a/books/tutorials/mailchain/prompt.md b/books/tutorials/mailchain/prompt.md
new file mode 100644
index 00000000..bbbd30a0
--- /dev/null
+++ b/books/tutorials/mailchain/prompt.md
@@ -0,0 +1,74 @@
+# Login Prompt
+
+We want to let users sign in with their Mailchain account or any valid Mailchain
+address. For that, we need a login page that prompts the user to enter their
+Mailchain address. Let's add that now.
+
+Let's create a file that will contain authentication-related routes:
+
+```sh
+$ touch routes/auth.js
+```
+
+Add the following code to that file, which creates a login route that will
+render the login page.
+
+```js
+var express = require('express');
+
+var router = express.Router();
+
+router.get('/login', function(req, res, next) {
+ res.render('login');
+});
+
+module.exports = router;
+```
+
+Next, we need to add this route to the app. Open `'app.js'` and `require` the
+newly created auth routes at line 10, below where `'routes/index'` is
+`require`'d:
+
+```js
+var indexRouter = require('./routes/index');
+var authRouter = require('./routes/auth');
+```
+
+Continuing within `'app.js'`, use the newly `require`'d `authRouter` at line 27,
+below where `indexRouter` is `use`'d.
+
+```js
+app.use('/', indexRouter);
+app.use('/', authRouter);
+```
+
+The login page has been added to our app! Let's see how it looks.
+
+Start the server:
+
+```sh
+$ npm start
+```
+
+And open [http://localhost:3000](http://localhost:3000) and click "Sign in." We
+are prompted to sign in, but there's no place to enter a Mailchain address.
+
+For that we need an HTML form. Let's add that now.
+
+Open `'views/login.ejs'` and add the form at line 15, below the "Sign in"
+heading:
+
+```html
+
Sign in
+
+```
+
+Refresh the page. We've now got a login page that prompts the user to sign in
+with Mailchain. Next, we will [set up Mailchain](../setup/), in preparation for
+sending the user a magic link.
diff --git a/books/tutorials/mailchain/send.md b/books/tutorials/mailchain/send.md
new file mode 100644
index 00000000..1d361734
--- /dev/null
+++ b/books/tutorials/mailchain/send.md
@@ -0,0 +1,33 @@
+# Send Web3 Email
+
+Now that we are prompting the user for their Mailchain address, and have the
+strategy configured, the next step is to send the user a Mailchain message when
+they click "Sign in with Mailchain."
+
+Open `'routes/auth.js'`, add this route at line 57, below the `'/login'` route:
+
+```js
+router.post('/login/mailchain', passport.authenticate('magiclink', {
+ action: 'requestToken',
+ failureRedirect: '/login'
+}), function(req, res, next) {
+ res.redirect('/login/mailchain/check');
+});
+```
+
+This route will process the form on the login page and send a Mailchain message to
+the user.
+
+Continuing within `'routes/auth.js'`, add this route at line 63, below the newly
+added `'/login/mailchain'` route:
+
+```js
+router.get('/login/mailchain/check', function(req, res, next) {
+ res.render('login/mailchain/check');
+});
+```
+
+This route will render a page instructing the user to check their Mailchain account
+and click the link.
+
+Next, we will [verify the Mailchain address](../verify/) when the user clicks that link.
diff --git a/books/tutorials/mailchain/session.md b/books/tutorials/mailchain/session.md
new file mode 100644
index 00000000..ca6f93ec
--- /dev/null
+++ b/books/tutorials/mailchain/session.md
@@ -0,0 +1,65 @@
+# Establish Session
+
+Once we've verified the user's Mailchain address, we need a login session to
+remember the fact that the user has authenticated as they navigate the app.
+
+To do that, we'll add session support. Begin by installing the necessary
+dependencies:
+
+```sh
+$ npm install express-session
+$ npm install connect-sqlite3
+```
+
+Open `'app.js'` and `require` the additional dependencies at line 8, below
+where `'morgan'` is `require`'d:
+
+```js
+var logger = require('morgan');
+var passport = require('passport');
+var session = require('express-session');
+
+var SQLiteStore = require('connect-sqlite3')(session);
+```
+
+Add the following code at line 29, after `express.static` middleware, to
+maintain and authenticate the session.
+
+```js
+app.use(express.static(path.join(__dirname, 'public')));
+app.use(session({
+ secret: 'keyboard cat',
+ resave: false,
+ saveUninitialized: false,
+ store: new SQLiteStore({ db: 'sessions.db', dir: './var/db' })
+}));
+app.use(passport.authenticate('session'));
+```
+
+Finally, we need to configure Passport to manage the login session. Open
+`'routes/auth.js'` and add the following code at line 53:
+
+```js
+passport.serializeUser(function(user, cb) {
+ process.nextTick(function() {
+ cb(null, { id: user.id, mailchain_address: user.mailchain_address });
+ });
+});
+
+passport.deserializeUser(function(user, cb) {
+ process.nextTick(function() {
+ return cb(null, user);
+ });
+});
+```
+
+Now, let's retry signing in. Open [http://localhost:3000](http://localhost:3000),
+click "Sign in", enter your Mailchain address and click "Sign in with Mailchain".
+
+Now, check your Mailchain Inbox and click the link.
+
+We are logged in! Go ahead and enter some tasks you've been needing to get
+done.
+
+At this point, users can sign in with Mailchain! Next, we will add the ability to
+[sign out](../logout/).
diff --git a/books/tutorials/mailchain/setup.md b/books/tutorials/mailchain/setup.md
new file mode 100644
index 00000000..5c4d472a
--- /dev/null
+++ b/books/tutorials/mailchain/setup.md
@@ -0,0 +1,35 @@
+# Set Up Mailchain
+
+Before we can let users sign in to our app with web3 email, we need a way to send
+Mailchain messages.
+
+For that, we are going to use [Mailchain](https://mailchain.com/).
+
+For the purposes of this tutorial, we suggest you create a Mailchain account for testing.
+
+Navigate to [Mailchain](https://app.mailchain.com/register) and follow the steps to register.
+
+You will be prompted to save your Secret Recovery Phrase, which we will use later.
+
+You can also retrieve your Secret Recovery Phrase at any time by navigating to
+**Settings > [Secret Recovery Phrase](https://app.mailchain.com/settings/security/)**.
+Then click **View** and enter your password to retrieve your Secret Recovery Phrase.
+
+Save your Secret Recovery Phrase somewhere safe and private. We will use it later.
+
+Now that we have the Secret Recovery Phrase and a Mailchain address, let's create a
+`'.env'` file to store them.
+
+```sh
+$ touch .env
+```
+
+Add your FROM_ADDRESS and API key. The contents of the file should look something like
+this:
+
+```sh
+FROM_ADDRESS=user@mailchain.com
+SECRET_RECOVERY_PHRASE=__INSERT_SECRET_RECOVERY_PHRASE__
+```
+
+Next we will [configure the strategy](../configure/).
diff --git a/books/tutorials/mailchain/verify.md b/books/tutorials/mailchain/verify.md
new file mode 100644
index 00000000..f6f14140
--- /dev/null
+++ b/books/tutorials/mailchain/verify.md
@@ -0,0 +1,74 @@
+# Verify Mailchain Address
+
+Now that we've sent the user a Mailchain message with a magic link, the next step is
+to verify the Mailchain address when they click the link.
+
+Open `'routes/auth.js'`, add this route at line 66, below the `'/login/mailchain/check'`
+route:
+
+```js
+router.get('/login/mailchain/verify', passport.authenticate('magiclink', {
+ successReturnToOrRedirect: '/',
+ failureRedirect: '/login'
+}));
+```
+
+This route will verify the Mailchain address when the link is clicked.
+
+Now create a folder and corresponding view for our route. Run the following commands:
+
+```
+$ mkdir views/login/mailchain
+$ touch views/login/mailchain/check.ejs
+```
+
+Open `'views/login/mailchain/check.ejs'`, add the following code:
+
+```
+
+
+
+
+
+ Express • TodoMVC
+
+
+
+
+
+
+
todos
+
Check your Mailchain Inbox
+
We sent a magic link to your Mailchain address. Click the link in that message to sign in.
+```
+
+Let's test it out to see what happens.
+
+Start the server:
+
+```sh
+$ npm start
+```
+
+Open [http://localhost:3000](http://localhost:3000), click "Sign in", enter your
+Mailchain address and click "Sign in with Mailchain".
+
+Now, check your Mailchain inbox and click the link.
+
+Uh oh... we are informed that there's an error related to sessions. Next, we
+will fix that by [establishing a session](../session/).
From 8aeda75cb3a2f20e7df0722acd5a0aab92fc610c Mon Sep 17 00:00:00 2001
From: Tim Boeckmann <2022378+tboeckmann@users.noreply.github.com>
Date: Sun, 12 Feb 2023 20:15:22 +0000
Subject: [PATCH 2/9] update intro
---
books/tutorials/mailchain/README.md | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)
diff --git a/books/tutorials/mailchain/README.md b/books/tutorials/mailchain/README.md
index ef01d9e2..baae9869 100644
--- a/books/tutorials/mailchain/README.md
+++ b/books/tutorials/mailchain/README.md
@@ -1,8 +1,16 @@
# Introduction
-We will build a todo list app in this tutorial, complete with functionality that
-allows users to sign in with Mailchain, the communication layer for Web3 offering
-completely private Web3 email. By following along with this tutorial, you will
+Mailchain is the communication layer for Web3 offering private, end-to-end encrypted email using
+blockchain wallet addresses and web3 identities (e.g. an Ethereum address, ENS name, Unstoppable
+Domains name, etc.). Instead of users needing to sign data with a blockchain wallet, passwordless
+authentication with magic links for Passport.js improves the safety of users:
+
+* User signup and login without passwords or needing to sign data
+* Supports magic links sent to ENS names, Ethereum addresses, Mailchain accounts and more
+* Handles secure token generation, expiration and confirmation
+
+In this tutorial, we will build a todo list app in this tutorial, complete with functionality
+that allows users to sign in with Mailchain. By following along with this tutorial, you will
learn how to use Passport for authentication.
If you want to see where we are headed, here's an example of the final result:
From f33a5ad657b5c863c4176230b82d06afc8f88f96 Mon Sep 17 00:00:00 2001
From: Tim Boeckmann <2022378+tboeckmann@users.noreply.github.com>
Date: Sun, 12 Feb 2023 20:18:02 +0000
Subject: [PATCH 3/9] update formatting
---
books/tutorials/mailchain/prompt.md | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/books/tutorials/mailchain/prompt.md b/books/tutorials/mailchain/prompt.md
index bbbd30a0..53331bca 100644
--- a/books/tutorials/mailchain/prompt.md
+++ b/books/tutorials/mailchain/prompt.md
@@ -63,7 +63,8 @@ heading:
From 2189f5ca339de6c469c3ab84df925b2fff1ee518 Mon Sep 17 00:00:00 2001
From: Tim Boeckmann
Date: Tue, 14 Feb 2023 14:02:10 +0000
Subject: [PATCH 4/9] update line numbers, formatting and flow
---
books/tutorials/mailchain/prompt.md | 20 +++-----------------
books/tutorials/mailchain/send.md | 2 +-
books/tutorials/mailchain/setup.md | 5 +----
3 files changed, 5 insertions(+), 22 deletions(-)
diff --git a/books/tutorials/mailchain/prompt.md b/books/tutorials/mailchain/prompt.md
index 53331bca..f4e9eeec 100644
--- a/books/tutorials/mailchain/prompt.md
+++ b/books/tutorials/mailchain/prompt.md
@@ -26,11 +26,10 @@ module.exports = router;
```
Next, we need to add this route to the app. Open `'app.js'` and `require` the
-newly created auth routes at line 10, below where `'routes/index'` is
+newly created auth route at line 10, below where `'routes/index'` is
`require`'d:
```js
-var indexRouter = require('./routes/index');
var authRouter = require('./routes/auth');
```
@@ -38,28 +37,15 @@ Continuing within `'app.js'`, use the newly `require`'d `authRouter` at line 27,
below where `indexRouter` is `use`'d.
```js
-app.use('/', indexRouter);
app.use('/', authRouter);
```
-The login page has been added to our app! Let's see how it looks.
+Now we will update the login page so the use can enter a Mailchain address.
-Start the server:
-
-```sh
-$ npm start
-```
-
-And open [http://localhost:3000](http://localhost:3000) and click "Sign in." We
-are prompted to sign in, but there's no place to enter a Mailchain address.
-
-For that we need an HTML form. Let's add that now.
-
-Open `'views/login.ejs'` and add the form at line 15, below the "Sign in"
+Open `'views/login.ejs'` and add the form at line 15, below `