diff --git a/src/server/auth/handlers/token.test.ts b/src/server/auth/handlers/token.test.ts index 4b7fae025..946cc6910 100644 --- a/src/server/auth/handlers/token.test.ts +++ b/src/server/auth/handlers/token.test.ts @@ -16,6 +16,18 @@ jest.mock('pkce-challenge', () => ({ }) })); +const mockTokens = { + access_token: 'mock_access_token', + token_type: 'bearer', + expires_in: 3600, + refresh_token: 'mock_refresh_token' +}; + +const mockTokensWithIdToken = { + ...mockTokens, + id_token: 'mock_id_token' +} + describe('Token Handler', () => { // Mock client data const validClient: OAuthClientInformationFull = { @@ -58,12 +70,7 @@ describe('Token Handler', () => { async exchangeAuthorizationCode(client: OAuthClientInformationFull, authorizationCode: string): Promise { if (authorizationCode === 'valid_code') { - return { - access_token: 'mock_access_token', - token_type: 'bearer', - expires_in: 3600, - refresh_token: 'mock_refresh_token' - }; + return mockTokens; } throw new InvalidGrantError('The authorization code is invalid or has expired'); }, @@ -291,18 +298,36 @@ describe('Token Handler', () => { ); }); + it('returns id token in code exchange if provided', async () => { + mockProvider.exchangeAuthorizationCode = async (client: OAuthClientInformationFull, authorizationCode: string): Promise => { + if (authorizationCode === 'valid_code') { + return mockTokensWithIdToken; + } + throw new InvalidGrantError('The authorization code is invalid or has expired'); + }; + + const response = await supertest(app) + .post('/token') + .type('form') + .send({ + client_id: 'valid-client', + client_secret: 'valid-secret', + grant_type: 'authorization_code', + code: 'valid_code', + code_verifier: 'valid_verifier' + }); + + expect(response.status).toBe(200); + expect(response.body.id_token).toBe('mock_id_token'); + }); + it('passes through code verifier when using proxy provider', async () => { const originalFetch = global.fetch; try { global.fetch = jest.fn().mockResolvedValue({ ok: true, - json: () => Promise.resolve({ - access_token: 'mock_access_token', - token_type: 'bearer', - expires_in: 3600, - refresh_token: 'mock_refresh_token' - }) + json: () => Promise.resolve(mockTokens) }); const proxyProvider = new ProxyOAuthServerProvider({ @@ -359,12 +384,7 @@ describe('Token Handler', () => { try { global.fetch = jest.fn().mockResolvedValue({ ok: true, - json: () => Promise.resolve({ - access_token: 'mock_access_token', - token_type: 'bearer', - expires_in: 3600, - refresh_token: 'mock_refresh_token' - }) + json: () => Promise.resolve(mockTokens) }); const proxyProvider = new ProxyOAuthServerProvider({ diff --git a/src/shared/auth.ts b/src/shared/auth.ts index 65b800e79..3438d6ea8 100644 --- a/src/shared/auth.ts +++ b/src/shared/auth.ts @@ -62,6 +62,7 @@ export const OAuthMetadataSchema = z export const OAuthTokensSchema = z .object({ access_token: z.string(), + id_token: z.string().optional(), // Optional for OAuth 2.1, but necessary in OpenID Connect token_type: z.string(), expires_in: z.number().optional(), scope: z.string().optional(),