diff --git a/test/e2e/requirements.ts b/test/e2e/requirements.ts index fa37dbf5a8..36da4fc523 100644 --- a/test/e2e/requirements.ts +++ b/test/e2e/requirements.ts @@ -2937,15 +2937,9 @@ export const REQUIREMENTS: Record = { 'client-auth:authprovider:onunauthorized-retry': { source: 'sdk', behavior: - 'When the server answers 401, the transport awaits AuthProvider.onUnauthorized() and retries the request once with the refreshed token; a second 401 (or a provider without onUnauthorized) surfaces as UnauthorizedError.', + 'When the server answers 401, the transport awaits AuthProvider.onUnauthorized() and retries the request once with the refreshed token; a second 401 on that retry rejects with SdkHttpError (ClientHttpAuthentication), while a provider without onUnauthorized surfaces the 401 as UnauthorizedError.', transports: ['streamableHttp'], - note: "This exercises the HTTP client transport's auth hook; the matrix transport arg is ignored, so it runs as a single streamableHttp-labelled cell to avoid duplicate runs.", - knownFailures: [ - { - test: 'second 401 after retry surfaces as UnauthorizedError', - note: 'A second 401 after onUnauthorized() re-authentication surfaces as SdkHttpError (ClientHttpAuthentication) instead of the UnauthorizedError documented on AuthProvider.onUnauthorized().' - } - ] + note: "This exercises the HTTP client transport's auth hook; the matrix transport arg is ignored, so it runs as a single streamableHttp-labelled cell to avoid duplicate runs." }, 'client-auth:authprovider:oauth-provider-adapted': { source: 'sdk', diff --git a/test/e2e/scenarios/client-auth.test.ts b/test/e2e/scenarios/client-auth.test.ts index 556cd29ef8..134edd4dd4 100644 --- a/test/e2e/scenarios/client-auth.test.ts +++ b/test/e2e/scenarios/client-auth.test.ts @@ -32,6 +32,8 @@ import { RegistrationRejectedError, resolveClientMetadata, SdkError, + SdkErrorCode, + SdkHttpError, SSEClientTransport, SseError, startAuthorization, @@ -2221,7 +2223,7 @@ verifies( const STALE = 'stale-bearer-token'; const ROTATED = 'rotated-but-still-rejected-token'; - // The provider refreshes on 401 but the resource keeps rejecting: the retry's 401 must surface as UnauthorizedError. + // The provider refreshes on 401 but the resource keeps rejecting: the retry's 401 must reject with SdkHttpError (ClientHttpAuthentication). let currentToken = STALE; let unauthorizedCalls = 0; const provider: AuthProvider = { @@ -2244,7 +2246,9 @@ verifies( const transport = new StreamableHTTPClientTransport(new URL(MCP_URL), { authProvider: provider, fetch: alwaysUnauthorizedFetch }); try { - await expect(client.connect(transport)).rejects.toThrow(UnauthorizedError); + const connectPromise = client.connect(transport); + await expect(connectPromise).rejects.toBeInstanceOf(SdkHttpError); + await expect(connectPromise).rejects.toMatchObject({ code: SdkErrorCode.ClientHttpAuthentication, status: 401 }); // onUnauthorized ran once and the transport retried exactly once before giving up. expect(unauthorizedCalls).toBe(1); @@ -2253,7 +2257,7 @@ verifies( await client.close(); } }, - { title: 'second 401 after retry surfaces as UnauthorizedError' } + { title: 'second 401 after retry rejects with SdkHttpError' } ); verifies('client-auth:authprovider:oauth-provider-adapted', async (_args: TestArgs) => {