fix tests and add more tests

This commit is contained in:
Raghunandan
2020-05-25 23:44:20 +02:00
parent a8184250fe
commit 1da6ac369e
2 changed files with 207 additions and 10 deletions
@@ -14,10 +14,11 @@
* limitations under the License.
*/
import { GoogleAuthProvider } from './provider';
import { GoogleAuthProvider, THOUSAND_DAYS_MS } from './provider';
import passport from 'passport';
import express from 'express';
import * as utils from './../utils';
import refresh from 'passport-oauth2-refresh';
const googleAuthProviderConfig = {
provider: 'google',
@@ -51,7 +52,7 @@ describe('GoogleAuthProvider', () => {
});
describe('start authentication handler', () => {
const mockResponse: any = ({} as unknown) as express.Response;
const mockResponse = ({} as unknown) as express.Response;
const mockNext: express.NextFunction = jest.fn();
it('should initiate authenticate request with provided scopes', () => {
@@ -116,28 +117,64 @@ describe('GoogleAuthProvider', () => {
describe('redirect frame handler', () => {
const mockRequest = ({} as unknown) as express.Request;
const mockResponse: any = ({
send: jest.fn(),
status: jest.fn().mockReturnThis(),
send: jest.fn().mockReturnThis(),
cookie: jest.fn().mockReturnThis(),
} as unknown) as express.Response;
const mockNext: express.NextFunction = jest.fn();
it('should call authenticate and post a response ', () => {
it('should call authenticate and post a response', () => {
const spyPostMessage = jest
.spyOn(utils, 'postMessageResponse')
.mockImplementation(() => jest.fn());
const spyPassport = jest
.spyOn(passport, 'authenticate')
.mockImplementation(() => jest.fn());
.mockImplementation((_x, callbackFunc) => {
const cb = callbackFunc as Function;
cb(null, { refreshToken: 'REFRESH_TOKEN' });
return jest.fn();
});
const googleAuthProvider = new GoogleAuthProvider(
googleAuthProviderConfig,
);
googleAuthProvider.frameHandler(mockRequest, mockResponse, mockNext);
const callbackFunc = spyPassport.mock.calls[0][1] as Function;
callbackFunc();
expect(spyPassport).toBeCalledTimes(1);
expect(spyPostMessage).toBeCalledTimes(1);
expect(mockResponse.cookie).toBeCalledTimes(1);
expect(mockResponse.cookie).toBeCalledWith(
'grtoken',
'REFRESH_TOKEN',
expect.objectContaining({
path: '/auth/google',
sameSite: 'none',
httpOnly: true,
maxAge: THOUSAND_DAYS_MS,
}),
);
});
it('should respond with a 401 if no refresh token returned', () => {
const spyPassport = jest
.spyOn(passport, 'authenticate')
.mockImplementation((_x, callbackFunc) => {
const cb = callbackFunc as Function;
cb(null, {});
return jest.fn();
});
const googleAuthProvider = new GoogleAuthProvider(
googleAuthProviderConfig,
);
googleAuthProvider.frameHandler(mockRequest, mockResponse, mockNext);
expect(spyPassport).toBeCalledTimes(1);
expect(mockResponse.send).toBeCalledTimes(1);
expect(mockResponse.send).toBeCalledWith('Failed to fetch refresh token');
expect(mockResponse.status).toBeCalledTimes(1);
expect(mockResponse.status).toBeCalledWith(401);
});
});
@@ -159,4 +196,159 @@ describe('GoogleAuthProvider', () => {
}).toThrow();
});
});
describe('refresh token handler', () => {
const mockResponse = ({
status: jest.fn().mockReturnThis(),
send: jest.fn().mockReturnThis(),
} as unknown) as express.Response;
describe('no refresh token cookie', () => {
it('should respond with a 401', () => {
const mockRequest = ({
cookies: jest.fn(),
} as unknown) as express.Request;
const googleAuthProvider = new GoogleAuthProvider(
googleAuthProviderConfig,
);
googleAuthProvider.refresh(mockRequest, mockResponse);
expect(mockResponse.send).toBeCalledTimes(1);
expect(mockResponse.send).toBeCalledWith('Missing session cookie');
expect(mockResponse.status).toBeCalledTimes(1);
expect(mockResponse.status).toBeCalledWith(401);
});
});
describe('refresh token cookie, no scope', () => {
const mockRequest = ({
cookies: { grtoken: 'REFRESH_TOKEN' },
query: {},
} as unknown) as express.Request;
const googleAuthProvider = new GoogleAuthProvider(
googleAuthProviderConfig,
);
it('should request for a new access token and fail if no access token returned', () => {
const spyRefresh = jest
.spyOn(refresh, 'requestNewAccessToken')
.mockImplementation((_x, _y, _z, callbackFunc) => {
const cb = callbackFunc as Function;
cb(undefined, undefined, undefined, {});
});
googleAuthProvider.refresh(mockRequest, mockResponse);
expect(spyRefresh).toBeCalledTimes(1);
expect(spyRefresh).toBeCalledWith(
'google',
'REFRESH_TOKEN',
{},
expect.any(Function),
);
expect(mockResponse.status).toBeCalledTimes(1);
expect(mockResponse.status).toBeCalledWith(401);
expect(mockResponse.send).toBeCalledTimes(1);
expect(mockResponse.send).toBeCalledWith(
'Failed to refresh access token',
);
});
it('should request for a new access token and return 401 if any error', () => {
const spyRefresh = jest
.spyOn(refresh, 'requestNewAccessToken')
.mockImplementation((_x, _y, _z, callbackFunc) => {
const cb = callbackFunc as Function;
cb({ error: 'ERROR' }, undefined, undefined, {});
});
googleAuthProvider.refresh(mockRequest, mockResponse);
expect(spyRefresh).toBeCalledTimes(1);
expect(spyRefresh).toBeCalledWith(
'google',
'REFRESH_TOKEN',
{},
expect.any(Function),
);
expect(mockResponse.status).toBeCalledTimes(1);
expect(mockResponse.status).toBeCalledWith(401);
expect(mockResponse.send).toBeCalledTimes(1);
expect(mockResponse.send).toBeCalledWith(
'Failed to refresh access token',
);
});
it('should fetch and return a new access token', () => {
const spyRefresh = jest
.spyOn(refresh, 'requestNewAccessToken')
.mockImplementation((_x, _y, _z, callbackFunc) => {
const cb = callbackFunc as Function;
cb(undefined, 'ACCESS_TOKEN', undefined, {
expires_in: 'EXPIRES_IN',
id_token: 'ID_TOKEN',
});
});
googleAuthProvider.refresh(mockRequest, mockResponse);
expect(spyRefresh).toBeCalledTimes(1);
expect(spyRefresh).toBeCalledWith(
'google',
'REFRESH_TOKEN',
{},
expect.any(Function),
);
expect(mockResponse.send).toBeCalledTimes(1);
expect(mockResponse.send).toBeCalledWith({
accessToken: 'ACCESS_TOKEN',
idToken: 'ID_TOKEN',
expiresInSeconds: 'EXPIRES_IN',
scope: undefined,
});
});
});
describe('refresh token cookie and scope', () => {
const mockRequest = ({
cookies: { grtoken: 'REFRESH_TOKEN' },
query: {
scope: 'a,b',
},
} as unknown) as express.Request;
const googleAuthProvider = new GoogleAuthProvider(
googleAuthProviderConfig,
);
it('should fetch and return a new access token with scopes', () => {
const spyRefresh = jest
.spyOn(refresh, 'requestNewAccessToken')
.mockImplementation((_x, _y, _z, callbackFunc) => {
const cb = callbackFunc as Function;
cb(undefined, 'ACCESS_TOKEN', undefined, {
expires_in: 'EXPIRES_IN',
id_token: 'ID_TOKEN',
scope: 'a,b',
});
});
googleAuthProvider.refresh(mockRequest, mockResponse);
expect(spyRefresh).toBeCalledTimes(1);
expect(spyRefresh).toBeCalledWith(
'google',
'REFRESH_TOKEN',
{ scope: 'a,b' },
expect.any(Function),
);
expect(mockResponse.send).toBeCalledTimes(1);
expect(mockResponse.send).toBeCalledWith({
accessToken: 'ACCESS_TOKEN',
idToken: 'ID_TOKEN',
expiresInSeconds: 'EXPIRES_IN',
scope: 'a,b',
});
});
});
});
});
@@ -26,7 +26,7 @@ import {
import { postMessageResponse } from './../utils';
import { InputError } from '@backstage/backend-common';
const THOUSAND_DAYS_MS = 1000 * 24 * 60 * 60 * 1000;
export const THOUSAND_DAYS_MS = 1000 * 24 * 60 * 60 * 1000;
export class GoogleAuthProvider
implements AuthProvider, AuthProviderRouteHandlers {
private readonly providerConfig: AuthProviderConfig;
@@ -57,6 +57,11 @@ export class GoogleAuthProvider
) {
return passport.authenticate('google', (_, user) => {
const { refreshToken } = user;
if (!refreshToken) {
return res.status(401).send('Failed to fetch refresh token');
}
delete user.refreshToken;
const options: CookieOptions = {
@@ -69,7 +74,7 @@ export class GoogleAuthProvider
};
res.cookie('grtoken', refreshToken, options);
postMessageResponse(res, {
return postMessageResponse(res, {
type: 'auth-result',
payload: user,
});
@@ -94,7 +99,7 @@ export class GoogleAuthProvider
'google',
refreshToken,
refreshTokenRequestParams,
(err, accessToken, _, params) => {
(err, accessToken, _refreshToken, params) => {
if (err || !accessToken) {
return res.status(401).send('Failed to refresh access token');
}