diff --git a/plugins/auth-backend/src/providers/google/provider.test.ts b/plugins/auth-backend/src/providers/google/provider.test.ts index b692a35216..874b89d92c 100644 --- a/plugins/auth-backend/src/providers/google/provider.test.ts +++ b/plugins/auth-backend/src/providers/google/provider.test.ts @@ -52,11 +52,15 @@ describe('GoogleAuthProvider', () => { }); describe('start authentication handler', () => { - const mockResponse = ({} as unknown) as express.Response; + const mockResponse = ({ + send: jest.fn().mockReturnThis(), + status: jest.fn().mockReturnThis(), + } as unknown) as express.Response; const mockNext: express.NextFunction = jest.fn(); it('should initiate authenticate request with provided scopes', () => { const mockRequest = ({ + header: () => 'XMLHttpRequest', query: { scope: 'a,b', }, @@ -80,6 +84,7 @@ describe('GoogleAuthProvider', () => { it('should throw error if no scopes provided', () => { const mockRequest = ({ + header: () => 'XMLHttpRequest', query: {}, } as unknown) as express.Request; @@ -93,7 +98,9 @@ describe('GoogleAuthProvider', () => { }); describe('logout handler', () => { - const mockRequest = ({} as unknown) as express.Request; + const mockRequest = ({ + header: () => 'XMLHttpRequest', + } as unknown) as express.Request; it('should perform logout and respond with 200', () => { const mockResponse: any = ({ @@ -122,7 +129,9 @@ describe('GoogleAuthProvider', () => { }); describe('redirect frame handler', () => { - const mockRequest = ({} as unknown) as express.Request; + const mockRequest = ({ + header: () => 'XMLHttpRequest', + } as unknown) as express.Request; const mockResponse: any = ({ status: jest.fn().mockReturnThis(), send: jest.fn().mockReturnThis(), @@ -245,6 +254,7 @@ describe('GoogleAuthProvider', () => { it('should respond with a 401', () => { const mockRequest = ({ cookies: jest.fn(), + header: () => 'XMLHttpRequest', } as unknown) as express.Request; const googleAuthProvider = new GoogleAuthProvider( @@ -262,6 +272,7 @@ describe('GoogleAuthProvider', () => { describe('refresh token cookie, no scope', () => { const mockRequest = ({ + header: () => 'XMLHttpRequest', cookies: { 'google-refresh-token': 'REFRESH_TOKEN' }, query: {}, } as unknown) as express.Request; @@ -349,6 +360,7 @@ describe('GoogleAuthProvider', () => { describe('refresh token cookie and scope', () => { const mockRequest = ({ + header: () => 'XMLHttpRequest', cookies: { 'google-refresh-token': 'REFRESH_TOKEN' }, query: { scope: 'a,b', @@ -387,6 +399,20 @@ describe('GoogleAuthProvider', () => { scope: 'a,b', }); }); + + it('ensures x-requested-with header', () => { + const mockHeaderRequest = ({ + header: () => 'TEST', + } as unknown) as express.Request; + + googleAuthProvider.refresh(mockHeaderRequest, mockResponse); + expect(mockResponse.send).toBeCalledTimes(1); + expect(mockResponse.send).toBeCalledWith( + 'Invalid X-Requested-With header', + ); + expect(mockResponse.status).toBeCalledTimes(1); + expect(mockResponse.status).toBeCalledWith(401); + }); }); }); }); diff --git a/plugins/auth-backend/src/providers/google/provider.ts b/plugins/auth-backend/src/providers/google/provider.ts index 096260e044..35c7b3f6e2 100644 --- a/plugins/auth-backend/src/providers/google/provider.ts +++ b/plugins/auth-backend/src/providers/google/provider.ts @@ -23,7 +23,7 @@ import { AuthProviderRouteHandlers, AuthProviderConfig, } from './../types'; -import { postMessageResponse } from './../utils'; +import { postMessageResponse, ensuresXRequestedWith } from './../utils'; import { InputError } from '@backstage/backend-common'; export const THOUSAND_DAYS_MS = 1000 * 24 * 60 * 60 * 1000; @@ -95,7 +95,11 @@ export class GoogleAuthProvider })(req, res, next); } - async logout(_req: express.Request, res: express.Response) { + async logout(req: express.Request, res: express.Response) { + if (!ensuresXRequestedWith(req)) { + return res.status(401).send('Invalid X-Requested-With header'); + } + const options: CookieOptions = { maxAge: 0, secure: false, @@ -110,6 +114,10 @@ export class GoogleAuthProvider } async refresh(req: express.Request, res: express.Response) { + if (!ensuresXRequestedWith(req)) { + return res.status(401).send('Invalid X-Requested-With header'); + } + const refreshToken = req.cookies[`${this.providerConfig.provider}-refresh-token`]; diff --git a/plugins/auth-backend/src/providers/utils.ts b/plugins/auth-backend/src/providers/utils.ts index 7fb906ad7c..83229e55d4 100644 --- a/plugins/auth-backend/src/providers/utils.ts +++ b/plugins/auth-backend/src/providers/utils.ts @@ -39,3 +39,12 @@ export const postMessageResponse = ( `); }; + +export const ensuresXRequestedWith = (req: express.Request) => { + const requiredHeader = req.header('X-Requested-With'); + + if (!requiredHeader || requiredHeader !== 'XMLHttpRequest') { + return false; + } + return true; +};