From 7b27b74e24652eca54405a069d66ddf3cb43d386 Mon Sep 17 00:00:00 2001 From: Luke Granger-Brown Date: Fri, 26 Sep 2025 05:52:53 +0100 Subject: [PATCH] fix: Recognise authentication_required for some OIDC providers (#10252) Some OIDC providers return 401 Unauthorized errors with an empty body when the access token has expired. Avoid trying to parse the body as JSON before we've checked whether the status code is OK. Fixes #10251. --- server/utils/oauth.test.ts | 31 +++++++++++++++++++++++++++++++ server/utils/oauth.ts | 7 ++++++- 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 server/utils/oauth.test.ts diff --git a/server/utils/oauth.test.ts b/server/utils/oauth.test.ts new file mode 100644 index 0000000000..8a16d66bb7 --- /dev/null +++ b/server/utils/oauth.test.ts @@ -0,0 +1,31 @@ +import fetchMock from "jest-fetch-mock"; +import OAuthClient from "./oauth"; + +class MinimalOAuthClient extends OAuthClient { + endpoints = { + authorize: 'http://example.com/authorize', + token: 'http://example.com/token', + userinfo: 'http://example.com/userinfo', + }; +} + +beforeEach(() => { + fetchMock.resetMocks(); +}); + +describe("userInfo", () => { + it("should work with empty-body 401 Unauthorized responses", async () => { + fetchMock.mockResponseOnce('', { + status: 401, + statusText: 'unauthorized', + }); + + const client = new MinimalOAuthClient('clientid', 'clientsecret'); + try { + expect.assertions(1); + await client.userInfo('token'); + } catch (e) { + expect(e.id).toBe('authentication_required'); + } + }); +}); diff --git a/server/utils/oauth.ts b/server/utils/oauth.ts index e92e7943fa..45f5c6ee0a 100644 --- a/server/utils/oauth.ts +++ b/server/utils/oauth.ts @@ -30,7 +30,6 @@ export default abstract class OAuthClient { "Content-Type": "application/json", }, }); - data = await response.json(); } catch (err) { throw InvalidRequestError(err.message); } @@ -40,6 +39,12 @@ export default abstract class OAuthClient { throw AuthenticationError(); } + try { + data = await response.json(); + } catch (err) { + throw InvalidRequestError(err.message); + } + return data; };