Skip to content
This repository has been archived by the owner on Aug 28, 2023. It is now read-only.

Commit

Permalink
Merge pull request #270 from AzureAD/sijliu/clockSkew
Browse files Browse the repository at this point in the history
Issue #245 Make clock skew configurable
  • Loading branch information
lovemaths authored Dec 12, 2016
2 parents 9591de9 + 57241e7 commit 32b7b3c
Show file tree
Hide file tree
Showing 10 changed files with 161 additions and 3 deletions.
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ passport.use(new OIDCStrategy({
scope: config.creds.scope,
loggingLevel: config.creds.loggingLevel,
nonceLifetime: config.creds.nonceLifetime,
clockSkew: config.creds.clockSkew,
},
function(iss, sub, profile, accessToken, refreshToken, done) {
if (!profile.oid) {
Expand Down Expand Up @@ -165,6 +166,10 @@ passport.use(new OIDCStrategy({
* `nonceLifetime` (Optional)

The lifetime of nonce in session in seconds. The default value is 3600 seconds.

* `clockSkew` (Optional)

This value is the clock skew (in seconds) allowed in token validation. It must be a positive integer. The default value is 300 seconds.

##### 5.1.1.3 Verify callback

Expand Down Expand Up @@ -267,6 +272,7 @@ var options = {
allowMultiAudiencesInToken: config.creds.allowMultiAudiencesInToken,
audience: config.creds.audience,
loggingLevel: config.creds.loggingLevel,
clockSkew: config.creds.clockSkew,
};

var bearerStrategy = new BearerStrategy(options,
Expand Down Expand Up @@ -353,6 +359,10 @@ var bearerStrategy = new BearerStrategy(options,

Logging level. 'info', 'warn' or 'error'.

* `clockSkew` (Optional)

This value is the clock skew (in seconds) allowed in token validation. It must be a positive integer. The default value is 300 seconds.

##### 5.2.1.3 Verify callback

If you set `passReqToCallback` option to false, you can use the following verify callback
Expand Down
14 changes: 14 additions & 0 deletions lib/bearerstrategy.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const passport = require('passport');
const util = require('util');

const aadutils = require('./aadutils');
const CONSTANTS = require('./constants');
const jwt = require('./jsonWebToken');
const Metadata = require('./metadata').Metadata;
const Log = require('./logging').getLogger;
Expand Down Expand Up @@ -132,6 +133,10 @@ const B2C_PREFIX = 'b2c_1_';
* (3) Description:
* We invalidate the `aud` claim in access_token against `audience`. The default value is `clientID`
*
* - `clockSkew` (1) Optional
* (2) must be a positive integer
* (3) Description:
* the clock skew (in seconds) allowed in token validation, default value is CLOCK_SKEW
* Examples:
*
* passport.use(new BearerStrategy(
Expand Down Expand Up @@ -181,6 +186,12 @@ function Strategy(options, verifyFn) {
// Set up the default values
//---------------------------------------------------------------------------

// clock skew. Must be a postive integer
if (options.clockSkew && (typeof options.clockSkew !== 'number' || options.clockSkew <= 0 || options.clockSkew % 1 !== 0))
throw new Error('clockSkew must be a positive integer');
if (!options.clockSkew)
options.clockSkew = CONSTANTS.CLOCK_SKEW;

// default value of passReqToCallback is false
if (options.passReqToCallback !== true)
options.passReqToCallback = false;
Expand Down Expand Up @@ -384,6 +395,9 @@ Strategy.prototype.authenticate = function authenticateStrategy(req) {
optionsToValidate.allowMultiAudiencesInToken = self._options.allowMultiAudiencesInToken;
optionsToValidate.ignoreExpiration = self._options.ignoreExpiration;

// clock skew
optionsToValidate.clockSkew = self._options.clockSkew;

log.info(`In Strategy.prototype.authenticate: we will validate the following options: ${optionsToValidate}`);

return next();
Expand Down
5 changes: 2 additions & 3 deletions lib/jsonWebToken.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
'use restrict';

const jws = require('jws');
const constants = require('./constants');

// check if two arrays have common elements
var hasCommonElem = (array1, array2) => {
Expand Down Expand Up @@ -189,7 +188,7 @@ exports.verify = function(jwtString, PEMKey, options, callback) {
if (typeof payload.exp !== 'number')
return done(new Error('invalid exp value in payload'));
if (!options.ignoreExpiration) {
if (Math.floor(Date.now() / 1000) >= payload.exp + constants.CLOCK_SKEW) {
if (Math.floor(Date.now() / 1000) >= payload.exp + options.clockSkew) {
return done(new Error('jwt is expired'));
}
}
Expand All @@ -201,7 +200,7 @@ exports.verify = function(jwtString, PEMKey, options, callback) {
return done(new Error('nbf value in payload is not a number'));
if (payload.nbf >= payload.exp)
return done(new Error('nbf value in payload is not before the exp value'));
if (payload.nbf >= Math.floor(Date.now() / 1000) + constants.CLOCK_SKEW)
if (payload.nbf >= Math.floor(Date.now() / 1000) + options.clockSkew)
return done(new Error('jwt is not active'));
}

Expand Down
14 changes: 14 additions & 0 deletions lib/oidcstrategy.js
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,11 @@ function onProfileLoaded(strategy, args) {
* (3) Description:
* the lifetime of nonce in session, default value is NONCE_LIFE_TIME
*
* - `clockSkew` (1) Optional
* (2) must be a positive integer
* (3) Description:
* the clock skew (in seconds) allowed in token validation, default value is CLOCK_SKEW
*
* Examples:
*
* passport.use(new OIDCStrategy({
Expand Down Expand Up @@ -337,6 +342,12 @@ function Strategy(options, verify) {
if (options.loggingLevel) { log.levels('console', options.loggingLevel); }
this.log = log;

// clock skew. Must be a postive integer
if (options.clockSkew && (typeof options.clockSkew !== 'number' || options.clockSkew <= 0 || options.clockSkew % 1 !== 0))
throw new Error('clockSkew must be a positive integer');
if (!options.clockSkew)
options.clockSkew = CONSTANTS.CLOCK_SKEW;

/****************************************************************************************
* Take care of identityMetadata
* (1) Check if it is common endpoint
Expand Down Expand Up @@ -776,6 +787,9 @@ Strategy.prototype.setOptions = function setOptions(params, oauthConfig, options
// nonce
optionsToValidate.nonce = params.nonce;

// clock skew
optionsToValidate.clockSkew = self._options.clockSkew;

// issuer
// if the metadata is not coming from common endpoint, we record the issuer value from metadata
if (!self._options.isCommonEndpoint || (self._options.isCommonEndpoint && params.tenantIdOrName))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ var setReqFromAuthRespRedirect = function(id_token_in_auth_resp, code_in_auth_re
optionsToValidate.ignoreExpiration = true;
optionsToValidate.algorithms = ['RS256'];
optionsToValidate.nonce = nonce_to_use;
optionsToValidate.clockSkew = testStrategy._options.clockSkew;

oauthConfig.authorization_endpoint = "https://login.microsoftonline.com/sijun1b2c.onmicrosoft.com/oauth2/v2.0/authorize?p=b2c_1_signin";
oauthConfig.redirectUrl = "https://localhost:3000/auth/openid/return";
Expand Down
1 change: 1 addition & 0 deletions test/Chai-passport_test/b2c_oidc_implicit_flow_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ var testPrepare = function(id_token_to_use, nonce_to_use, policy_to_use, action)
optionsToValidate.ignoreExpiration = true;
optionsToValidate.algorithms = ['RS256'];
optionsToValidate.nonce = nonce_to_use;
optionsToValidate.clockSkew = testStrategy._options.clockSkew;

if (action) {
for (let i = 0; i < action.length; i++)
Expand Down
116 changes: 116 additions & 0 deletions test/Chai-passport_test/clock_skew_test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/**
* Copyright (c) Microsoft Corporation
* All Rights Reserved
* MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this
* software and associated documentation files (the "Software"), to deal in the Software
* without restriction, including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
* OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
* OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

/* eslint-disable no-new */

'use restrict';

var chai = require('chai');
var url = require('url');
var OIDCStrategy = require('../../lib/index').OIDCStrategy;
var BearerStrategy = require('../../lib/index').BearerStrategy;
var CONSTANTS = require('../../lib/constants');

chai.use(require('chai-passport-strategy'));

var OIDC_options = {
redirectUrl: 'https://returnURL',
clientID: 'my_client_id',
clientSecret: 'my_client_secret',
identityMetadata: 'https://login.microsoftonline.com/xxx.onmicrosoft.com/.well-known/openid-configuration',
responseType: 'id_token',
responseMode: 'form_post',
validateIssuer: true,
passReqToCallback: false,
};

var Bearer_options = {
identityMetadata: 'https://login.microsoftonline.com/xxx.onmicrosoft.com/.well-known/openid-configuration',
clientID: 'my_client_id',
validateIssuer: true,
passReqToCallback: false,
ignoreExpiration: true,
};

describe('OIDCStrategy clock skew test', function() {
it('should have the default clock skew if none is given', function(done) {
var testStrategy = new OIDCStrategy(OIDC_options, function(profile, done) {});
chai.expect(testStrategy._options.clockSkew).to.equal(CONSTANTS.CLOCK_SKEW);
done();
});

it('should have the given clock skew', function(done) {
OIDC_options.clockSkew = 123;
var testStrategy = new OIDCStrategy(OIDC_options, function(profile, done) {});
chai.expect(testStrategy._options.clockSkew).to.equal(123);
done();
});

it('should throw with negative clock skew', function(done) {
OIDC_options.clockSkew = -123;
chai.expect(() => {
new OIDCStrategy(OIDC_options, function(profile, done) {});
}).to.throw('clockSkew must be a positive integer');
done();
});

it('should throw with non-integer clock skew', function(done) {
OIDC_options.clockSkew = 1.23;
chai.expect(() => {
new OIDCStrategy(OIDC_options, function(profile, done) {});
}).to.throw('clockSkew must be a positive integer');
done();
});
});

describe('BearerStrategy clock skew test', function() {
it('should have the default clock skew if none is given', function(done) {
var testStrategy = new BearerStrategy(Bearer_options, function(profile, done) {});
chai.expect(testStrategy._options.clockSkew).to.equal(CONSTANTS.CLOCK_SKEW);
done();
});

it('should have the given clock skew', function(done) {
Bearer_options.clockSkew = 123;
var testStrategy = new BearerStrategy(Bearer_options, function(profile, done) {});
chai.expect(testStrategy._options.clockSkew).to.equal(123);
done();
});

it('should throw with negative clock skew', function(done) {
Bearer_options.clockSkew = -123;
chai.expect(() => {
new BearerStrategy(Bearer_options, function(profile, done) {});
}).to.throw('clockSkew must be a positive integer');
done();
});

it('should throw with non-integer clock skew', function(done) {
Bearer_options.clockSkew = 1.23;
chai.expect(() => {
new BearerStrategy(Bearer_options, function(profile, done) {});
}).to.throw('clockSkew must be a positive integer');
done();
});
});

1 change: 1 addition & 0 deletions test/Chai-passport_test/oidc_common_endpoint_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ var testPrepare = function(id_token_to_use, nonce_to_use, action) {
optionsToValidate.ignoreExpiration = true;
optionsToValidate.algorithms = ['RS256'];
optionsToValidate.nonce = nonce_to_use;
optionsToValidate.clockSkew = testStrategy._options.clockSkew;

if (action) {
for (let i = 0; i < action.length; i++)
Expand Down
1 change: 1 addition & 0 deletions test/Chai-passport_test/oidc_hybrid_and_code_flow_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ var setReqFromAuthRespRedirect = function(id_token_in_auth_resp, code_in_auth_re
optionsToValidate.ignoreExpiration = true;
optionsToValidate.algorithms = ['RS256'];
optionsToValidate.nonce = nonce_to_use;
optionsToValidate.clockSkew = testStrategy._options.clockSkew;

oauthConfig.authorization_endpoint = "https://login.microsoftonline.com/268da1a1-9db4-48b9-b1fe-683250ba90cc/oauth2/authorize";
oauthConfig.redirectUrl = "http://localhost:3000/auth/openid/return";
Expand Down
1 change: 1 addition & 0 deletions test/Chai-passport_test/oidc_implicit_flow_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ var testPrepare = function(id_token_to_use, nonce_to_use, action) {
optionsToValidate.ignoreExpiration = true;
optionsToValidate.algorithms = ['RS256'];
optionsToValidate.nonce = nonce_to_use;
optionsToValidate.clockSkew = testStrategy._options.clockSkew;

if (action) {
for (let i = 0; i < action.length; i++)
Expand Down

0 comments on commit 32b7b3c

Please sign in to comment.