feat(keys): Check lastAuthAt freshness when fetching key data. (#506) r=@vladikoff
This commit is contained in:
Родитель
4f613d33d4
Коммит
e0de2f3b57
|
@ -127,6 +127,12 @@ const conf = convict({
|
|||
format: 'duration',
|
||||
default: '15 minutes',
|
||||
env: 'FXA_EXPIRATION_CODE'
|
||||
},
|
||||
keyDataAuth: {
|
||||
doc: 'Key data can only be fetched if lastAuthAt is within this duration',
|
||||
format: 'duration',
|
||||
default: '1 hour',
|
||||
env: 'FXA_EXPIRATION_KEY_DATA_AUTH'
|
||||
}
|
||||
},
|
||||
refreshToken: {
|
||||
|
|
12
lib/error.js
12
lib/error.js
|
@ -116,6 +116,7 @@ AppError.invalidAssertion = function invalidAssertion() {
|
|||
message: 'Invalid assertion'
|
||||
});
|
||||
};
|
||||
|
||||
AppError.unknownCode = function unknownCode(code) {
|
||||
return new AppError({
|
||||
code: 400,
|
||||
|
@ -264,4 +265,15 @@ AppError.missingPkceParameters = function missingPkceParameters() {
|
|||
});
|
||||
};
|
||||
|
||||
AppError.staleAuthAt = function staleAuthAt(authAt) {
|
||||
return new AppError({
|
||||
code: 401,
|
||||
error: 'Bad Request',
|
||||
errno: 119,
|
||||
message: 'Stale authentication timestamp'
|
||||
}, {
|
||||
authAt: authAt
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = AppError;
|
||||
|
|
|
@ -11,6 +11,9 @@ const P = require('../promise');
|
|||
const validators = require('../validators');
|
||||
const verify = require('../browserid');
|
||||
const Scope = require('../scope');
|
||||
const config = require('../config');
|
||||
|
||||
const AUTH_EXPIRES_AFTER_MS = config.get('expiration.keyDataAuth');
|
||||
|
||||
/**
|
||||
* We're using a static value for key material on purpose, in future this value can read from the DB.
|
||||
|
@ -71,6 +74,10 @@ module.exports = {
|
|||
const scopeResults = results[1];
|
||||
const response = {};
|
||||
|
||||
if (assertionData['fxa-lastAuthAt'] < (Date.now() - AUTH_EXPIRES_AFTER_MS) / 1000) {
|
||||
throw AppError.staleAuthAt(assertionData['fxa-lastAuthAt']);
|
||||
}
|
||||
|
||||
scopeResults.forEach((keyScope) => {
|
||||
response[keyScope.scope] = {
|
||||
identifier: keyScope.scope,
|
||||
|
|
32
test/api.js
32
test/api.js
|
@ -21,13 +21,25 @@ const assertSecurityHeaders = require('./lib/util').assertSecurityHeaders;
|
|||
|
||||
const USERID = unique(16).toString('hex');
|
||||
const VEMAIL = unique(4).toString('hex') + '@mozilla.com';
|
||||
const AUTH_AT = Math.floor(Date.now() / 1000);
|
||||
const STALE_AUTH_AT = AUTH_AT - (2 * 24 * 60 * 60);
|
||||
const VERIFY_GOOD = JSON.stringify({
|
||||
status: 'okay',
|
||||
email: USERID + '@' + config.get('browserid.issuer'),
|
||||
issuer: config.get('browserid.issuer'),
|
||||
idpClaims: {
|
||||
'fxa-verifiedEmail': VEMAIL,
|
||||
'fxa-lastAuthAt': 123456,
|
||||
'fxa-lastAuthAt': AUTH_AT,
|
||||
'fxa-generation': 123456
|
||||
}
|
||||
});
|
||||
const VERIFY_GOOD_BUT_STALE = JSON.stringify({
|
||||
status: 'okay',
|
||||
email: USERID + '@' + config.get('browserid.issuer'),
|
||||
issuer: config.get('browserid.issuer'),
|
||||
idpClaims: {
|
||||
'fxa-verifiedEmail': VEMAIL,
|
||||
'fxa-lastAuthAt': STALE_AUTH_AT,
|
||||
'fxa-generation': 123456
|
||||
}
|
||||
});
|
||||
|
@ -1226,7 +1238,7 @@ describe('/v1', function() {
|
|||
assert.equal(res.result.access_token.length,
|
||||
config.get('unique.token') * 2);
|
||||
assert.equal(res.result.scope, 'foo bar');
|
||||
assert.equal(res.result.auth_at, 123456);
|
||||
assert.equal(res.result.auth_at, AUTH_AT);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1262,7 +1274,7 @@ describe('/v1', function() {
|
|||
assert.equal(res.result.refresh_token.length,
|
||||
config.get('unique.token') * 2);
|
||||
assert.equal(res.result.scope, 'foo bar');
|
||||
assert.equal(res.result.auth_at, 123456);
|
||||
assert.equal(res.result.auth_at, AUTH_AT);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -2336,6 +2348,20 @@ describe('/v1', function() {
|
|||
assert.equal(Object.keys(res.result).length, 0, 'no scoped keys');
|
||||
});
|
||||
});
|
||||
|
||||
it('fails for assertions with lastAuthAt too far in the past', () => {
|
||||
genericRequest.payload.client_id = NO_KEY_SCOPES_CLIENT_ID;
|
||||
|
||||
mockAssertion().reply(200, VERIFY_GOOD_BUT_STALE);
|
||||
return Server.api.post(genericRequest)
|
||||
.then((res) => {
|
||||
assert.equal(res.statusCode, 401);
|
||||
assertSecurityHeaders(res);
|
||||
const body = res.result;
|
||||
assert.equal(body.errno, 119);
|
||||
assert.equal(body.message, 'Stale authentication timestamp');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
|
Загрузка…
Ссылка в новой задаче