/https://stormpath.com/blog/finally-manage-users-easily-in-node-dot-js/”>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 tutorial below and jump straight into the code, I’ve put this app on Github for your viewing pleasure: https://github.com/stormpath/stormpath-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
To build this simple app, 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. We’ll be using this 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:
$ 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:
$ 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/javascripts
create : stormpath-express-sample/public/images
create : stormpath-express-sample/public/stylesheets
create : stormpath-express-sample/public/stylesheets/style.styl
create : stormpath-express-sample/routes
create : stormpath-express-sample/routes/index.js
create : stormpath-express-sample/routes/users.js
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/bin
create : stormpath-express-sample/bin/www
install dependencies:
$ cd stormpath-express-sample && npm install
run the app:
$ DEBUG=my-application ./bin/www
Then, of course, you’ll want to go into your project directory and install all
of the basic dependencies:
$ cd stormpath-express-sample
$ npm install
And that’s it for the basic configuration!
Configuration
Now that we have our skeleton application ready (thanks, express!), let’s change our project name in the package.json
file.
Open up package.json
and change the name
field so it says "stormpath-express-sample"
instead of "application-name"
. This will make any errors we have easier to debug later on.
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:
$ npm install passport passport-stormpath express-session connect-flash stormpath --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:
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:
$ 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:
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:
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
:
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:
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:
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.
var app = 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});
} else {
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:
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:
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:
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:
// 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 actually work!
The Home Page Route
Let’s start by looking at the home page route:
// 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:
// 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.
// 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.
var app = 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});
} else {
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
and surname
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
// 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
// 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:
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:
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
:
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
:
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:
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
:
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
:
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):
$ 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:
$ 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-express-sample
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