diff --git a/src/components/Login/OidcLoginButton.tsx b/src/components/Login/OidcLoginButton.tsx index 2b7fa4b1..67c4f57f 100644 --- a/src/components/Login/OidcLoginButton.tsx +++ b/src/components/Login/OidcLoginButton.tsx @@ -1,6 +1,7 @@ import defineMessages from '@app/utils/defineMessages'; import { processCallback } from '@app/utils/oidc'; import type { PublicOidcProvider } from '@server/lib/settings'; +import axios from 'axios'; import { useRouter, useSearchParams } from 'next/navigation'; import { useCallback, useEffect, useState } from 'react'; import { useIntl } from 'react-intl'; @@ -26,15 +27,11 @@ export default function OidcLoginButton({ const [loading, setLoading] = useState(false); const redirectToLogin = useCallback(async () => { - let redirectUrl: string; try { - const res = await fetch(`/api/v1/auth/oidc/login/${provider.slug}`); - if (res.ok) { - const data = await res.json(); - redirectUrl = data.redirectUrl; - } else { - throw new Error(); - } + const res = await axios.get<{ redirectUrl: string }>( + `/api/v1/auth/oidc/login/${provider.slug}` + ); + window.location.href = res.data.redirectUrl; } catch (e) { setLoading(false); onError?.( @@ -42,10 +39,7 @@ export default function OidcLoginButton({ provider: provider.name, }) ); - return; } - - window.location.href = redirectUrl; }, [provider, intl, onError]); const handleCallback = useCallback(async () => { diff --git a/src/components/Settings/EditOidcModal/index.tsx b/src/components/Settings/EditOidcModal/index.tsx index 427dddc7..0735178a 100644 --- a/src/components/Settings/EditOidcModal/index.tsx +++ b/src/components/Settings/EditOidcModal/index.tsx @@ -7,6 +7,7 @@ import { Transition } from '@headlessui/react'; import { ChevronRightIcon } from '@heroicons/react/20/solid'; import { MagnifyingGlassIcon } from '@heroicons/react/24/solid'; import type { OidcProvider } from '@server/lib/settings'; +import axios from 'axios'; // <-- Import axios import { ErrorMessage, Field, @@ -99,24 +100,14 @@ export default function EditOidcModal(props: EditOidcModalProps) { const onSubmit = async ({ slug, ...provider }: OidcProvider) => { try { - const res = await fetch(`/api/v1/settings/oidc/${slug}`, { - method: 'PUT', - body: JSON.stringify(provider), - headers: { - 'Content-Type': 'application/json', - }, + await axios.put(`/api/v1/settings/oidc/${slug}`, provider); + + addToast(intl.formatMessage(messages.saveSuccess), { + appearance: 'success', + autoDismiss: true, }); - if (res.status === 200) { - addToast(intl.formatMessage(messages.saveSuccess), { - appearance: 'success', - autoDismiss: true, - }); - - props.onOk(); - } else { - throw new Error(`Request failed with code ${res.status}`); - } + props.onOk(); } catch (e) { addToast(intl.formatMessage(messages.saveError), { appearance: 'error', diff --git a/src/components/Settings/SettingsOidc/index.tsx b/src/components/Settings/SettingsOidc/index.tsx index 64c3c869..d7e01d1e 100644 --- a/src/components/Settings/SettingsOidc/index.tsx +++ b/src/components/Settings/SettingsOidc/index.tsx @@ -8,6 +8,7 @@ import { Transition } from '@headlessui/react'; import { PlusIcon } from '@heroicons/react/24/outline'; import { PencilIcon, TrashIcon } from '@heroicons/react/24/solid'; import type { OidcProvider, OidcSettings } from '@server/lib/settings'; +import axios from 'axios'; // <-- Import axios import { useState } from 'react'; import { useIntl } from 'react-intl'; import { useToasts } from 'react-toast-notifications'; @@ -47,14 +48,12 @@ export default function SettingsOidc(props: SettingsOidcProps) { async function onDelete(provider: OidcProvider) { try { - const response = await fetch(`/api/v1/settings/oidc/${provider.slug}`, { - method: 'DELETE', - }); - - if (response.status !== 200) - throw new Error(`Request failed with status ${response.status}`); - - revalidate(await response.json()); + // The fetch call is replaced with axios.delete. + // Axios automatically throws for non-2xx responses and handles JSON parsing. + const response = await axios.delete( + `/api/v1/settings/oidc/${provider.slug}` + ); + revalidate(response.data); } catch (e) { addToast(intl.formatMessage(messages.deleteError), { autoDismiss: true, diff --git a/src/components/UserProfile/UserSettings/UserLinkedAccountsSettings/index.tsx b/src/components/UserProfile/UserSettings/UserLinkedAccountsSettings/index.tsx index 7a38da01..01576f41 100644 --- a/src/components/UserProfile/UserSettings/UserLinkedAccountsSettings/index.tsx +++ b/src/components/UserProfile/UserSettings/UserLinkedAccountsSettings/index.tsx @@ -159,10 +159,14 @@ const UserLinkedAccountsSettings = () => { ...settings.currentSettings.openIdProviders.map((p) => ({ name: p.name, action: async () => { - const res = await fetch(`/api/v1/auth/oidc/login/${p.slug}`); - if (!res.ok) setError(intl.formatMessage(messages.errorUnknown)); - const json = await res.json(); - window.location.href = json.redirectUrl; + try { + const res = await axios.get<{ redirectUrl: string }>( + `/api/v1/auth/oidc/login/${p.slug}` + ); + window.location.href = res.data.redirectUrl; + } catch (e) { + setError(intl.formatMessage(messages.errorUnknown)); + } }, hide: accounts.some( (a) => diff --git a/src/utils/oidc.ts b/src/utils/oidc.ts index 920771c4..31a29e84 100644 --- a/src/utils/oidc.ts +++ b/src/utils/oidc.ts @@ -1,3 +1,5 @@ +import axios, { isAxiosError } from 'axios'; // <-- Import axios and isAxiosError + export async function processCallback( params: URLSearchParams, provider: string @@ -9,17 +11,22 @@ export async function processCallback( url.search = params.toString(); try { - const result = await fetch(url); - const message = await result.json(); + // The fetch call is replaced with axios.get. + // On success, the JSON response is automatically parsed and available in res.data. + const res = await axios.get(url.toString()); - if (!result.ok) { - return { type: 'error', message: message.message }; - } return { type: 'success', - message, + message: res.data, }; } catch (e) { + // Axios throws an error for non-2xx responses. We can check if it's an axios error + // to safely access the error message from the server's response payload. + if (isAxiosError(e) && e.response?.data?.message) { + return { type: 'error', message: e.response.data.message }; + } + + // Fallback for generic errors return { type: 'error', message: e.message,