Skip to content
This repository has been archived by the owner on Dec 13, 2018. It is now read-only.

Commit

Permalink
Expose configuration for local validation of access tokens
Browse files Browse the repository at this point in the history
  • Loading branch information
Robert committed Dec 4, 2015
1 parent 1b6a8d5 commit b218e1f
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 10 deletions.
83 changes: 73 additions & 10 deletions docs/authentication.rst
Original file line number Diff line number Diff line change
Expand Up @@ -37,19 +37,82 @@ To use cookie authentication, simply use the ``loginRequired`` middleware::
});

Behind the scenes we are issuing a OAuth2 Access Token and Refresh token for
the user, and storing them in secure, HTTPS-Only cookies. The maximum
lifetime of the cookies is controlled by the expiration time of the Refresh
Token.
the user, and storing them in secure, HTTPS-Only cookies. After the user has
logged in, these cookies will be supplied on every request. Our library will
assert that the access token is valid. If the access token is expired, we will
attempt to refresh it with the refresh token.

If you need to change the expiration time of the Refresh Token, please login
to the Stormpath Admin Console and navigate to the OAuth policy of your
Stormpath Application. Then change the expiration time of the Refresh Token.

.. note::
Express-Stormpath's session management will not interfere with any existing
session middleware you might have. The sessions that Stormpath uses are
exclusively used for Stormpath's purposes, so it's safe to create your own
separate sessions if needed.
Express-Stormpath's OAuth2 cookie feature will not interfere with any
existing cookie-based session middleware you might have. The cookies that
Stormpath creates are used exclusively for Stormpath's purposes, so it's
safe to create your own separate sessions if needed.


Setting Token Expiration Time
.............................

If you need to change the expiration time of the access token or refresh Token,
please login to the Stormpath Admin Console and navigate to the OAuth policy of
your Stormpath Application. There you will find the settings for each token.

Token Validation Strategy
.........................

When a request comes into your server, this library will use the access token
and refresh token cookies to make an authentication decision. The default
validation strategy works like this:

- Validate the signature and expiration time of the access token. If the access
token is expired, attempt to get a new one by using the refresh token.

- If the access token is expired and cannot be refreshed, deny the request

- If the access token is not expired and the signature is valid, the library
makes a request to the Stormpath API to assert that the access token has not
been revoked and that the associated account still exists and is not disabled.

In the last step, the API request will add a network request to the
authentication process. If this is not desirable (for performance reasons),
you can opt-in to `local` validation. In this situation, our library only
checks the signature of the token and does not make the extra request to the
Stormpath API to assert the token and the account.

You can opt-in to local validation with this configuration:

.. code-block:: javascript
{
web: {
oauth2: {
password: {
validationStrategy: 'local'
}
}
}
}
.. warning::

When using local validation, your server will not be aware of token revocation
or any changes to the associated Stormpath account. **This is a security
risk.**

There are two suggested strategies for dealing with this risk:

* Use a short expiration time for your access tokens (such as one hour or
less). This will limit the amount of time that the access token can be used
for validation. Our library *always* makes a request to the Stormpath API when
we attempt to refresh an access token, so the refresh attempt will fail
at this time if the refresh token has been revoked.

* Maintain a blacklist of revoked tokens, in your local application cache.
Implement a middleware function that asserts that the access token is not
in this cache, and reject the request if true. We may implement this as
a convenience feature in the future.




Issuing API Keys
Expand Down
1 change: 1 addition & 0 deletions lib/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ web:
ttl: 3600
password:
enabled: true
validationStrategy: stormpath
accessTokenCookie:
name: "access_token"
httpOnly: true
Expand Down
5 changes: 5 additions & 0 deletions lib/helpers/get-user.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ var expandAccount = require('./expand-account');
module.exports = function (req, res, next) {
var application = req.app.get('stormpathApplication');
var client = req.app.get('stormpathClient');
var config = req.app.get('stormpathConfig');
var logger = req.app.get('stormpathLogger');

// In the event this has already been run (this can happen due to Express
Expand Down Expand Up @@ -66,6 +67,10 @@ module.exports = function (req, res, next) {
} else if (req.cookies && req.cookies.access_token) {
var authenticator = new stormpath.JwtAuthenticator(application);

if (config.web.oauth2.password.validationStrategy === 'local') {
authenticator.withLocalValidation();
}

authenticator.authenticate(req.cookies.access_token, function (err, authenticationResult) {
if (err) {
logger.info('Failed to authenticate the request. Invalid access_token found.');
Expand Down
56 changes: 56 additions & 0 deletions test/helpers/test-get-user.js
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,62 @@ describe('getUser', function () {
});
});

it('should use local validation, if specified by configuration', function (done) {

var app = helpers.createStormpathExpressApp({
application: stormpathApplication,
website: true,
web: {
oauth2: {
password: {
validationStrategy: 'local'
}
}
}
});

app.get('/', getUser, function (req, res) {
res.json(req.user);
});

app.on('stormpath.ready', function () {

var agent = request.agent(app);

async.series([
function (callback) {
stormpathAccount.status = 'ENABLED';
stormpathAccount.save(callback);
},
function (callback) {
agent
.post('/login')
.send({
login: accountData.email,
password: accountData.password
})
.expect(302)
.end(callback);
},
function (callback) {
var a = new Date();
agent
.get('/')
.expect(200)
.end(function (err, res) {
if (err) {
return callback(err);
}
var b = new Date();
assert((b - a) < 20, 'Validation took too long - does not appear to be local validation');
assert.equal(res.body.email, accountData.email);
callback();
});
}
], done);
});
});

it('should set req.user and res.locals.user if an invalid access_token cookie is present with a valid refresh_token cookie', function (done) {
var app = createFakeExpressApp();
var agent = request.agent(app);
Expand Down

0 comments on commit b218e1f

Please sign in to comment.