diff --git a/src/scenarios/authorization-server/authorization-server-metadata.test.ts b/src/scenarios/authorization-server/authorization-server-metadata.test.ts index b20b47e..1cf862b 100644 --- a/src/scenarios/authorization-server/authorization-server-metadata.test.ts +++ b/src/scenarios/authorization-server/authorization-server-metadata.test.ts @@ -8,27 +8,32 @@ vi.mock('undici', () => ({ const mockedRequest = vi.mocked(request); -describe('AuthorizationServerMetadataEndpointScenario (SUCCESS only)', () => { +const SERVER_URL = 'https://example.com'; +const AUTHORIZATION_ENDPOINT = `${SERVER_URL}/auth`; +const TOKEN_ENDPOINT = `${SERVER_URL}/token`; + +const validMetadata = { + issuer: SERVER_URL, + authorization_endpoint: AUTHORIZATION_ENDPOINT, + token_endpoint: TOKEN_ENDPOINT, + response_types_supported: ['code'], + code_challenge_methods_supported: ['plain', 'S256'] +}; + +function mockMetadataResponse(body: Record) { + mockedRequest.mockResolvedValue({ + statusCode: 200, + headers: { 'content-type': 'application/json' }, + body: { json: async () => body } + } as any); +} + +describe('AuthorizationServerMetadataEndpointScenario', () => { it('returns SUCCESS for valid authorization server metadata', async () => { const scenario = new AuthorizationServerMetadataEndpointScenario(); - const serverUrl = 'https://example.com'; - - mockedRequest.mockResolvedValue({ - statusCode: 200, - headers: { - 'content-type': 'application/json' - }, - body: { - json: async () => ({ - issuer: 'https://example.com', - authorization_endpoint: 'https://example.com/auth', - token_endpoint: 'https://example.com/token', - response_types_supported: ['code'] - }) - } - } as any); - - const checks = await scenario.run(serverUrl); + mockMetadataResponse(validMetadata); + + const checks = await scenario.run(SERVER_URL); expect(checks).toHaveLength(1); @@ -37,15 +42,50 @@ describe('AuthorizationServerMetadataEndpointScenario (SUCCESS only)', () => { expect(check.errorMessage).toBeUndefined(); expect(check.details).toBeDefined(); expect(check.details!.contentType).toContain('application/json'); - expect((check.details!.body as any).issuer).toBe('https://example.com'); + expect((check.details!.body as any).issuer).toBe(SERVER_URL); expect((check.details!.body as any).authorization_endpoint).toBe( - 'https://example.com/auth' - ); - expect((check.details!.body as any).token_endpoint).toBe( - 'https://example.com/token' + AUTHORIZATION_ENDPOINT ); + expect((check.details!.body as any).token_endpoint).toBe(TOKEN_ENDPOINT); expect((check.details!.body as any).response_types_supported).toEqual([ 'code' ]); + expect( + (check.details!.body as any).code_challenge_methods_supported + ).toEqual(['plain', 'S256']); + }); + + it('returns FAILURE when code_challenge_methods_supported is missing', async () => { + const scenario = new AuthorizationServerMetadataEndpointScenario(); + mockMetadataResponse({ + issuer: validMetadata.issuer, + authorization_endpoint: validMetadata.authorization_endpoint, + token_endpoint: validMetadata.token_endpoint, + response_types_supported: validMetadata.response_types_supported + }); + + const checks = await scenario.run(SERVER_URL); + + expect(checks).toHaveLength(1); + + const check = checks[0]; + expect(check.status).toBe('FAILURE'); + expect(check.errorMessage).toContain('code_challenge_methods_supported'); + }); + + it('returns FAILURE when code_challenge_methods_supported does not include S256', async () => { + const scenario = new AuthorizationServerMetadataEndpointScenario(); + mockMetadataResponse({ + ...validMetadata, + code_challenge_methods_supported: ['plain'] + }); + + const checks = await scenario.run(SERVER_URL); + + expect(checks).toHaveLength(1); + + const check = checks[0]; + expect(check.status).toBe('FAILURE'); + expect(check.errorMessage).toContain('code_challenge_methods_supported'); }); }); diff --git a/src/scenarios/authorization-server/authorization-server-metadata.ts b/src/scenarios/authorization-server/authorization-server-metadata.ts index b386090..96b3669 100644 --- a/src/scenarios/authorization-server/authorization-server-metadata.ts +++ b/src/scenarios/authorization-server/authorization-server-metadata.ts @@ -142,6 +142,15 @@ export class AuthorizationServerMetadataEndpointScenario implements ClientScenar ); } + if ( + !Array.isArray(body.code_challenge_methods_supported) || + !body.code_challenge_methods_supported.includes('S256') + ) { + errors.push( + 'Response body does not include valid "code_challenge_methods_supported" claim' + ); + } + if (body.issuer !== serverUrl) { errors.push(`Invalid issuer: ${body.issuer ?? '(missing)'}`); }