From e41f4b966c2a1b77ff8d5c695f16d5cc191cb9a2 Mon Sep 17 00:00:00 2001 From: Silthus <755327+Silthus@users.noreply.github.com> Date: Sat, 17 Feb 2024 19:05:25 +0100 Subject: [PATCH] fix(auth): do not force validation of jwt for requests without auth header (#392) --- src/__tests__/areas.ts | 26 +++++++++++++------------- src/auth/middleware.ts | 42 +++++++++++++++++++++++++----------------- src/utils/testUtils.ts | 9 +++++++-- 3 files changed, 45 insertions(+), 32 deletions(-) diff --git a/src/__tests__/areas.ts b/src/__tests__/areas.ts index 995b3cc..8047815 100644 --- a/src/__tests__/areas.ts +++ b/src/__tests__/areas.ts @@ -75,36 +75,36 @@ describe('areas API', () => { }) }) - it('retrieves an area and lists associated organizations', async () => { + it('retrieves an area omitting organizations that exclude it', async () => { const response = await queryAPI({ query: areaQuery, operationName: 'area', - variables: { input: wa.metadata.area_id }, + variables: { input: ca.metadata.area_id }, userUuid, app }) - expect(response.statusCode).toBe(200) const areaResult = response.body.data.area - expect(areaResult.uuid).toBe(muuidToString(wa.metadata.area_id)) - expect(areaResult.organizations).toHaveLength(1) - expect(areaResult.organizations[0].orgId).toBe(muuidToString(alphaOrg.orgId)) + expect(areaResult.uuid).toBe(muuidToString(ca.metadata.area_id)) + // Even though alphaOrg associates with ca's parent, usa, it excludes + // ca and so should not be listed. + expect(areaResult.organizations).toHaveLength(0) }) - it('retrieves an area omitting organizations that exclude it', async () => { + it.each([userUuid, undefined])('retrieves an area and lists associated organizations', async (userId) => { const response = await queryAPI({ query: areaQuery, operationName: 'area', - variables: { input: ca.metadata.area_id }, - userUuid, + variables: { input: wa.metadata.area_id }, + userUuid: userId, app }) + expect(response.statusCode).toBe(200) const areaResult = response.body.data.area - expect(areaResult.uuid).toBe(muuidToString(ca.metadata.area_id)) - // Even though alphaOrg associates with ca's parent, usa, it excludes - // ca and so should not be listed. - expect(areaResult.organizations).toHaveLength(0) + expect(areaResult.uuid).toBe(muuidToString(wa.metadata.area_id)) + expect(areaResult.organizations).toHaveLength(1) + expect(areaResult.organizations[0].orgId).toBe(muuidToString(alphaOrg.orgId)) }) }) }) diff --git a/src/auth/middleware.ts b/src/auth/middleware.ts index 365ca29..4b6516e 100644 --- a/src/auth/middleware.ts +++ b/src/auth/middleware.ts @@ -3,6 +3,12 @@ import { AuthUserType } from '../types.js' import { verifyJWT } from './util.js' import { logger } from '../logger.js' +const EMTPY_USER: AuthUserType = { + isBuilder: false, + roles: [], + uuid: undefined +} + /** * Create a middleware context for Apollo server */ @@ -15,27 +21,29 @@ export const createContext = async ({ req }): Promise => { } } -async function validateTokenAndExtractUser (req: Request): Promise<{ user: AuthUserType, token: string }> { +async function validateTokenAndExtractUser (req: Request): Promise<{ user: AuthUserType, token?: string }> { const { headers } = req // eslint-disable-next-line @typescript-eslint/dot-notation const authHeader = String(headers?.['authorization'] ?? '') - if (!authHeader.startsWith('Bearer ')) { - throw new Error('Unauthorized. Please provide a valid JWT token in the Authorization header.') + if (authHeader.startsWith('Bearer ')) { + const token = authHeader.substring(7, authHeader.length).trim() + try { + const payload = await verifyJWT(token) + return { + user: { + isBuilder: payload?.scope?.includes('builder:default') ?? false, + roles: payload?.['https://tacos.openbeta.io/roles'] ?? [], + uuid: payload?.['https://tacos.openbeta.io/uuid'] != null ? muid.from(payload['https://tacos.openbeta.io/uuid']) : undefined + }, + token + } + } catch (e) { + logger.error(`Can't verify JWT token ${e.toString() as string}`) + throw new Error("Unauthorized. Can't verify JWT token") + } } - const token = authHeader.substring(7, authHeader.length).trim() - try { - const payload = await verifyJWT(token) - return { - user: { - isBuilder: payload?.scope?.includes('builder:default') ?? false, - roles: payload?.['https://tacos.openbeta.io/roles'] ?? [], - uuid: payload?.['https://tacos.openbeta.io/uuid'] != null ? muid.from(payload['https://tacos.openbeta.io/uuid']) : undefined - }, - token - } - } catch (e) { - logger.error(`Can't verify JWT token ${e.toString() as string}`) - throw new Error("Unauthorized. Can't verify JWT token") + return { + user: EMTPY_USER } } diff --git a/src/utils/testUtils.ts b/src/utils/testUtils.ts index f16a2a7..ed13ce2 100644 --- a/src/utils/testUtils.ts +++ b/src/utils/testUtils.ts @@ -46,10 +46,15 @@ export const queryAPI = async ({ }) const queryObj = { query, operationName, variables } - return await request(app ?? `http://localhost:${port}`) + let req = request(app ?? `http://localhost:${port}`) .post(endpoint) .send(queryObj) - .set('Authorization', 'Bearer placeholder-jwt-see-SpyOn') + + if (userUuid != null) { + req = req.set('Authorization', 'Bearer placeholder-jwt-see-SpyOn') + } + + return await req } export interface SetUpServerReturnType {