The primary use-case of this module is to provide a lean set of Express.js middleware that supports our new Stormpath AngularJS SDK
This module does not provide any built-in views or Angular code, it is meant to be a back-end authentication and authorization API for a Single-Page Application. If you are starting a project from scratch and would like some guidance with creating an AngularJS application, please see our Stormpath AngularJS Guide
If you are looking for a comprehensive Express.js solution which includes a server-side page templating system, please visit our Stormpath-Express integration. Note: we do not yet have an integration strategy for using AngularJS with Stormpath-Express.
The following features are supported by this module:
- Register new accounts
- Login users via username & password, Oauth Bearer Token, or Basic Auth
- Email verification workflow
- Password Reset Workflow
These features are NOT yet supported (but coming soon!):
- Social login
- ID Site Integration
If you have questions or feedback about this library, please get in touch and share your thoughts! support@stormpath.com
Stormpath is a User Management API that reduces development time with instant-on, scalable user infrastructure. Stormpath's intuitive API and expert support make it easy for developers to authenticate, manage, and secure users and roles in any application.
You can see an example integration in the Dashboard App Example application, which is part of our Stormpath AngularJS SDK
npm install --save stormpath-sdk-express
The features in this library depend on cookies and POST body
data. For that reason, you should use cookie-parser or body-parser or
any other library that sets req.cookies
and req.body
. If you need
to install them:
npm install --save cookie-parser body-parser
To use the library with default options, you will do the follwing:
- Require the module
- Create an instance of
spMiddlware
- Attach the default routes
- Use the
authenticate
middleware on all your routes, viaapp.use()
Basic Usage Example:
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var express = require('express');
var stormpathExpressSdk = require('stormpath-sdk-express');
var spConfig = {
appHref: 'YOUR_STORMPATH_APP_HREF',
apiKeyId: 'YOUR_STORMPATH_API_KEY_ID',
apiKeySecret: 'YOUR_STORMPATH_API_KEY_SECRET'
}
var app = express();
app.use(cookieParser());
app.use(bodyParser.json());
var spMiddleware = stormpathExpressSdk.createMiddleware(spConfig);
spMiddleware.attachDefaults(app);
app.use(spMiddleware.authenticate);
Doing this will enable the following functionality:
-
All endpoints in your Express application will first be routed through the
authenticate
middleware, which will assert that the user has an existing access token. If this is not true, an error response will be sent. -
The endpoint
/oauth/token
will accept Client Password or Client Credential grant requests and respond with an access token if authentication is successful. Depending on the grant type (password
orclient_credentials
) it delegates toauthenticateUsernamePasswordForToken
orauthenticateApiKeyForToken
. -
The
/logout
endpoint will be provided for ending cookie-based sessions. -
The XSRF Protection feature will be enabled. After authentication, all POST requests will validated with an XSRF token as well.
If you don't need to authenticate all routes, but still want to use token
authentication, then don't use the statement app.use(spMiddleware.authenticate)
.
Instead, use the authenticate
middleware on the
routes that need authentication:
Specific Route Example:
// Enforce authentication on the API
app.get('/api/*',spMiddleware.authenticate,function(req,res,next){
// If we get here, the user has been authenticated
// The account object is available at req.user
});
If you have a more complex use case, we suggest:
- Using the
spConfig
options to control features - Custom chaining of specific middleware from the Middleware API
- Create custom middleware using the Middleware Factory Methods
- Creating multiple instances of
spMiddleware
with differentspConfig
options - Using the Router object in Express 4.x to organize your routes
The default behavior of the middleware can be modified via the spCpnfig
object
that you pass to createMiddleware()
. This section describes all the
available options and which middleware functions they affect.
The default duration of access tokens is one hour. Use accessTokenTtl
to
set a different value in seconds. Once the token expires, the client
will need to obtain a new token.
var spConfig = {
accessTokenTtl: 60 * 60 * 24 // One day (24 hours)
}
Used by authenticateUsernamePasswordForToken
, authenticateApiKeyForToken
The default response to a successful password grant request is to create an
access token and write it to a secure cookie. The name of the cookie will be
access_token
and the value will be a JWT token.
Set { writeAccessTokenToCookie: false }
if you do not want to write the access
token to a cookie.
Use accessTokenCookieName
to change the default name of the cookie.
Example: disable access token cookies
var spConfig = {
writeAccessTokenToCookie: false
}
Example: custom access token cookie name
var spConfig = {
accessTokenCookieName: 'custom_cookie_name'
}
Used by authenticateUsernamePasswordForToken
, authenticateCookie
By default, this library will not send the access token in the
body of the response if the grant type is password
. Instead it will
write the access token to an HTTPS-only cookie and send 201 for the response status.
This is for security purposes. By exposing it in the body, you expose it to the javascript environment on the client, which is vulnerable to XSS attacks.
You can disable this security feature by enabling the access token response writer:
var spConfig = {
writeAccessTokenResponse: true
}
When enabled, the response body will be a TokenResponse
Used by authenticateUsernamePasswordForToken
This is a whitelist of domains that are allowed to make requests of your API. If you are making cross-origin requests (CORS) to your server, you will need to whitelist the origin domains with this option. This library will automatically respond with the relevant response headers that are required by CORS.
var spConfig = {
allowedOrigins: ['http://example.com']
}
Used by autoRouteHandler
via app.use(stormpathMiddleware)
By default, the library will respond to failed authentication by ending the response and sending an appropriate error code and message.
Set endOnError
to false to disable this default behaviour.
In this case, the library will assign the error to req.authenticationError
and continue the middleware chain.
var spConfig = {
endOnError: false
}
Used by handleAuthenticationError
Example: custom error handling
app.get('/secrets',spMiddleware.authenticate,function(req,res,next){
if(req.authenticationError){
res.json(401,{
error: req.authenticationError
});
}else{
res.json(200,{
message: 'Hello, ' + req.user.fullName
});
}
});
This library auto-negotiates HTTP vs HTTPS. If your server is accepting
HTTPS requests, it will automatically add the Secure
option to the
access token cookie. This relies on req.protocol
from the Express
framework.
If your server is behind a load balancer, you should use the "trust proxy"
option for Express.
If you cannot trust that your forward proxy is doing the right thing, then you should force this library to always use HTTPS.
Example: force HTTPS always
var spConfig = {
forceHttps: true
}
Used by writeToken
Use this handler to receive account objects that have been created. This handler
is called after a POST to /register
has resulted in a new account. You must
end the response manually, or call next()
to allow the default
responder to finish the response.
The newly created user is available at req.user
.
Example: Add some custom data after registration
var spConfig = {
postRegistrationHandler: function(req,res,next){
req.user.getCustomData(function(err,customData){
if(err){
res.end(err)
}else{
customData.myCustomValue = 'foo';
customData.save(function(err){
if(err){
res.end(err)
}else{
next();
}
})
}
});
}
}
Example: Send a custom registration response
var spConfig = {
postRegistrationHandler: function(req,res,next){
res.end("Thank you for registering");
}
}
By default, the library will not add scope to access tokens, we leave this in your control. Implement a scope factory if you wish to respond to a requested scope by granting specific scopes. It will be called before the access token is written. You MUST call the done callback with the granted scope. If your logic determines that no scope should be given, simply call back with a falsey value.
If you need more control over the token creation, see Custom Token Strategies
var spConfig = {
// ...
scopeFactory: function(req,res,authenticationResult,account,requestedScope,done){
// requestedScope is a string passed in from the request
var grantedScope = '';
// do your logic, then callback with an error or the granted scope
done(null,grantedScope)
}
}
This is a reference to the Stormpath Client that was created by this module,
based on the spConfig
that was passed.
This is the client that is exported by the Stormpath Node SDK, you can use it to achieve all the Stormpath API functionality that is supported by that SDK. Full documentation here:
http://docs.stormpath.com/nodejs/api/home
Defines the endpoint that is auto-bound to authenticateForToken
.
The binding only happens if you call app.use(spMiddleware)
.
var spConfig = {
tokenEndpoint: '/my/alt/token/endpoint'
}
This library combats Cross-Site Request Forgery by issuing XSRF tokens.
They are issued at the time an access token is created. The value of the XSRF
token is written to the XSRF-TOKEN
cookie and stored as the xsrfToken
claim
within the access token. Doing this allows the library to do a stateless
check when validating XSRF tokens.
Your client application, for any POST request, should supply the XSRF token
via the X-XSRF-TOKEN: <token>
HTTP Header. If you are using Angular.js, this
will happen for you automatically.
If you do not want to issue or validate XSRF tokens, disable the feature:
var spConfig = {
xsrf: false
}
This library will issue JWT access tokens with a configurable TTL and scope.
All other values (sub
, iat
, etc.) are set automatically and
used for verifying the integrity of the token during authentication.
If you want to implement your own token responder (not recommended!), you can
do so by setting the { writeTokens: false }
option in spConfig
Doing this will prevent the library from automatically generating tokens and sending
them in responses. Instead, it will provide an AuthenticationRequest
object at req.authenticationRequest
and call next()
. It is
up to you to generate a token and end the response.
NOTE: Disabling token creation will also disable the creation and validation of XSRF tokens.
This section is a list of of middleware functions that are available
on the object that is returned by createMiddleware(spConfig)
When you call createMiddleware(spConfig)
, you are simply creating a set
of middleware functions which are pre-bound to the Stormpath Application that
you define with the spConfig
options. You can then mix-and-match these
middleware functions to create authentication logic that suits your
application.
WARNING: We have put a lot of thought into the default decisions of this library. While we provide this API for developer use, it is required that you understand the security trade-offs that you may be making by customizing this library. If you need any assistance, please contact support@stormpath.com.
This is a convenience middleware that will inspect the request and call
authenticateCookie
or
authenticateBearerAuthorizationHeader
or
authenticateBasicAuth
for you
Example usage: authenticate specific routes
var app = express();
var spMiddleware = stormpathExpressSdk.createMiddleware({/* options */})
app.get('/something',spMiddleware.authenticate,function(req,res,next){
res.json({message: 'Hello, ' + req.user.fullName});
});
Looks for an access token in the request cookies.
If authenticated, assigns an Account
to req.user
and provides
the unpacked access token at req.accessToken
(an instance of Jwt
)
If an error is encountered, it ends the response with an error. If
using the option { endOnError: false}
, it sets
req.authenticationError
and continues the chain.
Example: use cookie authentication for a specific endpoint
var spMiddleware = stormpathExpressSdk.createMiddleware({/* options */})
var app = express();
app.get('/something',spMiddleware.authenticateCookie,function(req,res,next){
res.json({
message: 'Hello, ' + req.user.fullName + ', ' +
'you have a valid access token in a cookie. ' +
'Your token expires in: ' + req.accessToken.body.exp
});
});
Looks for an access token in the Authorization: Basic <Base64(apiKeyId:apiKeySecret)>
header
on the request
If authenticated, assigns an Account
to req.user
and provides
the unpacked access token at req.accessToken
(an instance of Jwt
)
If an error is encountered, it ends the response with an error. If
using the option { endOnError: false}
, it sets
req.authenticationError
and continues the chain.
Example: use Basic Authentication for a specific endpoint
var spMiddleware = stormpathExpressSdk.createMiddleware({/* options */})
var app = express();
app.use(spMiddleware)
app.get('/something',spMiddleware.authenticateBasicAuth,function(req,res,next){
res.json({
message: 'Hello, ' + req.user.fullName + ', ' +
'you have a valid API Key in your Basic Authorization header. ' +
'Your token expires in: ' + req.accessToken.body.exp
});
});
Looks for an access token in the Authorization: Bearer <Base64(accessToken)>
header
on the request
If authenticated, assigns an Account
to req.user
and provides
the unpacked access token at req.accessToken
(an instance of Jwt
)
If an error is encountered, it ends the response with an error. If
using the option { endOnError: false}
, it sets
req.authenticationError
and continues the chain.
Example: use bearer authentication for a specific endpoint
var spMiddleware = stormpathExpressSdk.createMiddleware({/* options */})
var app = express();
app.use(spMiddleware)
app.get('/something',spMiddleware.authenticateBearerAuthorizationHeader,function(req,res,next){
res.json({
message: 'Hello, ' + req.user.fullName + ', ' +
'you have a valid access token in your Authorization header. ' +
'Your token expires in: ' + req.accessToken.body.exp
});
});
This is used with the Stormpath API Key Authentication Feature. It expects an account's API Key and Secret to be supplied in HTTP headers via the HTTP Basic scheme.
Example: posting api key ID and secret to the token endpoint
POST /oauth/tokens?grant_type=client_credentials
Authorization: Basic <Base64(apiKeyId:apiKeySecret)>
If the supplied credentials are a valid API Key for an account, this function will respond
by writing a TokenResponse
and ending the request.
Example: accept only api keys for token exchange
var spMiddleware = stormpathExpressSdk.createMiddleware({/* options */})
var app = express();
app.post('/oauth/token',spMiddleware.authenticateApiKeyForToken);
Expects a JSON POST body, which has a username
and password
field, and a
grant type request of password
.
Example: posting username and password to the token endpoint
POST /oauth/tokens?grant_type=password
{
"username": "foo@bar.com",
"password": "aSuperSecurePassword"
}
If the supplied password is valid for the username, this function will respond
with an access token cookie or access token response, depending on the configuration
set by writeAccessTokenResponse
and
writeAccessTokenToCookie
Example: accept only username & password for token exchange
var spMiddleware = stormpathExpressSdk.createMiddleware({/* options */})
var app = express();
app.post('/login',spMiddleware.authenticateUsernamePasswordForToken);
This is a convenience middleware that will inspect the request
and delegate to authenticateApiKeyForToken
or
authenticateUsernamePasswordForToken
Example: manually defining the token endpoint
var spMiddleware = stormpathExpressSdk.createMiddleware({/* options */})
var app = express();
app.post('/tokens-r-us',spMiddleware.authenticateForToken);
Note: this example can also be accomplished with the tokenEndpoint
option inspConfig
.
This middleware allows you to perform group-based authorization. It takes a string or array of strings. If an array, the user must exist in all said groups.
Example: manually defining the token endpoint
var spMiddleware = stormpathExpressSdk.createMiddleware({/* options */})
var app = express();
app.get('/secrets',spMiddleware.groupsRequired('admin'));
app.get('/not-so-secrets',spMiddleware.groupsRequired(['admin','editor']));
Note: this example can also be accomplished with the tokenEndpoint
option inspConfig
.
This method will write an access token cookie or access token body response,
as configured by the writeAccessTokenResponse
and
writeAccessTokenToCookie
options.
It expects that req.jwt
has already been populated by one of the authentication
functions.
Example: manually defining the token endpoint
var spMiddleware = stormpathExpressSdk.createMiddleware({/* options */})
var app = express();
app.post('/tokens-r-us',spMiddleware.writeToken);
Note: example can also be accomplished with the tokenEndpoint
option in spConfig
.
This method will delete the XSRF and access token cookies on the client.
Example: manually defining a logout endpoint
var spMiddleware = stormpathExpressSdk.createMiddleware({/* options */})
var app = express();
app.get('/logout',spMiddleware.logout);
If you only need certain features or middleware functions, you can construct
the middleware functions manually. All middleware functions can be constructed
by calling their constructor directly off the library export. Simply
use a capital letter to access the constructor. When calling the constructor,
you must provide an spConfig
object with relevant options.
WARNING: We have put a lot of thought into the default decisions of this library. While we provide this API for developer use, it is required that you understand the security trade-offs that you may be making but customizing this library. If you need any assistance, please contact support@stormpath.com
For example: say you want one web service to issue access tokens for API Authentication. You then want all your other web services to read these tokens and use them for authentication.
Example: a token generation webservice
var express = require('express');
var stormpathSdkExpress = require('stormpath-sdk-express');
var spConfig = {
appHref: '',
apiKeyId: '',
apiKeySecret: '',
}
var tokenExchanger = stormpathSdkExpress.AuthenticateApiKeyForToken(spConfig);
var app = express();
app.post('/oauth/tokens',tokenExchanger);
Example: authenticate tokens in your other web services
var express = require('express');
var stormpathSdkExpress = require('stormpath-sdk-express');
var spConfig = {
appHref: '',
apiKeyId: '',
apiKeySecret: '',
}
var authenticate = stormpathSdkExpress.Authenticate(spConfig);
var app = express();
app.use('/api/*',authenticate,function(req,res,next){
// handle api request. Account will be available at req.user
});
These are the object types that you will find in this library.
This object is provided by the underlying Stormpath Node SDK, it is documented here:
http://docs.stormpath.com/nodejs/api/account
This object is provided by the underlying Stormpath Node SDK. It is documented here:
http://docs.stormpath.com/nodejs/api/authenticationResult
These are objects that represent a JWT token. They have methods for manipulating the token and compacting it to an encoded string. These instances are provided by the nJwt Library.
This object encapsulates the compacted JWT, exposes the scope of the token, and declares the expiration time as seconds from now.
Example: token response format
{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGc ...",
"expires_in": 3600,
"token_type": "Bearer",
"scope": "given-scope"
}
In lieu of a formal style guide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality. Lint and test your code using Grunt.
You can make your own contributions by forking the develop branch of this repository, making your changes, and issuing pull request on the develop branch.
We regularly maintain this repository, and are quick to review pull requests and accept changes!
We <333 contributions!
Copyright © 2015 Stormpath, Inc. and contributors.
This project is open-source via the Apache 2.0 License.