Introduction
We will build a todo list app in this tutorial, using Auth0 for sign in and sign up to our app. In this tutorial, you will learn how to use Passport to integrate with Auth0.
If you want to see where we are headed, here's an example of the final result: https://github.com/passport/todos-express-auth0
Before we dive in, you'll need a working development environment with Node.js and Git, 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 an Auth0 account. If you don't already have one, sign up now. This tutorial can be completed using Auth0's free plan.
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:
$ git clone https://github.com/passport/todos-express-starter.git auth0-tutorial
You now have a directory named 'auth0-tutorial'
. Let's cd
into
it:
$ cd auth0-tutorial
Take a moment browse through the files in the starter app. As we work through this tutorial, we'll be using Express as our web framework, along with EJS as our template engine and CSS for styling. We will use SQLite 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:
$ npm install
And start the server:
$ npm start
Let's check to see if its working. Open http://localhost:3000 in your browser. You should be greeted with a page explaining how todos help you get things done.
We are going to make the sign in button work. But first, we need to create an app in Auth0.
Create App
Before we can use Auth0 for sign in, we need to create an app in Auth0.
Go to the Auth0 Dashboard.
Navigate to Applications.
If you have an existing application, it will be listed on the applications screen. Click the application to obtain the client ID and secret, and proceed to configure the strategy. Otherwise, continue.
Click Create Application.
Enter a name for the application and choose Regular Web Applications as the application type. Click the Create button.
On the following screen, click the Settings tab. Scroll down and find the
Allowed Callback URLs text area. Enter 'http://localhost:3000/oauth2/redirect'
.
Below that, find the Allowed Logout URLs text area. Enter
'http://localhost:3000/'
. Scroll down futher and click Save Changes.
Scroll up to the top and find the Domain, Client ID, and Client Secret values for the newly created app. Next, we will use these values to configure the strategy.
Configure Strategy
Now that we've created an app in Auth0, we can configure Passport to integrate with Auth0.
First, let's create a '.env'
file to store the domain, client ID, and client
secret we just obtained from Auth0.
$ touch .env
Then, add the domain, client ID and secret. The contents of the file should look something like this:
AUTH0_DOMAIN=__INSERT_DOMAIN_HERE__
AUTH0_CLIENT_ID=__INSERT_CLIENT_ID_HERE__
AUTH0_CLIENT_SECRET=__INSERT_CLIENT_SECRET_HERE__
For this integration, we are going to use Passport and the
passport-openidconnect
strategy. Install both as dependencies:
$ npm install passport
$ npm install passport-openidconnect
Now, let's create a file that will contain authentication-related functionality:
$ touch routes/auth.js
Add the following code to that file, which configures the strategy to work with Auth0.
var passport = require('passport');
var OpenIDConnectStrategy = require('passport-openidconnect');
passport.use(new OpenIDConnectStrategy({
issuer: 'https://' + process.env['AUTH0_DOMAIN'] + '/',
authorizationURL: 'https://' + process.env['AUTH0_DOMAIN'] + '/authorize',
tokenURL: 'https://' + process.env['AUTH0_DOMAIN'] + '/oauth/token',
userInfoURL: 'https://' + process.env['AUTH0_DOMAIN'] + '/userinfo',
clientID: process.env['AUTH0_CLIENT_ID'],
clientSecret: process.env['AUTH0_CLIENT_SECRET'],
callbackURL: '/oauth2/redirect',
scope: [ 'profile' ]
}, function verify(issuer, profile, cb) {
return cb(null, profile);
}));
Now that the strategy is configured, we are ready to add login routes to the app.
Add Routes
When the user clicks the "Sign in" button, they will be redirected to our app's sign in page, which is hosted by Auth0. Once on that page, the user will log in. After they've logged in, the user will be redirected back to our app.
Open 'routes/auth.js'
and add the following code at the end of the file, which
creates two routes. The first will redirect the user to the sigin page. The
second will process the authentication result when the user is redirected back.
var express = require('express');
var qs = require('querystring');
var router = express.Router();
router.get('/login', passport.authenticate('openidconnect'));
router.get('/oauth2/redirect', passport.authenticate('openidconnect', {
successRedirect: '/',
failureRedirect: '/login'
}));
module.exports = router;
Next, we need to add these routes to our app. Open 'app.js'
and require
the
newly created auth routes at line 10, below where 'routes/index'
is
require
'd:
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.
app.use('/', indexRouter);
app.use('/', authRouter);
The routes have been added to the app. Next we need to maintain state when redirecting to Auth0.
Maintain State
When a user signs in to our app via our app's Auth0-hosted sign in page, they are redirected to Auth0. Auth0 takes care of authenticating the user and then redirects them back to our app.
For security, state needs to be maintained between these two redirects. Passport does this automatically, but the app first needs session support. Let's add that now.
Begin by installing the necessary dependencies:
$ 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:
var logger = require('morgan');
var session = require('express-session');
var SQLiteStore = require('connect-sqlite3')(session);
Add the following code at line 28, after express.static
middleware, to
add sessions to the application.
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' })
}));
Now that the app can maintain state, the final step is establishing a login session.
Establish Session
Once the user has signed in via Auth0, our app needs a login session to remember who the user is as they navigate the app.
Open 'app.js'
and require
Passport at line 9, below where
'express-session'
is require
'd:
var session = require('express-session');
var passport = require('passport');
Add the following code at line 35, after session
middleware, to authenticate
the session.
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 17:
passport.serializeUser(function(user, cb) {
process.nextTick(function() {
cb(null, { id: user.id, username: user.username, name: user.displayName });
});
});
passport.deserializeUser(function(user, cb) {
process.nextTick(function() {
return cb(null, user);
});
});
Now, let's try signing in.
Start the server:
npm start
Open http://localhost:3000 and click "Sign in."
If this is your first time signing in, go ahead and sign up. Otherwise enter the email address and password for your account.
We are logged in! Go ahead and enter some tasks you've been needing to get done.
At this point, users can sign in and sign up to our app! Next, we will add the ability to sign out.
Log Out
Now that users can sign in and sign up, they'll need a way to sign out.
Open 'routes/auth.js'
and add this route at line 40, below the
'/oauth2/redirect'
route:
router.post('/logout', function(req, res, next) {
req.logout(function(err) {
if (err) { return next(err); }
var params = {
client_id: process.env['AUTH0_CLIENT_ID'],
returnTo: 'http://localhost:3000/'
};
res.redirect('https://' + process.env['AUTH0_DOMAIN'] + '/v2/logout?' + qs.stringify(params));
});
});
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, sign up, and sign out!