From 4f96697a67b5e262f36710c4571897179524dddf Mon Sep 17 00:00:00 2001 From: Michael Thomas Date: Thu, 26 Mar 2026 10:18:43 -0400 Subject: [PATCH] fix: handle errors in OIDC callback params --- src/components/Login/OidcLoginButton.tsx | 11 +++++++---- .../UserSettings/UserLinkedAccountsSettings/index.tsx | 7 ++++++- src/utils/oidc.ts | 6 ++++++ 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/components/Login/OidcLoginButton.tsx b/src/components/Login/OidcLoginButton.tsx index 296151f7f..593de7d0e 100644 --- a/src/components/Login/OidcLoginButton.tsx +++ b/src/components/Login/OidcLoginButton.tsx @@ -53,12 +53,15 @@ export default function OidcLoginButton({ useEffect(() => { if (loading) return; - if (query.code != null && getOidcProviderSlug() === provider.slug) { + // OIDC provider has redirected back with an authorization code or error + const isCallback = query.code != null || query.error != null; + + if (isCallback && getOidcProviderSlug() === provider.slug) { clearOidcProviderSlug(); - // OIDC provider has redirected back with an authorization code handleCallback(); - } else if (query.code == null && query.provider === provider.slug) { - // Support direct redirect via ?provider=slug query param + } + // Support direct redirect via ?provider=slug query param + else if (!isCallback && query.provider === provider.slug) { redirectToLogin(); } // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/src/components/UserProfile/UserSettings/UserLinkedAccountsSettings/index.tsx b/src/components/UserProfile/UserSettings/UserLinkedAccountsSettings/index.tsx index 6f603e77e..4ec5469bf 100644 --- a/src/components/UserProfile/UserSettings/UserLinkedAccountsSettings/index.tsx +++ b/src/components/UserProfile/UserSettings/UserLinkedAccountsSettings/index.tsx @@ -92,8 +92,13 @@ const UserLinkedAccountsSettings = () => { useEffect(() => { if (!router.isReady) return; const code = router.query.code; + const error = router.query.error; const providerSlug = getOidcProviderSlug(); - if (typeof code !== 'string' || providerSlug == null) return; + if ( + (typeof code !== 'string' && typeof error !== 'string') || + providerSlug == null + ) + return; clearOidcProviderSlug(); // Strip the OIDC params from the URL immediately diff --git a/src/utils/oidc.ts b/src/utils/oidc.ts index 618542291..ff4810e62 100644 --- a/src/utils/oidc.ts +++ b/src/utils/oidc.ts @@ -81,6 +81,12 @@ export async function processOidcCallback( ): Promise< { type: 'success' } | { type: 'error'; errorCode: string | undefined } > { + const params = new URLSearchParams(window.location.search); + const errorParam = params.get('error'); + if (errorParam != null) { + return { type: 'error', errorCode: ApiErrorCode.OidcAuthorizationFailed }; + } + try { await axios.post( `/api/v1/auth/oidc/callback/${encodeURIComponent(providerSlug)}`,