Skip to content

Commit 31b956b

Browse files
authored
fix: Improve JWT authentication option handling (#1261)
1 parent c5dc7a2 commit 31b956b

File tree

9 files changed

+71
-27
lines changed

9 files changed

+71
-27
lines changed

packages/authentication/lib/core.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const jsonwebtoken = require('jsonwebtoken');
44
const uuidv4 = require('uuid/v4');
55
const { NotAuthenticated, BadRequest } = require('@feathersjs/errors');
66

7-
const getOptions = require('./options');
7+
const defaultOptions = require('./options');
88
const debug = require('debug')('@feathersjs/authentication/base');
99
const createJWT = promisify(jsonwebtoken.sign);
1010
const verifyJWT = promisify(jsonwebtoken.verify);
@@ -25,7 +25,7 @@ module.exports = class AuthenticationBase {
2525

2626
get configuration () {
2727
// Always returns a copy of the authentication configuration
28-
return getOptions(this.app.get(this.configKey));
28+
return Object.assign({}, defaultOptions, this.app.get(this.configKey));
2929
}
3030

3131
get strategyNames () {

packages/authentication/lib/jwt.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,16 @@ module.exports = class JWTStrategy {
2626
}, config);
2727
}
2828

29+
verifyConfiguration () {
30+
const allowedKeys = [ 'entity', 'service', 'header', 'schemes' ];
31+
32+
for (let key of Object.keys(this.configuration)) {
33+
if (!allowedKeys.includes(key)) {
34+
throw new Error(`Invalid JwtStrategy option 'authentication.${this.name}.${key}'. Did you mean to set it in 'authentication.jwtOptions'?`);
35+
}
36+
}
37+
}
38+
2939
getEntity (id, params) {
3040
const { service } = this.configuration;
3141
const entityService = this.app.service(service);
Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
const { merge } = require('lodash');
2-
3-
const defaults = {
1+
module.exports = {
42
entity: 'user',
53
service: 'users',
64
strategies: [],
@@ -12,7 +10,3 @@ const defaults = {
1210
expiresIn: '1d'
1311
}
1412
};
15-
16-
module.exports = function (...otherOptions) {
17-
return merge({}, defaults, ...otherOptions);
18-
};

packages/authentication/lib/service.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ module.exports = class AuthenticationService extends AuthenticationCore {
5353
this.getJwtOptions(authResult, params)
5454
]);
5555
}).then(([ authResult, payload, jwtOptions ]) => {
56+
if (authResult.accessToken) {
57+
return authResult;
58+
}
59+
5660
debug('Creating JWT with', payload, jwtOptions);
5761

5862
return this.createJWT(payload, jwtOptions, params.secret)
@@ -79,7 +83,7 @@ module.exports = class AuthenticationService extends AuthenticationCore {
7983
setup (app, path) {
8084
// The setup method checks for valid settings and registers the
8185
// connection and event (login, logout) hooks
82-
const { secret, service, entity, entityId } = this.configuration;
86+
const { secret, service, entity, entityId, strategies } = this.configuration;
8387

8488
if (typeof secret !== 'string') {
8589
throw new Error(`A 'secret' must be provided in your authentication configuration`);
@@ -95,6 +99,10 @@ module.exports = class AuthenticationService extends AuthenticationCore {
9599
}
96100
}
97101

102+
if (strategies.length === 0) {
103+
throw new Error(`At least one valid authentication strategy required in '${this.configKey}.strategies'`);
104+
}
105+
98106
this.hooks({
99107
after: [ connection(), events() ]
100108
});

packages/authentication/test/core.test.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,25 @@ describe('authentication/core', () => {
3939
assert.strictEqual(auth.configuration.entity, 'user');
4040
});
4141

42+
it('allows to override jwtOptions, does not merge', () => {
43+
const { jwtOptions } = auth.configuration;
44+
const auth2options = {
45+
jwtOptions: {
46+
expiresIn: '1w'
47+
}
48+
};
49+
50+
app.set('auth2', auth2options);
51+
52+
const auth2 = new AuthenticationCore(app, 'auth2');
53+
54+
assert.ok(jwtOptions);
55+
assert.strictEqual(jwtOptions.expiresIn, '1d');
56+
assert.strictEqual(jwtOptions.issuer, 'feathers');
57+
58+
assert.deepStrictEqual(auth2.configuration.jwtOptions, auth2options.jwtOptions);
59+
});
60+
4261
it('sets configKey and defaultAuthentication', () => {
4362
assert.strictEqual(app.get('defaultAuthentication'), 'authentication');
4463
});

packages/authentication/test/hooks/authenticate.test.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,12 @@ describe('authentication/hooks/authenticate', () => {
1111
beforeEach(() => {
1212
app = feathers();
1313
app.use('/authentication', new AuthenticationService(app, 'authentication', {
14-
secret: 'supersecret'
14+
secret: 'supersecret',
15+
strategies: [ 'first' ]
1516
}));
1617
app.use('/auth-v2', new AuthenticationService(app, 'auth-v2', {
17-
secret: 'supersecret'
18+
secret: 'supersecret',
19+
strategies: [ 'test' ]
1820
}));
1921
app.use('/users', {
2022
id: 'name',

packages/authentication/test/jwt.test.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,19 @@ describe('authentication/jwt', () => {
150150
assert.deepStrictEqual(authResult.authentication.payload, payload);
151151
});
152152
});
153+
154+
it('errors when trying to set invalid option', () => {
155+
app.get('authentication').otherJwt = {
156+
expiresIn: 'something'
157+
};
158+
159+
try {
160+
app.service('authentication').register('otherJwt', new JWTStrategy());
161+
assert.fail('Should never get here');
162+
} catch (error) {
163+
assert.strictEqual(error.message, `Invalid JwtStrategy option 'authentication.otherJwt.expiresIn'. Did you mean to set it in 'authentication.jwtOptions'?`);
164+
}
165+
});
153166
});
154167

155168
describe('parse', () => {

packages/authentication/test/options.test.js

Lines changed: 0 additions & 13 deletions
This file was deleted.

packages/authentication/test/service.test.js

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const jwt = require('jsonwebtoken');
44
const feathers = require('@feathersjs/feathers');
55
const memory = require('feathers-memory');
66

7-
const getOptions = require('../lib/options');
7+
const defaultOptions = require('../lib/options');
88
const AuthService = require('../lib/service');
99
const { Strategy1 } = require('./fixtures');
1010

@@ -27,7 +27,7 @@ describe('authentication/service', () => {
2727
});
2828

2929
it('settings returns authentication options', () => {
30-
assert.deepStrictEqual(service.configuration, getOptions(app.get('authentication')));
30+
assert.deepStrictEqual(service.configuration, Object.assign({}, defaultOptions, app.get('authentication')));
3131
});
3232

3333
describe('create', () => {
@@ -204,6 +204,17 @@ describe('authentication/service', () => {
204204
}
205205
});
206206

207+
it('errors when there is stragies', () => {
208+
app.get('authentication').strategies = [];
209+
210+
try {
211+
app.setup();
212+
assert.fail('Should never get here');
213+
} catch (error) {
214+
assert.strictEqual(error.message, `At least one valid authentication strategy required in 'authentication.strategies'`);
215+
}
216+
});
217+
207218
it('throws an error if entity service does not exist', () => {
208219
const app = feathers();
209220

0 commit comments

Comments
 (0)