diff --git a/books/tutorials/mailchain/README.md b/books/tutorials/mailchain/README.md new file mode 100644 index 00000000..a61aedd0 --- /dev/null +++ b/books/tutorials/mailchain/README.md @@ -0,0 +1,69 @@ +# Introduction + +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. + +Key feature include: + +- Users can signup and login to your application without passwords or needing to sign data with their wallets. +- Mailchain supports magic links sent to ENS names, Ethereum addresses, Mailchain accounts and more. +- Passport handles secure token generation, expiration and confirmation. + +In this tutorial, we will build a todo list app, 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 passwordless authentication using Mailchain to send magic links. + +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: + +1. A working development environment with [Node.js](https://nodejs.org/) +and [Git](https://git-scm.com/) +1. An editor and terminal of your +choosing. +1. A [Mailchain](https://mailchain.com/) account. If you don't already have one, it's free to sign up. + +Take a moment to set these up if you have not already done so. + +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: + +```sh +$ 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..07890cb5 --- /dev/null +++ b/books/tutorials/mailchain/configure.md @@ -0,0 +1,96 @@ +# 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 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`. + +```js +var mailchain = Mailchain.fromSecretRecoveryPhrase(process.env.SECRET_RECOVERY_PHRASE); +var fromAddress = process.env['FROM_ADDRESS'] || mailchain.user().address; +let createMailchainAddress = function(address) { + switch (address) { + case address.match(/^[\d\w\-\_]*@mailchain\.com$/)?.input: // Mailchain address: + return address + case address.match(/^0x[a-fA-F0-9]{40}$/)?.input: // Ethereum address: + return address + '@ethereum.mailchain.com' + case address.match(/^.*\.eth$/)?.input: // ENS address: + return address + '@ens.mailchain.com' + case address.match(/^.*\.*@mailchain$/)?.input: // Mailchain address without .com: + return address + '.com' + default: + console.error("Invalid 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: [ createMailchainAddress(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: '
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 sanitize the input address, then 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. + +The strategy is now configured. Next we need to +[send the user a magic link](../send/) when they click "Sign in with Mailchain" 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..f4e9eeec --- /dev/null +++ b/books/tutorials/mailchain/prompt.md @@ -0,0 +1,61 @@ +# 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 route at line 10, below where `'routes/index'` is +`require`'d: + +```js +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('/', authRouter); +``` + +Now we will update the login page so the use can enter a Mailchain address. + +Open `'views/login.ejs'` and add the form at line 15, below `+ We sent a magic link to your Mailchain address. Click the link in that + message to sign in. +
++ Didn't receive the message? Get another link +
+