From 0c57d7c108332fe47bc6d2e354a69563bb5f6519 Mon Sep 17 00:00:00 2001 From: Weiko Date: Mon, 27 Jul 2026 15:42:59 +0200 Subject: [PATCH] perf: reuse Google webhook OAuth client (#23361) ## Context A new client currently downloads signing certificates for every webhook ## Fix reuse same OAuth client instance ## Impact Probably small but not really risky to merge imho --- ...gle-messaging-notification.handler.spec.ts | 62 +++++++++++++++++++ .../google-messaging-notification.handler.ts | 3 +- 2 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 packages/twenty-server/src/modules/connected-account-sync-webhooks/drivers/google/__tests__/google-messaging-notification.handler.spec.ts diff --git a/packages/twenty-server/src/modules/connected-account-sync-webhooks/drivers/google/__tests__/google-messaging-notification.handler.spec.ts b/packages/twenty-server/src/modules/connected-account-sync-webhooks/drivers/google/__tests__/google-messaging-notification.handler.spec.ts new file mode 100644 index 0000000000..66fdb08dcc --- /dev/null +++ b/packages/twenty-server/src/modules/connected-account-sync-webhooks/drivers/google/__tests__/google-messaging-notification.handler.spec.ts @@ -0,0 +1,62 @@ +import { OAuth2Client } from 'google-auth-library'; + +import { GoogleMessagingNotificationHandler } from 'src/modules/connected-account-sync-webhooks/drivers/google/google-messaging-notification.handler'; + +const mockVerifyIdToken = jest.fn(); + +jest.mock('google-auth-library', () => ({ + ...jest.requireActual('google-auth-library'), + OAuth2Client: jest.fn().mockImplementation(() => ({ + verifyIdToken: (...args: unknown[]) => mockVerifyIdToken(...args), + })), +})); + +describe('GoogleMessagingNotificationHandler', () => { + it('should reuse the OAuth client while verifying every notification', async () => { + mockVerifyIdToken.mockResolvedValue({ + getPayload: () => ({ + email: 'pubsub@example.com', + email_verified: true, + }), + }); + + const handler = new GoogleMessagingNotificationHandler( + { + get: jest.fn((key: string) => { + if (key === 'MESSAGING_GMAIL_PUBSUB_VERIFICATION_EMAIL') { + return 'pubsub@example.com'; + } + + if (key === 'SERVER_URL') { + return 'https://example.com'; + } + }), + } as never, + { + incrementCounterBy: jest.fn(), + } as never, + {} as never, + {} as never, + {} as never, + ); + + await handler.handle({ + authorizationHeader: 'Bearer first-token', + body: {}, + }); + await handler.handle({ + authorizationHeader: 'Bearer second-token', + body: {}, + }); + + expect(OAuth2Client).toHaveBeenCalledTimes(1); + expect(mockVerifyIdToken).toHaveBeenNthCalledWith(1, { + idToken: 'first-token', + audience: 'https://example.com/webhooks/google/messaging', + }); + expect(mockVerifyIdToken).toHaveBeenNthCalledWith(2, { + idToken: 'second-token', + audience: 'https://example.com/webhooks/google/messaging', + }); + }); +}); diff --git a/packages/twenty-server/src/modules/connected-account-sync-webhooks/drivers/google/google-messaging-notification.handler.ts b/packages/twenty-server/src/modules/connected-account-sync-webhooks/drivers/google/google-messaging-notification.handler.ts index 3960e51e5f..de2d821986 100644 --- a/packages/twenty-server/src/modules/connected-account-sync-webhooks/drivers/google/google-messaging-notification.handler.ts +++ b/packages/twenty-server/src/modules/connected-account-sync-webhooks/drivers/google/google-messaging-notification.handler.ts @@ -34,6 +34,7 @@ export type GoogleMessagingNotificationRequest = { @Injectable() export class GoogleMessagingNotificationHandler implements WebhookNotificationHandler { private readonly logger = new Logger(GoogleMessagingNotificationHandler.name); + private readonly oauth2Client = new OAuth2Client(); constructor( private readonly twentyConfigService: TwentyConfigService, @@ -125,7 +126,7 @@ export class GoogleMessagingNotificationHandler implements WebhookNotificationHa const expectedAudience = `${this.twentyConfigService.get('SERVER_URL')}/webhooks/google/messaging`; try { - const ticket = await new OAuth2Client().verifyIdToken({ + const ticket = await this.oauth2Client.verifyIdToken({ idToken, audience: expectedAudience, });