Skip to content
Permalink
Browse files

feat(profile): expose subscription capabilities in profile if available

  • Loading branch information...
lmorchard committed Apr 14, 2019
1 parent dcaad8c commit 9f684aff03830f2f8ca6e55fbd52091346b4a94e
@@ -28,6 +28,7 @@ module.exports = {
locale: Joi.string().optional(),
amrValues: Joi.array().items(Joi.string().required()).optional(),
twoFactorAuthentication: Joi.boolean().optional(),
subscriptions: Joi.array().items(Joi.string().required()).optional(),
profileChangedAt: Joi.number().optional()
}
},
@@ -85,6 +86,9 @@ module.exports = {
if (typeof body.authenticatorAssuranceLevel !== 'undefined') {
result.twoFactorAuthentication = body.authenticatorAssuranceLevel >= 2;
}
if (typeof body.subscriptions !== 'undefined') {
result.subscriptions = body.subscriptions;
}
if (typeof body.profileChangedAt !== 'undefined') {
result.profileChangedAt = body.profileChangedAt;
}
@@ -29,6 +29,7 @@ module.exports = {
locale: Joi.string().allow(null),
amrValues: Joi.array().items(Joi.string().required()).allow(null),
twoFactorAuthentication: Joi.boolean().allow(null),
subscriptions: Joi.array().items(Joi.string().required()).optional(),

//openid-connect
sub: Joi.string().allow(null)
@@ -0,0 +1,39 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

const Joi = require('joi');

const AppError = require('../error');
const logger = require('../logging')('routes.subscriptions');

module.exports = {
auth: {
strategy: 'oauth',
// TODO: using email scope for now, may change someday
scope: ['profile:email', /* openid-connect scope */'email' ]
},
response: {
schema: {
subscriptions: Joi.array().items(Joi.string()).required()
}
},
handler: function subscriptions(req, reply) {
req.server.inject({
allowInternals: true,
method: 'get',
url: '/v1/_core_profile',
headers: req.headers,
credentials: req.auth.credentials
}, res => {
if (res.statusCode !== 200) {
return reply(res);
}
return reply({
// If auth server omits subscriptions, just use an empty list
subscriptions: res.result.subscriptions || []
});
});
}
};

@@ -44,6 +44,11 @@ module.exports = [
path: v('/email'),
config: require('./routes/email')
},
{
method: 'GET',
path: v('/subscriptions'),
config: require('./routes/subscriptions')
},
{
method: 'GET',
path: v('/uid'),
@@ -408,6 +408,57 @@ describe('/email', function() {
});
});

describe('/subscriptions', function() {
var tok = token();

it('should return subscriptions if auth server includes them', function() {
const expected = ['MechaMozilla', 'FirefoxPro'];
mock.tokenGood();
mock.subscriptions(expected);
return Server.api.get({
url: '/subscriptions',
headers: {
authorization: 'Bearer ' + tok
}
}).then(function(res) {
assert.equal(res.statusCode, 200);
assert.deepEqual(JSON.parse(res.payload).subscriptions, expected);
assertSecurityHeaders(res);
});
});

it('should return subscriptions as empty list if missing from auth server', function() {
mock.tokenGood();
mock.email('foo@example.com');
return Server.api.get({
url: '/subscriptions',
headers: {
authorization: 'Bearer ' + tok
}
}).then(function(res) {
assert.equal(res.statusCode, 200);
assert.deepEqual(JSON.parse(res.payload).subscriptions, []);
assertSecurityHeaders(res);
});
});

it('should NOT return subscriptions if not email scope', function() {
mock.token({
user: USERID,
scope: ['profile:uid']
});
return Server.api.get({
url: '/subscriptions',
headers: {
authorization: 'Bearer ' + tok
}
}).then(function(res) {
assert.equal(res.statusCode, 403);
assertSecurityHeaders(res);
});
});
});

describe('/_core_profile', () => {
const tok = token();

@@ -444,6 +495,31 @@ describe('/_core_profile', () => {
assert.equal(body.locale, 'en-US');
assert.deepEqual(body.amrValues, ['pwd']);
assert.equal(body.twoFactorAuthentication, false);
assert.equal(typeof body.subscriptions, 'undefined');
assertSecurityHeaders(res);
});
});

it('should return subscriptions if returned by auth-server', () => {
const expected = ['MechaMozilla', 'FirefoxPro'];
mock.tokenGood();
mock.coreProfile({
email: 'user@example.domain',
locale: 'en-US',
authenticationMethods: ['pwd'],
authenticatorAssuranceLevel: 1,
subscriptions: expected
});
return Server.api.get({
allowInternals: true,
url: '/_core_profile',
headers: {
authorization: 'Bearer ' + tok
}
}).then(res => {
assert.equal(res.statusCode, 200);
const body = JSON.parse(res.payload);
assert.deepEqual(body.subscriptions, expected);
assertSecurityHeaders(res);
});
});
@@ -149,6 +149,13 @@ module.exports = function mock(options) {
});
},

subscriptions: function mockSubscriptions(subscriptions) {
var parts = url.parse(config.get('authServer.url'));
return nock(parts.protocol + '//' + parts.host)
.get(parts.path + '/account/profile')
.reply(200, { subscriptions });
},

profileChangedAt: function mockProfileChangedAt(email, profileChangedAt) {
var parts = url.parse(config.get('authServer.url'));
return nock(parts.protocol + '//' + parts.host)

0 comments on commit 9f684af

Please sign in to comment.
You can’t perform that action at this time.