UNPKG

passport-oauth2-refresh

Version:

A passport.js add-on to provide automatic OAuth 2.0 token refreshing.

305 lines (251 loc) 8.72 kB
'use strict'; require('mocha'); const chai = require('chai'); const sinon = require('sinon'); const expect = chai.expect; const refresh = require('../lib/refresh.js'); // Constructor, for additional distinct instances const AuthTokenRefresh = refresh.AuthTokenRefresh; chai.use(require('sinon-chai')); // Dummy OAuth2 object function OAuth2( clientId, clientSecret, baseSite, authorizeUrl, accessTokenUrl, ) { this._accessTokenUrl = accessTokenUrl; } // Add dummy method OAuth2.prototype.getOAuthAccessToken = new Function(); // Makes it easy to invocate in the specs const newOAuth2 = function (accessTokenUrl) { return new OAuth2(null, null, null, null, accessTokenUrl); }; describe('Auth token refresh', function () { beforeEach(function () { refresh._strategies = {}; }); describe('use', function () { it('should add a strategy with an explicitly defined name', function () { const strategy = { name: 'internal_name', _oauth2: newOAuth2(), }; refresh.use('explicit_name', strategy); expect(refresh._strategies.explicit_name.strategy).to.equal(strategy); expect(refresh._strategies.strategy).to.be.undefined; }); it('should add a strategy without an explicitly defined name', function () { const strategy = { name: 'internal_name', _oauth2: newOAuth2(), }; refresh.use(strategy); expect(refresh._strategies.internal_name.strategy).to.equal(strategy); }); it('should add a strategy with a refreshURL', function () { const strategy = { name: 'test_strategy', _refreshURL: 'refreshURL', _oauth2: newOAuth2('accessTokenUrl'), }; refresh.use(strategy); expect(refresh._strategies.test_strategy.strategy).to.equal(strategy); expect( refresh._strategies.test_strategy.refreshOAuth2._accessTokenUrl, ).to.equal('refreshURL'); }); it('should add a strategy without a refreshURL', function () { const strategy = { name: 'test_strategy', _oauth2: newOAuth2('accessTokenUrl'), }; refresh.use(strategy); expect(refresh._strategies.test_strategy.strategy).to.equal(strategy); expect( refresh._strategies.test_strategy.refreshOAuth2._accessTokenUrl, ).to.equal('accessTokenUrl'); }); it("should create a new oauth2 object with the same prototype as the strategy's _oauth2 object", function () { const strategyOAuth2 = newOAuth2(); const strategy = { name: 'test_strategy', _oauth2: strategyOAuth2, }; refresh.use(strategy); expect(refresh._strategies.test_strategy.refreshOAuth2).to.not.equal( strategyOAuth2, ); expect(refresh._strategies.test_strategy.refreshOAuth2).to.be.instanceof( OAuth2, ); }); it('should set the oauth2 adapter with the options object', function () { const strategyOAuth2 = newOAuth2(); const customOAuth2 = newOAuth2(); const strategy = { name: 'test_strategy', _oauth2: strategyOAuth2, }; const setRefreshOAuth2 = sinon.fake.returns(customOAuth2); refresh.use(strategy, { setRefreshOAuth2, }); expect(setRefreshOAuth2).to.have.been.calledWith({ strategyOAuth2, refreshOAuth2: sinon.match.instanceOf(OAuth2), }); expect(refresh._strategies.test_strategy.refreshOAuth2).to.equal( customOAuth2, ); }); it('should throw if the strategy does not supply an oauth2 instance and the setRefreshOAuth2 function is not specified', function () { const strategy = { name: 'test_strategy', }; const fn = function () { refresh.use(strategy); }; expect(fn).to.throw( Error, 'The OAuth2 adapter used to refresh the token is not configured correctly. Use the setRefreshOAuth2 option to return a OAuth 2.0 adapter.', ); }); it('should throw if the strategy does not supply an oauth2 instance and the setRefreshOAuth2 function does not return an oauth2 adapter', function () { const strategy = { name: 'test_strategy', }; const modifyOAuth2 = sinon.fake.returns(undefined); const fn = function () { refresh.use(strategy, { modifyOAuth2, }); }; expect(fn).to.throw( Error, 'The OAuth2 adapter used to refresh the token is not configured correctly. Use the setRefreshOAuth2 option to return a OAuth 2.0 adapter.', ); }); it('should not add a null strategy', function () { const strategy = null; const fn = function () { refresh.use(strategy); }; expect(fn).to.throw(Error, 'Cannot register: strategy is null'); }); it('should not add a strategy with no name', function () { const strategy = { name: '', _oauth2: newOAuth2(), }; const fn = function () { refresh.use(strategy); }; expect(fn).to.throw( Error, 'Cannot register: name must be specified, or strategy must include name', ); }); it('should use the default getOAuthAccessToken function if not overwritten by strategy', function () { const strategy = { name: 'test_strategy', _oauth2: newOAuth2(), }; refresh.use(strategy); expect( refresh._strategies.test_strategy.refreshOAuth2.getOAuthAccessToken, ).to.equal(OAuth2.prototype.getOAuthAccessToken); }); it('should use the overwritten getOAuthAccessToken function if overwritten by strategy', function () { const strategy = { name: 'test_strategy', _oauth2: newOAuth2(), }; strategy._oauth2.getOAuthAccessToken = new Function(); refresh.use(strategy); expect( refresh._strategies.test_strategy.refreshOAuth2.getOAuthAccessToken, ).to.equal(strategy._oauth2.getOAuthAccessToken); expect( refresh._strategies.test_strategy.refreshOAuth2.getOAuthAccessToken, ).not.equal(OAuth2.prototype.getOAuthAccessToken); }); }); describe('has', function () { it('should return true if a strategy has been added', function () { const strategy = { name: 'test_strategy', _oauth2: newOAuth2(), }; refresh.use(strategy); expect(refresh.has('test_strategy')).to.be.true; }); it('should return false if a strategy has not been added', function () { expect(refresh.has('test_strategy')).to.be.false; }); }); describe('request new access token', function () { it('should refresh an access token', function () { const getOAuthAccessTokenSpy = sinon.spy(); const done = sinon.spy(); refresh._strategies = { test_strategy: { refreshOAuth2: { getOAuthAccessToken: getOAuthAccessTokenSpy, }, }, }; refresh.requestNewAccessToken('test_strategy', 'refresh_token', done); expect(getOAuthAccessTokenSpy).to.have.been.calledWith( 'refresh_token', { grant_type: 'refresh_token' }, done, ); }); it('should refresh a new access token with extra params', function () { const getOAuthAccessTokenSpy = sinon.spy(); const done = sinon.spy(); refresh._strategies = { test_strategy: { refreshOAuth2: { getOAuthAccessToken: getOAuthAccessTokenSpy, }, }, }; refresh.requestNewAccessToken( 'test_strategy', 'refresh_token', { some: 'extra_param' }, done, ); expect(getOAuthAccessTokenSpy).to.have.been.calledWith( 'refresh_token', { grant_type: 'refresh_token', some: 'extra_param' }, done, ); }); it('should not refresh if the strategy was not previously registered', function () { const done = sinon.spy(); const expected = sinon.match .instanceOf(Error) .and( sinon.match.has( 'message', 'Strategy was not registered to refresh a token', ), ); refresh.requestNewAccessToken('test_strategy', 'refresh_token', done); expect(done).to.have.been.calledWith(expected); }); }); describe('multiple instances', function () { it('should support creating a second, independent instance', function () { const refresh2 = new AuthTokenRefresh(); expect(refresh).to.be.instanceof(AuthTokenRefresh); expect(refresh2).to.be.instanceof(AuthTokenRefresh); expect(refresh).to.not.equal(refresh2); }); }); });