diff --git a/plugins/auth-backend/src/providers/gcp-iap/helpers.test.ts b/plugins/auth-backend/src/providers/gcp-iap/helpers.test.ts index 5063090442..94fa9c0bd8 100644 --- a/plugins/auth-backend/src/providers/gcp-iap/helpers.test.ts +++ b/plugins/auth-backend/src/providers/gcp-iap/helpers.test.ts @@ -56,6 +56,23 @@ describe('helpers', () => { ); await expect(validator(validJwt)).rejects.toThrowError(TypeError); }); + + it('rejects empty payload', async () => { + const mockClient = { + getIapPublicKeys: async () => ({ pubkeys: '' }), + verifySignedJwtWithCertsAsync: async () => ({ + getPayload: () => undefined, + }), + }; + const validator = createTokenValidator( + 'a', + mockClient as unknown as OAuth2Client, + ); + await expect(validator(validJwt)).rejects.toMatchObject({ + name: 'TypeError', + message: 'Token had no payload', + }); + }); }); describe('parseRequestToken', () => { @@ -106,18 +123,11 @@ describe('helpers', () => { }); it('rejects bad token payloads', async () => { - await expect( - parseRequestToken(validJwt, async () => undefined as any), - ).rejects.toMatchObject({ - name: 'AuthenticationError', - message: 'Google IAP token had no payload', - }); - await expect( parseRequestToken(validJwt, async () => ({ sub: 'a' } as any)), ).rejects.toMatchObject({ name: 'AuthenticationError', - message: 'Google IAP token payload had no sub or email claim', + message: 'Google IAP token payload is missing sub and/or email claim', }); }); }); diff --git a/plugins/auth-backend/src/providers/gcp-iap/helpers.ts b/plugins/auth-backend/src/providers/gcp-iap/helpers.ts index 0bc4c34ecc..c7e81adc73 100644 --- a/plugins/auth-backend/src/providers/gcp-iap/helpers.ts +++ b/plugins/auth-backend/src/providers/gcp-iap/helpers.ts @@ -23,9 +23,13 @@ export function createTokenValidator( audience: string, mockClient?: OAuth2Client, ): (token: string) => Promise { - return async function tokenValidator(token) { - const client = mockClient ?? new OAuth2Client(); + const client = mockClient ?? new OAuth2Client(); + return async function tokenValidator(token) { + // TODO(freben): Rate limit the public key reads. It may be sensible to + // cache these for some reasonable time rather than asking for the public + // keys on every single sign-in. But since the rate of events here is so + // slow, I decided to keep it simple for now. const response = await client.getIapPublicKeys(); const ticket = await client.verifySignedJwtWithCertsAsync( token, @@ -36,7 +40,7 @@ export function createTokenValidator( const payload = ticket.getPayload(); if (!payload) { - throw new TypeError('No payload'); + throw new TypeError('Token had no payload'); } return payload; @@ -60,11 +64,9 @@ export async function parseRequestToken( throw new AuthenticationError(`Google IAP token verification failed, ${e}`); } - if (!payload) { - throw new AuthenticationError('Google IAP token had no payload'); - } else if (!payload.sub || !payload.email) { + if (!payload.sub || !payload.email) { throw new AuthenticationError( - 'Google IAP token payload had no sub or email claim', + 'Google IAP token payload is missing sub and/or email claim', ); }