From 033d0607661e138e9dcbfd5e3038a71c1fd81e0b Mon Sep 17 00:00:00 2001 From: Matthew Orford Date: Thu, 9 Oct 2025 16:57:32 -0400 Subject: [PATCH 1/3] merge the two --- packages/cubejs-api-gateway/src/gateway.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/cubejs-api-gateway/src/gateway.ts b/packages/cubejs-api-gateway/src/gateway.ts index 8e2cfce793f26..87d69ff0e40fd 100644 --- a/packages/cubejs-api-gateway/src/gateway.ts +++ b/packages/cubejs-api-gateway/src/gateway.ts @@ -2158,6 +2158,11 @@ class ApiGateway { } public async contextByReq(req: Request, securityContext, requestId: string): Promise { + req.securityContext = { + ...req.securityContext, + ...securityContext, + }; + const extensions = typeof this.extendContext === 'function' ? await this.extendContext(req) : {}; return { From 6d71fc31e1425067d0ec59a1b6344a78cd587ef7 Mon Sep 17 00:00:00 2001 From: Matthew Orford Date: Thu, 9 Oct 2025 17:47:03 -0400 Subject: [PATCH 2/3] add tests --- packages/cubejs-api-gateway/test/auth.test.ts | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/packages/cubejs-api-gateway/test/auth.test.ts b/packages/cubejs-api-gateway/test/auth.test.ts index 5ee4f47903483..eaabbe13d68cb 100644 --- a/packages/cubejs-api-gateway/test/auth.test.ts +++ b/packages/cubejs-api-gateway/test/auth.test.ts @@ -673,4 +673,113 @@ describe('test authorization', () => { // no warnings, done on checkAuth/checkAuthMiddleware level expect(loggerMock.mock.calls.length).toEqual(0); }); + + test('extendContext receives securityContext from checkAuth', async () => { + const loggerMock = jest.fn(() => { + // + }); + + const extendContextMock = jest.fn((req) => { + return { + securityContext: { + ...req.securityContext, + extendedField: 'added_by_extend_context', + } + }; + }); + + const expectSecurityContext = (securityContext) => { + expect(securityContext.uid).toEqual(5); + expect(securityContext.extendedField).toEqual('added_by_extend_context'); + expect(securityContext.iat).toBeDefined(); + expect(securityContext.exp).toBeDefined(); + }; + + const handlerMock = jest.fn((req, res) => { + expectSecurityContext(req.context.securityContext); + res.status(200).end(); + }); + + const { app } = createApiGateway(handlerMock, loggerMock, { + extendContext: extendContextMock, + }); + + const token = generateAuthToken({ uid: 5 }); + + await request(app) + .get('/test-auth-fake') + .set('Authorization', `Authorization: ${token}`) + .expect(200); + + expect(handlerMock.mock.calls.length).toEqual(1); + expect(extendContextMock.mock.calls.length).toEqual(1); + + // should receive securityContext from checkAuth + expect(extendContextMock.mock.calls[0][0].securityContext).toMatchObject({ + uid: 5, + iat: expect.any(Number), + exp: expect.any(Number), + }); + expectSecurityContext(handlerMock.mock.calls[0][0].context.securityContext); + }); + + test('extendContext with custom checkAuth returning securityContext', async () => { + const loggerMock = jest.fn(() => { + // + }); + + const checkAuthMock = jest.fn(async (req: Request, auth?: string) => { + if (auth) { + const decoded = jwt.verify(auth, 'secret') as any; + return { + security_context: { + ...decoded, + tenantId: 'tenant_123', + customField: 'from_check_auth', + } + }; + } + return {}; + }); + + const extendContextMock = jest.fn((req) => { + // should receive securityContext from checkAuth + expect(req.securityContext).toBeDefined(); + expect(req.securityContext.customField).toEqual('from_check_auth'); + + return { + securityContext: { + ...req.securityContext, + extendedField: 'from_extend_context', + } + }; + }); + + const handlerMock = jest.fn((req, res) => { + expect(req.context.securityContext.customField).toEqual('from_check_auth'); + expect(req.context.securityContext.extendedField).toEqual('from_extend_context'); + res.status(200).end(); + }); + + const { app } = createApiGateway(handlerMock, loggerMock, { + checkAuth: checkAuthMock, + extendContext: extendContextMock, + }); + + const token = generateAuthToken({ uid: 5 }); + + await request(app) + .get('/test-auth-fake') + .set('Authorization', `Authorization: ${token}`) + .expect(200); + + expect(checkAuthMock.mock.calls.length).toEqual(1); + expect(extendContextMock.mock.calls.length).toEqual(1); + expect(handlerMock.mock.calls.length).toEqual(1); + expect(extendContextMock.mock.calls[0][0].securityContext).toMatchObject({ + uid: 5, + tenantId: 'tenant_123', + customField: 'from_check_auth', + }); + }); }); From 5be6baaab880166eb5f4abe6311f6cec0dec4119 Mon Sep 17 00:00:00 2001 From: Matthew Orford Date: Thu, 9 Oct 2025 18:27:27 -0400 Subject: [PATCH 3/3] lint --- packages/cubejs-api-gateway/test/auth.test.ts | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/cubejs-api-gateway/test/auth.test.ts b/packages/cubejs-api-gateway/test/auth.test.ts index eaabbe13d68cb..6ce1ab0127dda 100644 --- a/packages/cubejs-api-gateway/test/auth.test.ts +++ b/packages/cubejs-api-gateway/test/auth.test.ts @@ -679,14 +679,12 @@ describe('test authorization', () => { // }); - const extendContextMock = jest.fn((req) => { - return { - securityContext: { - ...req.securityContext, - extendedField: 'added_by_extend_context', - } - }; - }); + const extendContextMock = jest.fn((req) => ({ + securityContext: { + ...req.securityContext, + extendedField: 'added_by_extend_context', + } + })); const expectSecurityContext = (securityContext) => { expect(securityContext.uid).toEqual(5);