diff --git a/packages/authentication-jwt/lib/index.js b/packages/authentication-jwt/lib/index.js index e0b1d7107f..913f8089a4 100644 --- a/packages/authentication-jwt/lib/index.js +++ b/packages/authentication-jwt/lib/index.js @@ -59,7 +59,8 @@ function init (options = {}) { let Verifier = DefaultVerifier; let strategyOptions = merge({ - secretOrKey: jwtSettings.secret, + secretOrKey: typeof jwtSettings.secret !== 'function' ? jwtSettings.secret : null, + secretOrKeyProvider: typeof jwtSettings.secret === 'function' ? jwtSettings.secret : null, jwtFromRequest: ExtractJwt.fromExtractors(extractors) }, jwtSettings.jwt, omit(jwtSettings, ['jwt', 'header', 'secret'])); diff --git a/packages/authentication-jwt/test/index.test.js b/packages/authentication-jwt/test/index.test.js index fe8c559740..d0c8b0f591 100644 --- a/packages/authentication-jwt/test/index.test.js +++ b/packages/authentication-jwt/test/index.test.js @@ -40,238 +40,433 @@ describe('@feathersjs/authentication-jwt', () => { expect(typeof ExtractJwt.fromExtractors).to.equal('function'); }); - describe('initialization', () => { - let app; - let validToken; - let Payload = { userId: 0 }; - - beforeEach(done => { - app = expressify(feathers()); - app.use('/users', memory()); - app.configure(authentication({ - secret: 'supersecret', - cookie: { - enabled: true, - name: 'feathers-jwt' - } - })); - - app.service('users').create({ - name: 'test user' + describe('secretOrKey', () => { + describe('initialization', () => { + let app; + let validToken; + let Payload = { userId: 0 }; + + beforeEach(done => { + app = expressify(feathers()); + app.use('/users', memory()); + app.configure(authentication({ + secret: 'supersecret', + cookie: { + enabled: true, + name: 'feathers-jwt' + } + })); + + app.service('users').create({ + name: 'test user' + }); + + JWT.sign(Payload, 'supersecret', app.get('authentication').jwt, (error, token) => { + if (error) { return done(error); } + validToken = token; + done(); + }); }); - JWT.sign(Payload, 'supersecret', app.get('authentication').jwt, (error, token) => { - if (error) { return done(error); } - validToken = token; - done(); + it('throws an error if passport has not been registered', () => { + expect(() => { + expressify(feathers()).configure(jwt()); + }).to.throw(); }); - }); - it('throws an error if passport has not been registered', () => { - expect(() => { - expressify(feathers()).configure(jwt()); - }).to.throw(); - }); + it('throws an error if header is not a string', () => { + expect(() => { + app.configure(jwt({ header: true })); + app.setup(); + }).to.throw(); + }); - it('throws an error if header is not a string', () => { - expect(() => { - app.configure(jwt({ header: true })); + it('registers the jwt passport strategy', () => { + sinon.spy(app.passport, 'use'); + sinon.spy(passportJWT, 'Strategy'); + app.configure(jwt()); app.setup(); - }).to.throw(); - }); - it('registers the jwt passport strategy', () => { - sinon.spy(app.passport, 'use'); - sinon.spy(passportJWT, 'Strategy'); - app.configure(jwt()); - app.setup(); + expect(passportJWT.Strategy).to.have.been.calledOnce; + expect(app.passport.use).to.have.been.calledWith('jwt'); + + app.passport.use.restore(); + passportJWT.Strategy.restore(); + }); + + it('registers the strategy options', () => { + sinon.spy(app.passport, 'options'); + app.configure(jwt()); + app.setup(); - expect(passportJWT.Strategy).to.have.been.calledOnce; - expect(app.passport.use).to.have.been.calledWith('jwt'); + expect(app.passport.options).to.have.been.calledOnce; - app.passport.use.restore(); - passportJWT.Strategy.restore(); - }); + app.passport.options.restore(); + }); - it('registers the strategy options', () => { - sinon.spy(app.passport, 'options'); - app.configure(jwt()); - app.setup(); + describe('passport strategy options', () => { + let authOptions; + let args; - expect(app.passport.options).to.have.been.calledOnce; + beforeEach(() => { + sinon.spy(passportJWT, 'Strategy'); + app.configure(jwt({ custom: true })); + app.setup(); + authOptions = app.get('authentication'); + args = passportJWT.Strategy.getCall(0).args[0]; + }); - app.passport.options.restore(); - }); + afterEach(() => { + passportJWT.Strategy.restore(); + }); - describe('passport strategy options', () => { - let authOptions; - let args; + it('sets secretOrKey', () => { + expect(args.secretOrKey).to.equal('supersecret'); + }); - beforeEach(() => { + it('sets jwtFromRequest', () => { + expect(args.jwtFromRequest).to.be.a('function'); + }); + + it('sets session', () => { + expect(args.session).to.equal(authOptions.session); + }); + + it('sets entity', () => { + expect(args.entity).to.equal(authOptions.entity); + }); + + it('sets service', () => { + expect(args.service).to.equal(authOptions.service); + }); + + it('sets passReqToCallback', () => { + expect(args.passReqToCallback).to.equal(authOptions.passReqToCallback); + }); + + it('sets algorithms', () => { + expect(args.algorithms).to.deep.equal([authOptions.jwt.algorithm]); + }); + + it('sets audience', () => { + expect(args.audience).to.equal(authOptions.jwt.audience); + }); + + it('sets expiresIn', () => { + expect(args.expiresIn).to.equal(authOptions.jwt.expiresIn); + }); + + it('sets issuer', () => { + expect(args.issuer).to.equal(authOptions.jwt.issuer); + }); + + it('sets subject', () => { + expect(args.subject).to.equal(authOptions.jwt.subject); + }); + + it('sets header', () => { + expect(args.header).to.deep.equal(authOptions.jwt.header); + }); + + it('supports setting custom options', () => { + expect(args.custom).to.equal(true); + }); + }); + + it('supports overriding default options', () => { sinon.spy(passportJWT, 'Strategy'); - app.configure(jwt({ custom: true })); + app.configure(jwt({ subject: 'custom' })); app.setup(); - authOptions = app.get('authentication'); - args = passportJWT.Strategy.getCall(0).args[0]; - }); - afterEach(() => { + expect(passportJWT.Strategy.getCall(0).args[0].subject).to.equal('custom'); + passportJWT.Strategy.restore(); }); - it('sets secretOrKey', () => { - expect(args.secretOrKey).to.equal('supersecret'); - }); + it('pulls options from global config with custom name', () => { + sinon.spy(passportJWT, 'Strategy'); + let authOptions = app.get('authentication'); + authOptions.custom = { entity: 'device' }; + app.set('authentication', authOptions); - it('sets jwtFromRequest', () => { - expect(args.jwtFromRequest).to.be.a('function'); - }); + app.configure(jwt({ name: 'custom' })); + app.setup(); - it('sets session', () => { - expect(args.session).to.equal(authOptions.session); - }); + expect(passportJWT.Strategy.getCall(0).args[0].entity).to.equal('device'); + expect(passportJWT.Strategy.getCall(0).args[0].bodyKey).to.equal('accessToken'); - it('sets entity', () => { - expect(args.entity).to.equal(authOptions.entity); + passportJWT.Strategy.restore(); }); - it('sets service', () => { - expect(args.service).to.equal(authOptions.service); - }); + describe('Bearer scheme', () => { + it('authenticates using the default verifier', () => { + const req = { + query: {}, + body: {}, + headers: { + authorization: `Bearer ${validToken}` + }, + cookies: {} + }; + + app.configure(jwt()); + app.setup(); - it('sets passReqToCallback', () => { - expect(args.passReqToCallback).to.equal(authOptions.passReqToCallback); + return app.authenticate('jwt')(req).then(result => { + expect(result.success).to.equal(true); + }); + }); }); - it('sets algorithms', () => { - expect(args.algorithms).to.deep.equal([authOptions.jwt.algorithm]); - }); + describe('Cookie', () => { + it('authenticates using a cookie if set in options', () => { + const req = { + query: {}, + body: {}, + headers: {}, + cookies: { + 'feathers-jwt': validToken + } + }; - it('sets audience', () => { - expect(args.audience).to.equal(authOptions.jwt.audience); - }); + app.configure(jwt()); + app.setup(); - it('sets expiresIn', () => { - expect(args.expiresIn).to.equal(authOptions.jwt.expiresIn); + return app.authenticate('jwt')(req).then(result => { + expect(result.success).to.equal(true); + }); + }); }); - it('sets issuer', () => { - expect(args.issuer).to.equal(authOptions.jwt.issuer); - }); + describe('custom Verifier', () => { + it('throws an error if a verify function is missing', () => { + expect(() => { + class CustomVerifier { + constructor (app) { + this.app = app; + } + } - it('sets subject', () => { - expect(args.subject).to.equal(authOptions.jwt.subject); - }); + app.configure(jwt({ Verifier: CustomVerifier })); + app.setup(); + }).to.throw(); + }); - it('sets header', () => { - expect(args.header).to.deep.equal(authOptions.jwt.header); - }); + it('verifies through custom verify function', () => { + const req = { + query: {}, + body: {}, + headers: { + authorization: `${validToken}` + }, + cookies: {} + }; + class CustomVerifier extends Verifier { + verify (req, payload, done) { + expect(payload.userId).to.equal(Payload.userId); + done(null, payload, Payload); + } + } + + app.configure(jwt({ Verifier: CustomVerifier })); + app.setup(); - it('supports setting custom options', () => { - expect(args.custom).to.equal(true); + return app.authenticate('jwt')(req).then(result => { + expect(result.data.payload.userId).to.deep.equal(Payload.userId); + }); + }); }); }); + }); - it('supports overriding default options', () => { - sinon.spy(passportJWT, 'Strategy'); - app.configure(jwt({ subject: 'custom' })); - app.setup(); + describe('secretOrKeyProvider', () => { + describe('initialization', () => { + let app; + let validToken; + let Payload = { userId: 0 }; + + beforeEach(done => { + app = expressify(feathers()); + app.use('/users', memory()); + app.configure(authentication({ + secret: sinon.spy(function(request, token, done) { + done(null, 'supersecret'); + }), + cookie: { + enabled: true, + name: 'feathers-jwt' + } + })); - expect(passportJWT.Strategy.getCall(0).args[0].subject).to.equal('custom'); + app.service('users').create({ + name: 'test user' + }); - passportJWT.Strategy.restore(); - }); + JWT.sign(Payload, 'supersecret', app.get('authentication').jwt, (error, token) => { + if (error) { return done(error); } + validToken = token; + done(); + }); + }); - it('pulls options from global config with custom name', () => { - sinon.spy(passportJWT, 'Strategy'); - let authOptions = app.get('authentication'); - authOptions.custom = { entity: 'device' }; - app.set('authentication', authOptions); + it('throws an error if passport has not been registered', () => { + expect(() => { + expressify(feathers()).configure(jwt()); + }).to.throw(); + }); - app.configure(jwt({ name: 'custom' })); - app.setup(); + it('throws an error if header is not a string', () => { + expect(() => { + app.configure(jwt({ header: true })); + app.setup(); + }).to.throw(); + }); - expect(passportJWT.Strategy.getCall(0).args[0].entity).to.equal('device'); - expect(passportJWT.Strategy.getCall(0).args[0].bodyKey).to.equal('accessToken'); + it('registers the jwt passport strategy', () => { + sinon.spy(app.passport, 'use'); + sinon.spy(passportJWT, 'Strategy'); + app.configure(jwt()); + app.setup(); - passportJWT.Strategy.restore(); - }); + expect(passportJWT.Strategy).to.have.been.calledOnce; + expect(app.passport.use).to.have.been.calledWith('jwt'); - describe('Bearer scheme', () => { - it('authenticates using the default verifier', () => { - const req = { - query: {}, - body: {}, - headers: { - authorization: `Bearer ${validToken}` - }, - cookies: {} - }; + app.passport.use.restore(); + passportJWT.Strategy.restore(); + }); + it('registers the strategy options', () => { + sinon.spy(app.passport, 'options'); app.configure(jwt()); app.setup(); - return app.authenticate('jwt')(req).then(result => { - expect(result.success).to.equal(true); + expect(app.passport.options).to.have.been.calledOnce; + + app.passport.options.restore(); + }); + + describe('passport strategy options', () => { + let authOptions; + let args; + + beforeEach(() => { + sinon.spy(passportJWT, 'Strategy'); + app.configure(jwt({ custom: true })); + app.setup(); + authOptions = app.get('authentication'); + args = passportJWT.Strategy.getCall(0).args[0]; + }); + + afterEach(() => { + passportJWT.Strategy.restore(); + }); + + it('sets secretOrKeyProvider', () => { + expect(args.secretOrKeyProvider).to.be.a('function'); }); }); - }); - describe('Cookie', () => { - it('authenticates using a cookie if set in options', () => { - const req = { - query: {}, - body: {}, - headers: {}, - cookies: { - 'feathers-jwt': validToken - } - }; + it('supports overriding default options', () => { + sinon.spy(passportJWT, 'Strategy'); + app.configure(jwt({ subject: 'custom' })); + app.setup(); - app.configure(jwt()); + expect(passportJWT.Strategy.getCall(0).args[0].subject).to.equal('custom'); + + passportJWT.Strategy.restore(); + }); + + it('pulls options from global config with custom name', () => { + sinon.spy(passportJWT, 'Strategy'); + let authOptions = app.get('authentication'); + authOptions.custom = { entity: 'device' }; + app.set('authentication', authOptions); + + app.configure(jwt({ name: 'custom' })); app.setup(); - return app.authenticate('jwt')(req).then(result => { - expect(result.success).to.equal(true); + expect(passportJWT.Strategy.getCall(0).args[0].entity).to.equal('device'); + expect(passportJWT.Strategy.getCall(0).args[0].bodyKey).to.equal('accessToken'); + + passportJWT.Strategy.restore(); + }); + + describe('Bearer scheme', () => { + it('authenticates using the default verifier', () => { + const req = { + query: {}, + body: {}, + headers: { + authorization: `Bearer ${validToken}` + }, + cookies: {} + }; + + app.configure(jwt()); + app.setup(); + + return app.authenticate('jwt')(req).then(result => { + expect(result.success).to.equal(true); + }); }); }); - }); - describe('custom Verifier', () => { - it('throws an error if a verify function is missing', () => { - expect(() => { - class CustomVerifier { - constructor (app) { - this.app = app; + describe('Cookie', () => { + it('authenticates using a cookie if set in options', () => { + const req = { + query: {}, + body: {}, + headers: {}, + cookies: { + 'feathers-jwt': validToken } - } + }; - app.configure(jwt({ Verifier: CustomVerifier })); + app.configure(jwt()); app.setup(); - }).to.throw(); + + return app.authenticate('jwt')(req).then(result => { + expect(result.success).to.equal(true); + }); + }); }); - it('verifies through custom verify function', () => { - const req = { - query: {}, - body: {}, - headers: { - authorization: `${validToken}` - }, - cookies: {} - }; - class CustomVerifier extends Verifier { - verify (req, payload, done) { - expect(payload.userId).to.equal(Payload.userId); - done(null, payload, Payload); + describe('custom Verifier', () => { + it('throws an error if a verify function is missing', () => { + expect(() => { + class CustomVerifier { + constructor (app) { + this.app = app; + } + } + + app.configure(jwt({ Verifier: CustomVerifier })); + app.setup(); + }).to.throw(); + }); + + it('verifies through custom verify function', () => { + const req = { + query: {}, + body: {}, + headers: { + authorization: `${validToken}` + }, + cookies: {} + }; + class CustomVerifier extends Verifier { + verify (req, payload, done) { + expect(payload.userId).to.equal(Payload.userId); + done(null, payload, Payload); + } } - } - app.configure(jwt({ Verifier: CustomVerifier })); - app.setup(); + app.configure(jwt({ Verifier: CustomVerifier })); + app.setup(); - return app.authenticate('jwt')(req).then(result => { - expect(result.data.payload.userId).to.deep.equal(Payload.userId); + return app.authenticate('jwt')(req).then(result => { + expect(result.data.payload.userId).to.deep.equal(Payload.userId); + }); }); }); });