From 9d3aea60e6673610505da347af67641fe2e26e87 Mon Sep 17 00:00:00 2001 From: vladikoff Date: Thu, 28 Mar 2019 09:26:43 -0400 Subject: [PATCH] feat(token): return the uid from the /token endpoint Fixes https://github.com/mozilla/fxa-auth-server/pull/2985/files#r268891903 --- fxa-oauth-server/lib/grant.js | 1 + fxa-oauth-server/lib/routes/authorization.js | 1 + fxa-oauth-server/lib/routes/token.js | 1 + fxa-oauth-server/lib/validators.js | 4 ++++ fxa-oauth-server/test/api.js | 2 ++ lib/oauthdb/grant-tokens-from-authorization-code.js | 1 + lib/oauthdb/grant-tokens-from-credentials.js | 1 + lib/oauthdb/grant-tokens-from-refresh-token.js | 1 + lib/routes/validators.js | 1 + test/local/oauthdb/grant-tokens-from-authorization-code.js | 3 +++ test/local/oauthdb/grant-tokens-from-refresh-token.js | 5 +++++ test/local/oauthdb/grant-tokens-from-session-token.js | 5 +++++ test/remote/oauth_tests.js | 3 +++ 13 files changed, 29 insertions(+) diff --git a/fxa-oauth-server/lib/grant.js b/fxa-oauth-server/lib/grant.js index 44f20a2d6..3052a0244 100644 --- a/fxa-oauth-server/lib/grant.js +++ b/fxa-oauth-server/lib/grant.js @@ -119,6 +119,7 @@ module.exports.generateTokens = async function generateTokens(grant) { const access = await db.generateAccessToken(grant); const result = { access_token: access.token.toString('hex'), + user: access.userId.toString('hex'), token_type: access.type, scope: access.scope.toString() }; diff --git a/fxa-oauth-server/lib/routes/authorization.js b/fxa-oauth-server/lib/routes/authorization.js index dde642a1c..f36ee1c9a 100644 --- a/fxa-oauth-server/lib/routes/authorization.js +++ b/fxa-oauth-server/lib/routes/authorization.js @@ -111,6 +111,7 @@ module.exports = { code: Joi.string(), state: Joi.string(), access_token: validators.token, + user: validators.uid, token_type: Joi.string().valid('bearer'), scope: Joi.string().allow(''), auth_at: Joi.number(), diff --git a/fxa-oauth-server/lib/routes/token.js b/fxa-oauth-server/lib/routes/token.js index c6ce9de92..f18b8c297 100644 --- a/fxa-oauth-server/lib/routes/token.js +++ b/fxa-oauth-server/lib/routes/token.js @@ -166,6 +166,7 @@ module.exports = { access_token: validators.token.required(), refresh_token: validators.token, id_token: validators.assertion, + user: validators.uid.required(), scope: validators.scope.required(), token_type: Joi.string().valid('bearer').required(), expires_in: Joi.number().max(MAX_TTL_S).required(), diff --git a/fxa-oauth-server/lib/validators.js b/fxa-oauth-server/lib/validators.js index c75e396eb..72eae6541 100644 --- a/fxa-oauth-server/lib/validators.js +++ b/fxa-oauth-server/lib/validators.js @@ -29,6 +29,10 @@ exports.token = Joi.string() .length(config.get('unique.token') * 2) .regex(exports.HEX_STRING); +exports.uid = Joi.string() + .length(32) + .regex(exports.HEX_STRING); + const scopeString = Joi.string().max(256); exports.scope = Joi.extend({ diff --git a/fxa-oauth-server/test/api.js b/fxa-oauth-server/test/api.js index 0db861be6..39c1144f6 100644 --- a/fxa-oauth-server/test/api.js +++ b/fxa-oauth-server/test/api.js @@ -814,6 +814,7 @@ describe('/v1', function() { assert.equal(res.statusCode, 200); assertSecurityHeaders(res); assert(res.result.access_token); + assert(res.result.user); assert.equal(res.result.token_type, 'bearer'); assert(res.result.scope); assert(res.result.expires_in <= defaultExpiresIn); @@ -1096,6 +1097,7 @@ describe('/v1', function() { assert.equal(res.statusCode, 200); assertSecurityHeaders(res); assert.ok(res.result.access_token); + assert.ok(res.result.user); assert.equal(res.result.token_type, 'bearer'); assert.ok(res.result.auth_at); assert.ok(res.result.expires_in); diff --git a/lib/oauthdb/grant-tokens-from-authorization-code.js b/lib/oauthdb/grant-tokens-from-authorization-code.js index 4473c7052..8fac40d96 100644 --- a/lib/oauthdb/grant-tokens-from-authorization-code.js +++ b/lib/oauthdb/grant-tokens-from-authorization-code.js @@ -28,6 +28,7 @@ module.exports = (config) => { refresh_token: validators.refreshToken.optional(), id_token: validators.assertion.optional(), scope: validators.scope.required(), + user: validators.uid.required(), token_type: Joi.string().valid('bearer').required(), expires_in: Joi.number().required(), auth_at: Joi.number().required(), diff --git a/lib/oauthdb/grant-tokens-from-credentials.js b/lib/oauthdb/grant-tokens-from-credentials.js index 081d978a9..3a8b8267c 100644 --- a/lib/oauthdb/grant-tokens-from-credentials.js +++ b/lib/oauthdb/grant-tokens-from-credentials.js @@ -26,6 +26,7 @@ module.exports = (config) => { access_token: validators.accessToken.required(), refresh_token: validators.refreshToken.optional(), id_token: validators.assertion.optional(), + user: validators.uid.required(), scope: validators.scope.required(), auth_at: Joi.number().required(), token_type: Joi.string().valid('bearer').required(), diff --git a/lib/oauthdb/grant-tokens-from-refresh-token.js b/lib/oauthdb/grant-tokens-from-refresh-token.js index d41d032f8..f3131848e 100644 --- a/lib/oauthdb/grant-tokens-from-refresh-token.js +++ b/lib/oauthdb/grant-tokens-from-refresh-token.js @@ -24,6 +24,7 @@ module.exports = (config) => { }), response: Joi.object({ access_token: validators.accessToken.required(), + user: validators.uid.required(), scope: validators.scope.required(), token_type: Joi.string().valid('bearer').required(), expires_in: Joi.number().required() diff --git a/lib/routes/validators.js b/lib/routes/validators.js index 81fc3c651..1026f58e5 100644 --- a/lib/routes/validators.js +++ b/lib/routes/validators.js @@ -86,6 +86,7 @@ module.exports.clientId = module.exports.hexString.length(16); module.exports.clientSecret = module.exports.hexString; module.exports.accessToken = module.exports.hexString.length(64); module.exports.refreshToken = module.exports.hexString.length(64); +module.exports.uid = module.exports.hexString.length(32); module.exports.authorizationCode = module.exports.hexString.length(64); // Note that the empty string is a valid scope value (meaning "no permissions"). module.exports.scope = isA.string().max(256).regex(/^[a-zA-Z0-9 _\/.:-]*$/).allow(''); diff --git a/test/local/oauthdb/grant-tokens-from-authorization-code.js b/test/local/oauthdb/grant-tokens-from-authorization-code.js index d5fa518a7..9ca5a081e 100644 --- a/test/local/oauthdb/grant-tokens-from-authorization-code.js +++ b/test/local/oauthdb/grant-tokens-from-authorization-code.js @@ -10,6 +10,7 @@ const oauthdbModule = require('../../../lib/oauthdb'); const error = require('../../../lib/error'); const { mockLog } = require('../../mocks'); +const MOCK_USER_ID = '5A6773A8D23E49FDAFCC976882E0B57E'; const MOCK_CLIENT_ID = '0123456789ABCDEF'; const MOCK_AUTHORIZATION_CODE = '1111112222223333334444445555556611111122222233333344444455555566'; const MOCK_ACCESS_TOKEN = 'aaaaaa2222223333334444445555556611111122222233333344444455555566'; @@ -39,6 +40,7 @@ describe('oauthdb/grantTokensFromAuthorizationCode', () => { mockOAuthServer.post('/v1/token', body => true) .reply(200, { access_token: MOCK_ACCESS_TOKEN, + user: MOCK_USER_ID, scope: '', token_type: 'bearer', expires_in: 123, @@ -52,6 +54,7 @@ describe('oauthdb/grantTokensFromAuthorizationCode', () => { }); assert.deepEqual(res, { access_token: MOCK_ACCESS_TOKEN, + user: MOCK_USER_ID, scope: '', token_type: 'bearer', expires_in: 123, diff --git a/test/local/oauthdb/grant-tokens-from-refresh-token.js b/test/local/oauthdb/grant-tokens-from-refresh-token.js index 37cc606c7..95d105d6d 100644 --- a/test/local/oauthdb/grant-tokens-from-refresh-token.js +++ b/test/local/oauthdb/grant-tokens-from-refresh-token.js @@ -10,6 +10,7 @@ const oauthdbModule = require('../../../lib/oauthdb'); const error = require('../../../lib/error'); const { mockLog } = require('../../mocks'); +const MOCK_USER_ID = '5A6773A8D23E49FDAFCC976882E0B57E'; const MOCK_CLIENT_ID = '0123456789ABCDEF'; const MOCK_ACCESS_TOKEN = 'aaaaaa2222223333334444445555556611111122222233333344444455555566'; const MOCK_REFRESH_TOKEN = 'bbbbbb2222223333334444445555556611111122222233333344444455555566'; @@ -39,6 +40,7 @@ describe('oauthdb/grantTokensFromRefreshToken', () => { mockOAuthServer.post('/v1/token', body => true) .reply(200, { access_token: MOCK_ACCESS_TOKEN, + user: MOCK_USER_ID, scope: '', token_type: 'bearer', expires_in: 123, @@ -52,6 +54,7 @@ describe('oauthdb/grantTokensFromRefreshToken', () => { }); assert.deepEqual(res, { access_token: MOCK_ACCESS_TOKEN, + user: MOCK_USER_ID, scope: '', token_type: 'bearer', expires_in: 123, @@ -62,6 +65,7 @@ describe('oauthdb/grantTokensFromRefreshToken', () => { mockOAuthServer.post('/v1/token', body => true) .reply(200, { access_token: MOCK_ACCESS_TOKEN, + user: MOCK_USER_ID, scope: '', token_type: 'bearer', expires_in: 123, @@ -76,6 +80,7 @@ describe('oauthdb/grantTokensFromRefreshToken', () => { }); assert.deepEqual(res, { access_token: MOCK_ACCESS_TOKEN, + user: MOCK_USER_ID, scope: '', token_type: 'bearer', expires_in: 123, diff --git a/test/local/oauthdb/grant-tokens-from-session-token.js b/test/local/oauthdb/grant-tokens-from-session-token.js index 5c8ff2504..c0d54f201 100644 --- a/test/local/oauthdb/grant-tokens-from-session-token.js +++ b/test/local/oauthdb/grant-tokens-from-session-token.js @@ -9,6 +9,7 @@ const nock = require('nock'); const oauthdbModule = require('../../../lib/oauthdb'); const { mockLog } = require('../../mocks'); +const MOCK_USER_ID = '5A6773A8D23E49FDAFCC976882E0B57E'; const MOCK_CLIENT_ID = '0123456789ABCDEF'; const MOCK_ACCESS_TOKEN = 'aaaaaa2222223333334444445555556611111122222233333344444455555566'; const MOCK_REFRESH_TOKEN = 'bbbbbb2222223333334444445555556611111122222233333344444455555566'; @@ -46,6 +47,7 @@ describe('oauthdb/grantTokensFromSessionToken', () => { mockOAuthServer.post('/v1/token', body => true) .reply(200, { access_token: MOCK_ACCESS_TOKEN, + user: MOCK_USER_ID, scope: 'test1', token_type: 'bearer', expires_in: 123, @@ -58,6 +60,7 @@ describe('oauthdb/grantTokensFromSessionToken', () => { }); assert.deepEqual(res, { access_token: MOCK_ACCESS_TOKEN, + user: MOCK_USER_ID, scope: 'test1', token_type: 'bearer', expires_in: 123, @@ -71,6 +74,7 @@ describe('oauthdb/grantTokensFromSessionToken', () => { access_token: MOCK_ACCESS_TOKEN, refresh_token: MOCK_REFRESH_TOKEN, id_token: MOCK_ID_TOKEN, + user: MOCK_USER_ID, scope: 'test1 openid', token_type: 'bearer', expires_in: 123, @@ -88,6 +92,7 @@ describe('oauthdb/grantTokensFromSessionToken', () => { access_token: MOCK_ACCESS_TOKEN, refresh_token: MOCK_REFRESH_TOKEN, id_token: MOCK_ID_TOKEN, + user: MOCK_USER_ID, scope: 'test1 openid', token_type: 'bearer', expires_in: 123, diff --git a/test/remote/oauth_tests.js b/test/remote/oauth_tests.js index 0aaaf1844..73794a260 100644 --- a/test/remote/oauth_tests.js +++ b/test/remote/oauth_tests.js @@ -97,6 +97,7 @@ describe('/oauth/ routes', function () { assert.ok(res.access_token); assert.ok(res.refresh_token); + assert.ok(res.user); assert.equal(res.scope, SCOPE); assert.ok(res.auth_at); assert.ok(res.expires_in); @@ -124,6 +125,7 @@ describe('/oauth/ routes', function () { assert.ok(res.access_token); assert.ok(res.refresh_token); assert.ok(res.id_token); + assert.ok(res.user); assert.equal(res.scope, SCOPE); assert.ok(res.auth_at); assert.ok(res.expires_in); @@ -135,6 +137,7 @@ describe('/oauth/ routes', function () { grant_type: 'refresh_token', }); assert.ok(res.access_token); + assert.ok(res.user); assert.equal(res.scope, SCOPE); assert.ok(res.expires_in); assert.ok(res.token_type);