Skip to content

bkyarger/stormpath-sdk-express

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

58 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Stormpath Express.js SDK [BETA]

NPM Version NPM Downloads Build Status

This module is BETA

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.

Example Integration

You can see an example integration in the Dashboard App Example application, which is part of our Stormpath AngularJS SDK

Table of Contents

Usage

Install via NPM

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

Use with all routes

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, via app.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 or client_credentials) it delegates to authenticateUsernamePasswordForToken or authenticateApiKeyForToken.

  • 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.

Use with some routes

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
});

More complex usage

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 different spConfig options
  • Using the Router object in Express 4.x to organize your routes

spConfig options

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.

Access Token TTL (seconds)

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

Access Token Cookie

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

Access Token Response

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

Allowed Origins

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)

Error Handlers

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
    });
  }
});

HTTPS

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

Post Registration Handler

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");
  }
}

Scope Factory

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)

  }
}

spClient

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

Token Endpoint

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'
}

XSRF Protection

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
}

Custom Token Strategies

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.

Middleware API

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.

authenticate

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});
});

authenticateCookie

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
    });
});

authenticateBasicAuth

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
    });
});

authenticateBearerAuthorizationHeader

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
    });
});

authenticateApiKeyForToken

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);

authenticateUsernamePasswordForToken

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);

authenticateForToken

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.

groupsRequired

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.

writeToken

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.

logout

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);

Middleware Factory Methods

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
});

Types

These are the object types that you will find in this library.

Account

This object is provided by the underlying Stormpath Node SDK, it is documented here:

http://docs.stormpath.com/nodejs/api/account

AuthenticationRequest

This object is provided by the underlying Stormpath Node SDK. It is documented here:

http://docs.stormpath.com/nodejs/api/authenticationResult

Jwt

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.

TokenResponse

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"
}

Contributing

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

Copyright © 2015 Stormpath, Inc. and contributors.

This project is open-source via the Apache 2.0 License.

About

Stormpath SDK for Node.js Express applications.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • JavaScript 100.0%