Complete, compliant and well tested module for implementing an OAuth2 Server/Provider with express in node.js
npm install oauth2-server
The module provides two middlewares, one for authorization and routing, another for error handling, use them as you would any other middleware:
varexpress=require('express'),bodyParser=require('body-parser'),oauthserver=require('oauth2-server');varapp=express();app.use(bodyParser.urlencoded({extended: true}));app.use(bodyParser.json());app.oauth=oauthserver({model: {},// See below for specificationgrants: ['password'],debug: true});app.all('/oauth/token',app.oauth.grant());app.get('/',app.oauth.authorise(),function(req,res){res.send('Secret area');});app.use(app.oauth.errorHandler());app.listen(3000);After running with node, visting http://127.0.0.1:3000 should present you with a json response saying your access token could not be found.
Note: As no model was actually implemented here, delving any deeper, i.e. passing an access token, will just cause a server error. See below for the specification of what's required from the model.
- Supports authorization_code, password, refresh_token, client_credentials and extension (custom) grant types
- Implicitly supports any form of storage e.g. PostgreSQL, MySQL, Mongo, Redis...
- Full test suite
- stringmodel
- Model object (see below)
- arraygrants
- grant types you wish to support, currently the module supports
passwordandrefresh_token - Default:
[] - function|booleandebug
- If
trueerrors will be logged to console. You may also pass a custom function, in which case that function will be called with the error as its first argument - Default:
false - numberaccessTokenLifetime
- Life of access tokens in seconds
- If
null, tokens will considered to never expire - Default:
3600 - numberrefreshTokenLifetime
- Life of refresh tokens in seconds
- If
null, tokens will considered to never expire - Default:
1209600 - numberauthCodeLifetime
- Life of auth codes in seconds
- Default:
30 - regexpclientIdRegex
- Regex to sanity check client id against before checking model. Note: the default just matches common
client_idstructures, change as needed - Default:
/^[a-z0-9-_]{3,40}$/i - booleanpassthroughErrors
- If true, non grant errors will not be handled internally (so you can ensure a consistent format with the rest of your api)
- booleancontinueAfterResponse
- If true,
nextwill be called even if a response has been sent (you probably don't want this)
The module requires a model object through which some aspects or storage, retrieval and custom validation are abstracted. The last parameter of all methods is a callback of which the first parameter is always used to indicate an error.
Note: see https://github.com/thomseddon/node-oauth2-server/tree/master/examples/postgresql for a full model example using postgres.
- stringbearerToken
- The bearer token (access token) that has been provided
- functioncallback (error, accessToken)
- mixederror
- Truthy to indicate an error
- objectaccessToken
- The access token retrieved form storage or falsey to indicate invalid access token
- Must contain the following keys:
- dateexpires
- The date when it expires
nullto indicate the token never expires
- mixeduserorstring|numberuserId
- If a
userkey exists, this is saved asreq.user - Otherwise a
userIdkey must exist, which is saved inreq.user.id
- If a
- dateexpires
- stringclientId
- string|nullclientSecret
- If null, omit from search query (only search by clientId)
- functioncallback (error, client)
- mixederror
- Truthy to indicate an error
- objectclient
- The client retrieved from storage or falsey to indicate an invalid client
- Saved in
req.client - Must contain the following keys:
- stringclientId
- stringredirectUri (
authorization_codegrant type only)
- stringclientId
- stringgrantType
- functioncallback (error, allowed)
- mixederror
- Truthy to indicate an error
- booleanallowed
- Indicates whether the grantType is allowed for this clientId
- stringaccessToken
- stringclientId
- dateexpires
- objectuser
- stringscope
- functioncallback (error)
- mixederror
- Truthy to indicate an error
- stringaccessToken
- mixedscope
- functioncallback (error, invalid)
- mixederror
- Truthy to indicate an error
- boolean|stringinvalid
- Falsey to indicate token possesses required scope; truthy (boolean or string) as invalid scope error message
- stringscope
- objectclient
- objectuser
- functioncallback (error, validScope, invalid)
- mixederror
- Truthy to indicate an error
- mixedvalidScope
- Validated/sanitized scope string
- boolean|stringinvalid
- Falsey to indicate solicited scope was granted; truthy (boolean or string) as invalid scope error message
- stringauthCode
- functioncallback (error, authCode)
- mixederror
- Truthy to indicate an error
- objectauthCode
- The authorization code retrieved form storage or falsey to indicate invalid code
- Must contain the following keys:
- string|numberclientId
- client id associated with this auth code
- dateexpires
- The date when it expires
- string|numberuserId
- The userId
- string|numberclientId
- stringauthCode
- stringclientId
- dateexpires
- mixeduser
- Whatever was passed as
userto the codeGrant function (see example)
- Whatever was passed as
- stringscope
- functioncallback (error)
- mixederror
- Truthy to indicate an error
- stringusername
- stringpassword
- functioncallback (error, user)
- mixederror
- Truthy to indicate an error
- objectuser
- The user retrieved from storage or falsey to indicate an invalid user
- Saved in
req.user - Must contain the following keys:
- string|numberid
- stringrefreshToken
- stringclientId
- dateexpires
- objectuser
- stringscope
- functioncallback (error)
- mixederror
- Truthy to indicate an error
- stringrefreshToken
- The bearer token (refresh token) that has been provided
- functioncallback (error, refreshToken)
- mixederror
- Truthy to indicate an error
- objectrefreshToken
- The refresh token retrieved form storage or falsey to indicate invalid refresh token
- Must contain the following keys:
- string|numberclientId
- client id associated with this token
- dateexpires
- The date when it expires
nullto indicate the token never expires
- string|numberuserId
- The userId
- string|numberclientId
The spec does not actually require that you revoke the old token - hence this is optional (Last paragraph: http://tools.ietf.org/html/rfc6749#section-6)
- stringrefreshToken
- functioncallback (error)
- mixederror
- Truthy to indicate an error
Required for extension grant grant type
- stringgrantType
- The (custom) grant type
- objectreq
- The raw request
- functioncallback (error, supported, user)
- mixederror
- Truthy to indicate an error
- booleansupported
- Whether you support the grant type
- objectuser
- The user retrieved from storage or falsey to indicate an invalid user
- Saved in
req.user - Must contain the following keys:
- string|numberid
- stringclientId
- stringclientSecret
- functioncallback (error, user)
- mixederror
- Truthy to indicate an error
- objectuser
- The user retrieved from storage or falsey to indicate an invalid user
- Saved in
req.user - Must contain the following keys:
- string|numberid
- stringtype
accessTokenorrefreshToken- objectreq
- The current express request
- functioncallback (error, token)
- mixederror
- Truthy to indicate an error
- string|object|nulltoken
- string indicates success
- null indicates to revert to the default token generator
- object indicates a reissue (i.e. will not be passed to saveAccessToken/saveRefreshToken)
- Must contain the following keys (if object):
- stringaccessToken OR refreshToken dependant on type
- Must contain the following keys (if object):
You can support extension/custom grants by implementing the extendedGrant method as outlined above.
Any grant type that is a valid URI will be passed to it for you to handle (as defined in the spec).
You can access the grant type via the first argument and you should pass back supported as false if you do not support it to ensure a consistent (and compliant) response.
First you must insert client id/secret and user into storage. This is out of the scope of this example.
To obtain a token you should POST to /oauth/token. You should include your client credentials in
the Authorization header ("Basic " + client_id:client_secret base64'd), and then grant_type ("password"),
username, password, and optionally a scope in the request body, for example:
POST /oauth/token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=password&username=johndoe&password=A3ddj3w&scope=readonly
This will then call the following on your model (in this order):
- getClient (clientId, clientSecret, callback)
- grantTypeAllowed (clientId, grantType, callback)
- getUser (username, password, callback)
- saveAccessToken (accessToken, clientId, expires, user, scope, callback)
- saveRefreshToken (refreshToken, clientId, expires, user, callback) (if using)
Provided there weren't any errors, this will return the following (excluding the refresh_token if you've not enabled the refresh_token grant type):
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"bearer",
"expires_in":3600,
"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
"scope": "readonly"
}
See: https://github.com/thomseddon/node-oauth2-server/blob/master/Changelog.md
Copyright (c) 2013 Thom Seddon
