Update 5/11/2016: Writing Node.js code? Check out our Node Greatest Hits after reading this one:
- A 15-Minute App Tutorial for AngularJS + Node.js + Stormpath,
- Create a Simple Node.js Web App with Express.js, Bootstrap and Stormpath
- Everything you ever wanted to know about sessions in Node, but were afraid to ask,
- Choosing Your Node.js Authentication Strategy,
- Making Express.js Authentication Fun Again!
UPDATE: Since I wrote this post several months back — I’ve actually built a brand new library called express-stormpath which does everything you can see below — and more! If you’d like to use our new library which is much simpler, you should read this blog post about it — otherwise, keep reading!
Since the release of our new node.js Stormpath Library I’ve been itching to get my hands dirty and build something.
So — let’s build something together 🙂
NOTE: If you’d like to skip the Stormpath tutorial below and jump straight into the code, I’ve put this app on Github for your viewing pleasure: https://github.com/stormpath/stormpath-passport-express-sample
What We’re Building
I thought a great way to test out the new Stormpath node libraries would be to build a simple website that allows you to do a few things:
- Create a new account (register) with email and password.
- Log into your new account (login) with email and password.
- Display a dashboard page once you’ve logged in, that is only accessible to users with an account.
- Redirect unauthenticated users who try to access the dashboard back to the login page.
- Allow a logged in user to log out of their account (logout).
Pretty simple — but that should at least allow us to play around with the new tooling!
In the end, things should look like this:
Sound good? I thought so!
Intro to Stormpath
If you aren’t already familiar with Stormpath, it’s an API service that allows you to create, edit, and securely store your application’s user accounts and user account data. Stormpath makes it easy to do stuff like:
- User registration and login.
- Account verification via email.
- Password reset via email.
- Social login.
- And a bunch of other cool stuff you probably don’t like coding!
So — why should you use Stormpath?
Well, quite simply, we make building user accounts a lot easier, more secure, and more scalable than what you’re probably used to.
Using Stormpath not only allows you to easily build your application out, but it also allows you to scale your site to support millions of users without changing your code, or even needing a database!
So, let’s dive in.
If you don’t already have a Stormpath account and application, you’ll need to create one now — you can do so here: https://api.stormpath.com/register
The rest of this article will assume you have a Stormpath account and API key pair.
The Tools
For this simple app tutorial, we’ll be using
If you’re not familiar with express, you should check it out! express is an awesome web framework for node.js which makes building full-fledged web applications a lot simpler.
One of the most important pieces of express is the middleware layer (powered by connect). This middleware allows you to tap into HTTP requests before they reach your view code, allowing you to do cool stuff like authenticate users, among other things.
Next up is passport. Passport is a generic library for handling user authentication in node web apps. It allows you to abstract away some of the technical details related to handling web session and authentication. In this Node.js, Passport & Express example, we’ll be using it in combination with the official passport-stormpath backend to log users in and out of our new web app.
Lastly, we’ll also be using the official Stormpath node library to register new users (essentially we’ll be sending Stormpath’s API service a POST request when a new user creates an account, this way the user will be stored securely on Stormpath’s servers).
Getting Started
So, now that we’ve covered what this web app will be doing, what it’ll look like, and what we’ll use to build it: let’s get this going!
The first thing you need to do is install node.js (if you don’t already have it installed).
Next, let’s install express.js by running:
1 2 |
$ npm install -g express-generator |
The express-generator
module allows you to easily generate a project skeleton to get started with (yey!).
Now that we have express, let’s create our basic project directory:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
$ express --css stylus stormpath-express-sample create : stormpath-express-sample create : stormpath-express-sample/package.json create : stormpath-express-sample/app.js create : stormpath-express-sample/public create : stormpath-express-sample/public/stylesheets create : stormpath-express-sample/public/stylesheets/style.styl create : stormpath-express-sample/views create : stormpath-express-sample/views/index.jade create : stormpath-express-sample/views/layout.jade create : stormpath-express-sample/views/error.jade create : stormpath-express-sample/routes create : stormpath-express-sample/routes/index.js create : stormpath-express-sample/routes/users.js create : stormpath-express-sample/public/javascripts create : stormpath-express-sample/public/images create : stormpath-express-sample/bin create : stormpath-express-sample/bin/www install dependencies: $ cd stormpath-express-sample && npm install run the app: $ DEBUG=stormpath-express-sample:* npm start |
Then, of course, you’ll want to go into your project directory and install all of the basic dependencies:
1 2 3 |
$ cd stormpath-express-sample $ npm install |
And that’s it for the basic configuration!
Install Required Modules
The next thing we’ll want to do is install all of the required modules we’re going to be using.
You can install them all by running the following command:
1 2 |
$ npm install passport passport-stormpath express-session connect-flash stormpath static-favicon --save |
This command will install:
- passport.js: the authentication framework.
- passport-stormpath: the official Stormpath backend for passport.js.
- express-session: a session management tool.
- connect-flash: a simple flash library which works with the express.js framework.
- stormpath: the official Stormpath node library.
NOTE: The --save
flag instructs node to install the packages, and then add them as dependencies to your project’s package.json
file. This way, if you want to bootstrap this project at a later time, you can do so easily by just running npm install
.
Set Environment Variables
Now that we’ve installed all the required modules, let’s configure our environment variables.
We’re going to be using environment variables to tell our web app what credentials / secrets to use when running our app.
You’ll want to create a .env
file in the root of your project with the following contents:
1 2 3 4 5 |
export STORMPATH_API_KEY_ID=xxx export STORMPATH_API_KEY_SECRET=xxx export STORMPATH_APP_HREF=xxx export EXPRESS_SECRET=some_random_string |
Obviously, you’ll need to replace these values with the appropriate values! The Stormpath settings (API_KEY_ID
, API_KEY_SECRET
, and APP_HREF
) will be used to tell the app how to communicate with Stormpath. The EXPRESS_SECRET
variable is used to make session management secure (this should be a long string of random characters).
If you haven’t already created a Stormpath application, you’ll want to go into the Stormpath admin console and create a new Application called stormpath-express-sample
. Once you’ve created this application, you should see an Application REST URL in the web interface — use this URL as the value for your STORMPATH_APP_HREF
environment variable.
Once you’ve got the environment variables defined in your .env
file, all you need to do is use them:
1 2 |
$ source .env |
The source
command will run the .env
file, setting the environment variables in your current terminal session.
Configure Middleware
The next thing we need to do is open up the app.js
file and import our dependencies. You’ll want to make the top of your app.js
file look like the below:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
var express = require('express'); var path = require('path'); var favicon = require('static-favicon'); var logger = require('morgan'); var cookieParser = require('cookie-parser'); var bodyParser = require('body-parser'); // These are the new imports we're adding: var passport = require('passport'); var StormpathStrategy = require('passport-stormpath'); var session = require('express-session'); var flash = require('connect-flash'); |
Now that we have our dependencies imported, let’s configure them!
In app.js
, you’ll want to add the following:
1 2 3 4 5 6 7 8 |
var app = express(); // Here is what we're adding: var strategy = new StormpathStrategy(); passport.use(strategy); passport.serializeUser(strategy.serializeUser); passport.deserializeUser(strategy.deserializeUser); |
Next, we need to configure some of our middleware in app.js
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
app.use(favicon()); app.use(logger('dev')); app.use(bodyParser.json()); app.use(bodyParser.urlencoded()); app.use(cookieParser()); app.use(require('stylus').middleware(path.join(__dirname, 'public'))); app.use(express.static(path.join(__dirname, 'public'))); // Stuff we're adding: app.use(session({ secret: process.env.EXPRESS_SECRET, key: 'sid', cookie: { secure: false }, })); app.use(passport.initialize()); app.use(passport.session()); app.use(flash()); |
This just tells express to activate our middleware components.
I learned how to do all of the above by reading through the project documentation for the following libraries (it’s nothing special!):
Now we’re ready to move onto the good stuff: our routes.
Writing the Routes
Writing routes is where the real action happens.
We’re going to define several routes:
- A
/
route which just displays a simple home page. - A
/register
route which renders a registration page. This route will need to accept both GET and POST requests. - A
/login
route which will allow existing users to log in. This route will need to accept both GET and POST requests as well. - A
/logout
route which will log users out of their account. - A
/dashboard
route which will display a dashboard page for logged in users.
To keep things orderly, let’s create two separate route files.
First, open up the routes/index.js
file and replace its contents with the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
var express = require('express'); var router = express.Router(); // Render the home page. router.get('/', function(req, res) { res.render('index', { title: 'Home', user: req.user }); }); // Render the dashboard page. router.get('/dashboard', function (req, res) { if (!req.user || req.user.status !== 'ENABLED') { return res.redirect('/login'); } res.render('dashboard', { title: 'Dashboard', user: req.user }); }); module.exports = router; |
This index.js
route file holds all of the main website routes (anything not auth related).
Next, create a new file named routes/auth.js
and add the following code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
var express = require('express'); var router = express.Router(); var passport = require('passport'); var stormpath = require('stormpath'); // Render the registration page. router.get('/register', function(req, res) { res.render('register', { title: 'Register', error: req.flash('error')[0] }); }); // Register a new user to Stormpath. router.post('/register', function(req, res) { var username = req.body.username; var password = req.body.password; // Grab user fields. if (!username || !password) { return res.render('register', { title: 'Register', error: 'Email and password required.' }); } // Initialize our Stormpath client. var apiKey = new stormpath.ApiKey( process.env['STORMPATH_API_KEY_ID'], process.env['STORMPATH_API_KEY_SECRET'] ); var spClient = new stormpath.Client({ apiKey: apiKey }); // Grab our app, then attempt to create this user's account. spClient.getApplication(process.env['STORMPATH_APP_HREF'], function(err, app) { if (err) throw err; app.createAccount({ givenName: 'John', surname: 'Smith', username: username, email: username, password: password, }, function (err, createdAccount) { if (err) { return res.render('register', { title: 'Register', error: err.userMessage }); } passport.authenticate('stormpath')(req, res, function () { return res.redirect('/dashboard'); }); }); }); }); // Render the login page. router.get('/login', function(req, res) { res.render('login', { title: 'Login', error: req.flash('error')[0] }); }); // Authenticate a user. router.post('/login', passport.authenticate('stormpath', { successRedirect: '/dashboard', failureRedirect: '/login', failureFlash: 'Invalid email or password.' })); // Logout the user, then redirect to the home page. router.get('/logout', function(req, res) { req.logout(); res.redirect('/'); }); module.exports = router; |
This auth.js
route file contains all the routes that handle user-specific stuff: registration, login, and logout.
Now that we’ve created our routes, we have to also plug them into the main app.js
file so they’ll be used by express.
First, remove the existing routes near the top of your app.js
file:
1 2 3 4 5 6 7 |
app.use(passport.session()); app.use(flash()); // Remove these two lines. var routes = require('./routes/index'); var users = require('./routes/users'); |
Next, add in the following two lines to replace the ones you just removed:
1 2 3 |
var index_routes = require('./routes/index'); var auth_routes = require('./routes/auth'); |
After importing the routes, we’ll also need to bind them to express’ URL routing mechanism:
1 2 3 4 5 6 7 |
app.use(passport.session()); app.use(flash()); // Specify the routes here. app.use('/', index_routes); app.use('/', auth_routes); |
Then, delete the existing routes that were there — the lines you’ll want to remove are the following:
1 2 3 4 |
// Remove these two lines: app.use('/', routes); app.use('/users', users); |
And with the changes above made — we’ve just successfully finished writing our routes! In the next section, we’ll dive into understanding the routes, and explain why things work the way they do.
Understanding the Routes
Now that we’ve defined our routes let’s see how they work!
The Home Page Route
Let’s start by looking at the home page route:
1 2 3 4 5 |
// Render the home page. router.get('/', function(req, res) { res.render('index', { title: 'Home', user: req.user }); }); |
The home page route isn’t doing much other than rendering a template (which we have yet to create!), and passing in some variable values.
The important thing to note here is the title
and user
values. We’ll use the title
variable in our template to generate a nice HTML title for the page. We’ll also use the user
variable to tell us whether or not the person viewing the home page is a user or not.
If the person viewing the page IS a user, then instead of displaying a Login button on the website, we’ll just display a link to the Dashboard page.
The req.user
variable is automatically populated for us by passport. It will either be undefined
, or a JSON object of this user’s account.
The Dashboard Route
The dashboard route is also quite simple:
1 2 3 4 5 6 7 8 9 10 11 12 |
// Render the dashboard page. router.get('/dashboard', function (req, res) { if (!req.user || req.user.status !== 'ENABLED') { return res.redirect('/login'); } res.render('dashboard', { title: 'Dashboard', user: req.user, }); }); |
The first thing we’ll do here is check to see if the person viewing this page is a user, and then, if their account is enabled or not (Stormpath allows you to have users that disabled).
If the person viewing our dashboard page isn’t a valid user, we’ll redirect the user to the login page.
If the person IS a valid user, we’ll render our dashboard page (simple — right?).
The Registration Route
Now we’ll dive into the authentication routes. We’ll start by looking at our registration route — which is responsible for signing up new users.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
// Render the registration page. router.get('/register', function(req, res) { res.render('register', { title: 'Register', error: req.flash('error')[0] }); }); // Register a new user to Stormpath. router.post('/register', function(req, res) { var username = req.body.username; var password = req.body.password; // Grab user fields. if (!username || !password) { return res.render('register', { title: 'Register', error: 'Email and password required.' }); } // Initialize our Stormpath client. var apiKey = new stormpath.ApiKey( process.env['STORMPATH_API_KEY_ID'], process.env['STORMPATH_API_KEY_SECRET'] ); var spClient = new stormpath.Client({ apiKey: apiKey }); // Grab our app, then attempt to create this user's account. spClient.getApplication(process.env['STORMPATH_APP_HREF'], function(err, app) { if (err) throw err; app.createAccount({ givenName: 'John', surname: 'Smith', username: username, email: username, password: password }, function (err, createdAccount) { if (err) { return res.render('register', {title: 'Register', error: err.userMessage}); } passport.authenticate('stormpath')(req, res, function () { return res.redirect('/dashboard'); }); }); }); }); |
The first bit just renders the registration page for GET requests.
The second bit is where things get interesting.
Firstly, we’re checking for a username
and password
field from the HTTP form. This is what the user will be submitting to us when they create a new account. (The username
field is actually an email address, but I’ll explain this in more detail later.)
Next, we’ll re-render the registration page (with an error message) if either the username
or password
fields are missing.
After that’s out of the way, we need to initialize the Stormpath client. This involves using our API key credentials to make a new client, and then fetching our Stormpath application (since we’ll need to create this new user account inside of our application namespace).
Once we’ve fetched our Stormpath application, we’ll then create a new user account using the createAccount
method. Since Stormpath requires (at minimum):
- givenName
- surname
- and password fields — but we only want to store email / password — I’m just setting the
givenName
andsurname
values to'John'
and'Smith'
.
NOTE: If the user account creation fails, we’ll render the registration page and pass in an appropriate error message from Stormpath. This will automatically handle problems like users attempting to register with an email that already exists.
If everything goes smoothly, we’ll use the passport.js library to log this new user in (creating a session, transparently), then redirect the user to the dashboard page.
The Login Route
1 2 3 4 5 6 7 8 9 10 11 12 |
// Render the login page. router.get('/login', function(req, res) { res.render('login', { title: 'Login', error: req.flash('error')[0] }); }); // Authenticate a user. router.post('/login', passport.authenticate('stormpath', { successRedirect: '/dashboard', failureRedirect: '/login', failureFlash: 'Invalid email or password.' })); |
The login routes (shown above) are responsible for logging existing users back into the site. The GET route only renders the login page, along with an optional error message to display if a user enters invalid credentials.
NOTE: The req.flash('error')[0]
you see is provided by the connect-flash library. If a login fails, the passport.js library will embed a tiny little ‘error’ message in a cookie, which is what the req.flash('error')[0]
call then retrieves.
The POST request is completely offloaded to the passport.js library. Passport provides the authenticate
method which will transparently check the username
and password
form fields against the Stormpath API, and take care of login automatically.
If the login is successful, Passport will redirect the user to the dashboard page — otherwise, it’ll re-render the login page once more, along with a friendly error message.
The Logout Route
1 2 3 4 5 6 |
// Logout the user, then redirect to the home page. router.get('/logout', function(req, res) { req.logout(); res.redirect('/'); }); |
The logout route (above) will log the user out of their account, destroying their session cookies.
The passport library automatically adds a logout
method onto the req
object, which makes it easy to log a user out of their account.
Once we’ve logged the user out, we simply redirect them back to the home page.
Writing the Templates
The last big thing we have to do is define our templates. The templates are the HTML layer that gets presented to users.
Let’s get started!
Create a Layout
The first thing we’ll want to do is create a layout.jade
template. By default, our new express app is using the jade templating language (check it out if you haven’t, it’s quite cool).
Here’s our views/layout.jade
template:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
doctype html html head meta(charset='utf-8') meta(content='IE=edge', http-equiv='X-UA-Compatible') meta(content='width=device-width, initial-scale=1', name='viewport') title= 'Express-Stormpath (sample) | ' + title link(href='/css/bootstrap.min.css', rel='stylesheet') link(href='/css/style.css', rel='stylesheet') <!--[if lt IE 9 ]> <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script> <script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script> <![endif]--> body .container .header ul.nav.nav-pills.pull-right li a(href='/') Home if user li a(href='/dashboard') Dashbaord li a(href='/logout') Logout else li a(href='/login') Login h3.text-muted Flask-Express block body script(src='https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js') script(src='/js/bootstrap.min.js') |
The main thing our fancy new layout template does is provide a basic HTML page layout that all our other pages will use. The line which reads block body
will be overridden by our other templates.
The most important bit to take note of here is the nav menu:
1 2 3 4 5 6 7 8 9 10 |
if user li a(href='/dashboard') Dashbaord li a(href='/logout') Logout else li a(href='/login') Login block body |
We’re using a simple conditional here to check to see whether or not a user is logged in or not (if user
), and changing the links the visitor sees accordingly. This ensures that if a user is logged in, and on the homepage — they won’t see a ‘Login’ button displayed in the navbar.
The Home Page Template
Place the following code into: views/index.jade
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
extends layout block body #welcome.jumbotron h1 Welcome to Stormpath-Express p.lead. <br/> <br/> Welcome to this gloriously simple <a href="https://github.com/stormpath/stormpath-express-sample">Stormpath</a> sample app! ul li First, take a look through this very basic site. li. Then, check out this project's source code <a href="https://github.com/stormpath/stormpath-express-sample">on GitHub</a>. li Lastly, integrate Stormpath into your own sites! <br/> <br/> h2 What this Sample App Demonstrates br p. This simple app demonstrates how easy it is to register, login, and securely authenticate users on your website using Stormpath. p. Aren't a Stormpath user yet? <a href="https://stormpath.com">Go signup now!</a> p. <b>NOTE</b>: This app will NOT work until you have gone through the bootstrapping instructions found in this project's <code>README.md</code> file. For more information, please follow the guide on this project's <a href="https://github.com/stormpath/stormpath-express-sample">GitHub page</a>. p.bigbutton a.bigbutton.btn.btn-lg.btn-danger(href='/register', role='button') Register |
This is our home page template. All it does is render some static information for users, so there isn’t much to go into here.
Since our layout.jade
template is already handling our user-smart nav bar, we don’t need to do anything special 🙂
The Registration Template
Place the following code into views/register.jade
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
extends layout block body .register .row .col-lg-6.col-lg-offset-3 form.bs-example.form-horizontal(method='post', action='') fieldset legend Create a New Account if error .alert.alert-dismissable.alert-danger.register-fail button.close(data-dismiss='alert', type='button') × p. #{error} p. Registering for this site will create a new user account for you via <a href="https://stormpath.com">Stormpath</a>, then log you in automatically. .alert.alert-dismissable.alert-info button.close(data-dismiss='alert', type='button') × strong NOTE: Your password must be between 8 and 100 characters, and must contain lowercase letters, uppercase letters, and numbers. .form-group label.col-lg-4.control-label(for='username') Email .col-lg-4 input#username.form-control(name='username', type='email', placeholder='Email', autofocus) .form-group label.col-lg-4.control-label(for='password') Password .col-lg-4 input#password.form-control(name='password', type='password', placeholder='Password') .form-group .col-lg-10.col-lg-offset-4 button.btn.btn-primary(type='submit') Register h2 A Note About Stormpath Security p. In a real environment, you should <b>only deploy your application</b> behind SSL / TLS to avoid having user credentials sent in plain text over the network. p. Stormpath user creation is incredibly secure, is not susceptible to any known vulnerabilities (MITM, hashing issues, replace attacks, etc.), and provides you with a number of options to improve security (including buil-in email verification, among other things). We will not verify your account by email now, but this can be easily enabled. p.last-p. Stormpath can also be configured to allow for weaker (or force stronger) passwords, so it can fit into any application workflow. |
This page handles user registration.
Take a close look at the form — we’re specifying two fields for the user to input: email and password. Take a look at the email input box, however:
1 2 |
input#username.form-control(name='username', type='email', placeholder='Email', autofocus) |
Although we’re collecting a user’s email address here, we’re setting the HTML name
attribute to the value username
. This is to make passport happy.
Passport expects username
and password
form elements, so that’s what we’ll use (despite the fact that we’re signing a user with just email / password).
We’ll also render an error message to the user (if there is one). This will be called if the registration fails for some reason, displaying a user-friendly error message.
The Login Template
Start by putting the following code into views/login.jade
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
extends layout block body .login .row .col-lg-6.col-lg-offset-3 form.bs-example.form-horizontal(method='post', action='') fieldset legend Login if error .alert.alert-dismissable.alert-danger.login-fail button.close(type='button', data-dismiss='alert') × p. #{error} p.last-p. Once you enter your credentials and hit the Login button below, your credentials will be securely sent to <a href="https://stormpath.com">Stormpath</a> and verified. If your credentials are valid, you'll be logged in using a secure session -- otherwise, you'll get a user friendly error message. .form-group label.col-lg-4.control-label(for='username') Email .col-lg-4 input#username.form-control(type='email', name='username', placeholder='Email', autofocus) .form-group label.col-lg-4.control-label(for='password') Password .col-lg-4 input#password.form-control(type='password', name='password', placeholder='Password') .form-group .col-lg-10.col-lg-offset-4 button.btn.btn-primary(type='submit') Login |
The login template works almost exactly like the registration template we talked about previously. Although we ask users to log in with an email and password, we secretly name the email input field username
to make passport happy.
Other than that, things work as you would expect if there is an error, we’ll display a user-friendly error message.
The Dashboard Template
Put the following code into views/dashboard.jade
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
extends layout block body .dashboard .row .col-lg-12 .jumbotron h1 Dashboard br br p Welcome to your user dashboard! p. This page displays some of your user information using Jade templates. p. If you click the Logout link in the navbar at the top of this page, you'll be logged out of your account and redirected back to the main page of this site. p. Your user account information is being securely stored using passport sessions, and Stormpath as the authentication backend. br br |
This is probably the simplest of all the templates — it just renders some static content.
This page is only accessible to logged-in users.
Static Assets
The last thing we need to do before we can run our awesome new project is drop in our static assets. This mainly involves copying over Twitter Bootstrap (I’m not a good designer, OK!).
You can run the following commands to download the necessary files locally (into your public
directory):
1 2 3 4 5 |
$ mkdir -p public/{css,js} $ wget https://raw.githubusercontent.com/stormpath/stormpath-express-sample/master/public/css/bootstrap.min.css --directory-prefix public/css $ wget https://raw.githubusercontent.com/stormpath/stormpath-express-sample/master/public/css/style.css --directory-prefix public/css $ wget https://raw.githubusercontent.com/stormpath/stormpath-express-sample/master/public/js/bootstrap.min.js --directory-prefix public/js |
Let’s Run This Thing
Now that we’ve gone through the entire project from start to finish let’s run this thing!
From the command line, you should be able to run npm start
at the root of your project, to launch a local web server on port 3000:
1 2 3 4 5 6 |
$ npm start > stormpath-express-sample@0.0.1 start > /Users/rdegges/stormpath-express-sample > node ./bin/www |
Now, open your browser and visit http://localhost:3000. Assuming everything is working well, you should be able to register a new account on the site, visit the dashboard, logout, and log back in again — with no problems.
Here’s how things should look:
Lastly — all this code is available on Github here: https://github.com/stormpath/stormpath-passport-express-sample
Want More Automation?
Passport.js is purely for authentication (i.e. checking that someone’s username and password is valid, or getting their identity from a social provider). It is not designed for handling user registrations, manage password resets, or implement email verification workflows, etc. In turn, our Passport strategy only automates authentication. If you need to do more, like password reset, you’ll need to also use the Stormpath Node SDK.
For a more seamless and automated experience, check out our express-stormpath library instead of Passport.js. Express.js provides user registration, along with a full suite of user management features for your Express-based web application. This feature includes:
- Create, register and authenticate users.
- Store custom user data with each account.
- Create and assign permissions (groups, roles, etc.).
- Handle complex authentication and authorization patterns, like multi-tenancy.
- Log users in via social login with Facebook and Google OAuth.
- Cache user information for quick access.
- Secure all your passwords.
- Automate all your password reset and account verification workflows.
Final Thoughts
Firstly, if you made it to the bottom of this post: thanks! Hopefully, you enjoyed building this node app along with me!
With that said, in the future, we’re planning on expanding our Stormpath support for both node.js and passport.js to make integration even simpler.
Among other things, we’d like to:
- Make registering users a one liner (instead of requiring custom development work).
- Make asserting user permissions simple and effortless (making it easy to handle user groups and permissions at a fine-grained level).
- Make our passport.js middleware more magical by having it automatically patch user data into templates for rendering.
- Make our passport.js strategy support caching clusters (or either Redis or Memcached) — this way you’ll be able to speed up user sessions quite a bit by avoiding round-trips to the Stormpath REST API.
Got anything else you think would be cool to see added? Please drop us a line! We’d love to hear from you 🙂
Best,
-Randall