Skip to content

Commit

Permalink
Merge branch 'master' into typescript-types
Browse files Browse the repository at this point in the history
  • Loading branch information
joshcanhelp authored Oct 8, 2019
2 parents 72803d2 + 559d4ac commit a05772c
Show file tree
Hide file tree
Showing 17 changed files with 203 additions and 155 deletions.
12 changes: 11 additions & 1 deletion lib/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,15 @@ async function get(config) {

const issuer = await Issuer.discover(config.issuerBaseURL);

if (Array.isArray(issuer.id_token_signing_alg_values_supported) &&
!issuer.id_token_signing_alg_values_supported.includes(config.idTokenAlg)) {
throw new Error(`The issuer doesn't support the ID token algorithm ${config.idTokenAlg}
Supported types:
- ${issuer.id_token_signing_alg_values_supported.sort().join('\n- ')}
`);
}

// TODO: Does not respect out-of-order response types; 'id_token code' is not respected if issuer is 'code id_token'
if (Array.isArray(issuer.response_types_supported) &&
!issuer.response_types_supported.includes(authorizeParams.response_type)) {
throw new Error(`The issuer doesn't support the response_type ${authorizeParams.response_type}
Expand All @@ -43,7 +52,8 @@ Supported response modes:

const client = new issuer.Client({
client_id: config.clientID,
client_secret: config.clientSecret
client_secret: config.clientSecret,
id_token_signed_response_alg: config.idTokenAlg,
});

if (config.idpLogout && !issuer.end_session_endpoint) {
Expand Down
13 changes: 9 additions & 4 deletions lib/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const paramsSchema = Joi.object().keys({
baseURL: Joi.string().uri().required(),
clientID: Joi.string().required(),
clientSecret: Joi.string().optional(),
idTokenAlg: Joi.string().not('none').optional().default('RS256'),
authorizationParams: Joi.object().optional(),
clockTolerance: Joi.number().optional().default(5),
getUser: Joi.func().optional().default(getUser),
Expand Down Expand Up @@ -77,11 +78,15 @@ module.exports.get = function(params) {

config.authorizationParams = buildAuthorizeParams(config.authorizationParams);

const missingClientSecret = !config.clientSecret &&
config.authorizationParams.response_type.split(' ').includes('code');
// Code grant requires a client secret to exchange the code for tokens
const responseTypeHasCode = config.authorizationParams.response_type.split(' ').includes('code');
if (responseTypeHasCode && !config.clientSecret) {
throw new Error('"clientSecret" is required for response_type code');
}

if(missingClientSecret) {
throw new Error('"clientSecret" is required for response_type code and response_mode query');
// HS256 ID tokens require a client secret to validate the signature.
if ('HS' === config.idTokenAlg.substring(0,2) && !config.clientSecret) {
throw new Error('"clientSecret" is required for ID tokens with HS algorithms');
}

return config;
Expand Down
60 changes: 30 additions & 30 deletions test/auth.tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ describe('auth', function() {

before(async function() {
router = expressOpenid.auth({
clientID: '123',
baseURL: 'https://myapp.com',
issuerBaseURL: 'https://flosser.auth0.com',
clientID: '__test_client_id__',
baseURL: 'https://example.org',
issuerBaseURL: 'https://test.auth0.com',
required: false
});
baseUrl = await server.create(router);
Expand All @@ -46,14 +46,14 @@ describe('auth', function() {
assert.equal(res.statusCode, 302);

const parsed = url.parse(res.headers.location, true);
assert.equal(parsed.hostname, 'flosser.auth0.com');
assert.equal(parsed.hostname, 'test.auth0.com');
assert.equal(parsed.pathname, '/authorize');
assert.equal(parsed.query.client_id, '123');
assert.equal(parsed.query.client_id, '__test_client_id__');

assert.equal(parsed.query.scope, 'openid profile email');
assert.equal(parsed.query.response_type, 'id_token');
assert.equal(parsed.query.response_mode, 'form_post');
assert.equal(parsed.query.redirect_uri, 'https://myapp.com/callback');
assert.equal(parsed.query.redirect_uri, 'https://example.org/callback');
assert.property(parsed.query, 'nonce');
assert.property(parsed.query, 'state');

Expand All @@ -70,9 +70,9 @@ describe('auth', function() {

before(async function() {
router = expressOpenid.auth({
clientID: '123',
baseURL: 'https://myapp.com',
issuerBaseURL: 'https://flosser.auth0.com',
clientID: '__test_client_id__',
baseURL: 'https://example.org',
issuerBaseURL: 'https://test.auth0.com',
authorizationParams: {
response_mode: undefined,
response_type: 'none',
Expand All @@ -89,13 +89,13 @@ describe('auth', function() {

const parsed = url.parse(res.headers.location, true);

assert.equal(parsed.hostname, 'flosser.auth0.com');
assert.equal(parsed.hostname, 'test.auth0.com');
assert.equal(parsed.pathname, '/authorize');
assert.equal(parsed.query.client_id, '123');
assert.equal(parsed.query.client_id, '__test_client_id__');
assert.equal(parsed.query.scope, 'openid profile email');
assert.equal(parsed.query.response_type, 'none');
assert.equal(parsed.query.response_mode, undefined);
assert.equal(parsed.query.redirect_uri, 'https://myapp.com/callback');
assert.equal(parsed.query.redirect_uri, 'https://example.org/callback');
assert.property(parsed.query, 'nonce');
assert.property(parsed.query, 'state');
});
Expand All @@ -111,10 +111,10 @@ describe('auth', function() {

before(async function() {
router = router = expressOpenid.auth({
clientID: '123',
clientSecret: '456',
baseURL: 'https://myapp.com',
issuerBaseURL: 'https://flosser.auth0.com',
clientID: '__test_client_id__',
clientSecret: '__test_client_secret__',
baseURL: 'https://example.org',
issuerBaseURL: 'https://test.auth0.com',
authorizationParams: {
response_mode: undefined,
response_type: 'code',
Expand All @@ -131,13 +131,13 @@ describe('auth', function() {

const parsed = url.parse(res.headers.location, true);

assert.equal(parsed.hostname, 'flosser.auth0.com');
assert.equal(parsed.hostname, 'test.auth0.com');
assert.equal(parsed.pathname, '/authorize');
assert.equal(parsed.query.client_id, '123');
assert.equal(parsed.query.client_id, '__test_client_id__');
assert.equal(parsed.query.scope, 'openid profile email');
assert.equal(parsed.query.response_type, 'code');
assert.equal(parsed.query.response_mode, undefined);
assert.equal(parsed.query.redirect_uri, 'https://myapp.com/callback');
assert.equal(parsed.query.redirect_uri, 'https://example.org/callback');
assert.property(parsed.query, 'nonce');
assert.property(parsed.query, 'state');
});
Expand All @@ -153,9 +153,9 @@ describe('auth', function() {

before(async function() {
router = router = expressOpenid.auth({
clientID: '123',
baseURL: 'https://myapp.com',
issuerBaseURL: 'https://flosser.auth0.com',
clientID: '__test_client_id__',
baseURL: 'https://example.org',
issuerBaseURL: 'https://test.auth0.com',
authorizationParams: {
response_mode: undefined,
response_type: 'id_token',
Expand All @@ -171,13 +171,13 @@ describe('auth', function() {

const parsed = url.parse(res.headers.location, true);

assert.equal(parsed.hostname, 'flosser.auth0.com');
assert.equal(parsed.hostname, 'test.auth0.com');
assert.equal(parsed.pathname, '/authorize');
assert.equal(parsed.query.client_id, '123');
assert.equal(parsed.query.client_id, '__test_client_id__');
assert.equal(parsed.query.scope, 'openid profile email');
assert.equal(parsed.query.response_type, 'id_token');
assert.equal(parsed.query.response_mode, undefined);
assert.equal(parsed.query.redirect_uri, 'https://myapp.com/callback');
assert.equal(parsed.query.redirect_uri, 'https://example.org/callback');
assert.property(parsed.query, 'nonce');
assert.property(parsed.query, 'state');
});
Expand All @@ -195,9 +195,9 @@ describe('auth', function() {

before(async function() {
router = expressOpenid.auth({
clientID: '123',
baseURL: 'https://myapp.com',
issuerBaseURL: 'https://flosser.auth0.com',
clientID: '__test_client_id__',
baseURL: 'https://example.org',
issuerBaseURL: 'https://test.auth0.com',
redirectUriPath: '/custom-callback',
loginPath: '/custom-login',
logoutPath: '/custom-logout',
Expand All @@ -223,9 +223,9 @@ describe('auth', function() {
assert.equal(res.statusCode, 302);

const parsed = url.parse(res.headers.location, true);
assert.equal(parsed.hostname, 'flosser.auth0.com');
assert.equal(parsed.hostname, 'test.auth0.com');
assert.equal(parsed.pathname, '/authorize');
assert.equal(parsed.query.redirect_uri, 'https://myapp.com/custom-callback');
assert.equal(parsed.query.redirect_uri, 'https://example.org/custom-callback');
});

});
Expand Down
70 changes: 32 additions & 38 deletions test/callback_route_form_post.tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ const request = require('request-promise-native').defaults({
const expressOpenid = require('..');
const server = require('./fixture/server');
const cert = require('./fixture/cert');
const clientID = 'foobar';
const clientID = '__test_client_id__';

function testCase(params) {
return () => {
const router = expressOpenid.auth({
clientID: clientID,
baseURL: 'https://myapp.com',
issuerBaseURL: 'https://flosser.auth0.com',
baseURL: 'https://example.org',
issuerBaseURL: 'https://test.auth0.com',
required: false
});

Expand Down Expand Up @@ -61,8 +61,8 @@ function testCase(params) {
describe('callback routes response_type: id_token, response_mode: form_post', function() {
describe('when body is empty', testCase({
session: {
nonce: '123',
state: '123'
nonce: '__test_nonce__',
state: '__test_state__'
},
body: true,
assertions() {
Expand All @@ -78,13 +78,11 @@ describe('callback routes response_type: id_token, response_mode: form_post', fu

describe("when state doesn't match", testCase({
session: {
nonce: '123',
state: '123'
nonce: '__test_nonce__',
state: '__valid_state__'
},
body: {
nonce: '123',
state: '456',
id_token: 'sioua'
state: '__invalid_state__'
},
assertions() {
it('should return 400', function() {
Expand All @@ -99,13 +97,12 @@ describe('callback routes response_type: id_token, response_mode: form_post', fu

describe("when id_token can't be parsed", testCase({
session: {
nonce: '123',
state: '123'
nonce: '__test_nonce__',
state: '__test_state__'
},
body: {
nonce: '123',
state: '123',
id_token: 'sioua'
state: '__test_state__',
id_token: '__invalid_token__'
},
assertions() {
it('should return 400', function() {
Expand All @@ -120,13 +117,12 @@ describe('callback routes response_type: id_token, response_mode: form_post', fu

describe('when id_token has invalid alg', testCase({
session: {
nonce: '123',
state: '123'
nonce: '__test_nonce__',
state: '__test_state__'
},
body: {
nonce: '123',
state: '123',
id_token: jwt.sign({ foo: '123'}, 'f00')
state: '__test_state__',
id_token: jwt.sign({sub: '__test_sub__'}, '__invalid_alg__')
},
assertions() {
it('should return 400', function() {
Expand All @@ -141,13 +137,12 @@ describe('callback routes response_type: id_token, response_mode: form_post', fu

describe('when id_token is missing issuer', testCase({
session: {
nonce: '123',
state: '123'
nonce: '__test_nonce__',
state: '__test_state__'
},
body: {
nonce: '123',
state: '123',
id_token: jwt.sign({ foo: '123'}, cert.key, { algorithm: 'RS256' })
state: '__test_state__',
id_token: jwt.sign({sub: '__test_sub__'}, cert.key, { algorithm: 'RS256' })
},
assertions() {
it('should return 400', function() {
Expand All @@ -162,24 +157,23 @@ describe('callback routes response_type: id_token, response_mode: form_post', fu

describe('when id_token is valid', testCase({
session: {
state: '123',
nonce: 'abcdefg',
returnTo: '/foobar'
state: '__test_state__',
nonce: '__test_nonce__',
returnTo: '/return-to'
},
body: {
nonce: '123',
state: '123',
state: '__test_state__',
id_token: jwt.sign({
'nickname': 'jjjj',
'name': 'Jeranio',
'email': 'jjjj@example.com',
'nickname': '__test_nickname__',
'name': '__test_name__',
'email': '__test_email__',
'email_verified': true,
'iss': 'https://flosser.auth0.com/',
'sub': 'xasdas',
'iss': 'https://test.auth0.com/',
'sub': '__test_sub__',
'aud': clientID,
'iat': Math.round(Date.now() / 1000),
'exp': Math.round(Date.now() / 1000) + 60000,
'nonce': 'abcdefg'
'nonce': '__test_nonce__'
}, cert.key, { algorithm: 'RS256', header: { kid: cert.kid } })
},
assertions() {
Expand All @@ -188,7 +182,7 @@ describe('callback routes response_type: id_token, response_mode: form_post', fu
});

it('should redirect to the intended url', function() {
assert.equal(this.response.headers['location'], '/foobar');
assert.equal(this.response.headers['location'], '/return-to');
});

it('should contain the claims in the current session', function() {
Expand All @@ -201,7 +195,7 @@ describe('callback routes response_type: id_token, response_mode: form_post', fu
json: true,
jar: this.jar
});
assert.equal(res.body.nickname, 'jjjj');
assert.equal(res.body.nickname, '__test_nickname__');
});
}
}));
Expand Down
6 changes: 3 additions & 3 deletions test/client.tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ describe('client initialization', function() {
const config = getConfig({
clientID: '__test_client_id__',
clientSecret: '__test_client_secret__',
issuerBaseURL: 'https://flosser.auth0.com',
baseURL: 'https://theapplication.com',
issuerBaseURL: 'https://test.auth0.com',
baseURL: 'https://example.org',
});

let client;
before(async function() {
client = await getClient(config);

nock('https://flosser.auth0.com')
nock('https://test.auth0.com')
.post('/introspection')
.reply(200, function() {
return this.req.headers;
Expand Down
Loading

0 comments on commit a05772c

Please sign in to comment.