fix(settings): the client registration form says what the server refused

Registering an OAuth client by hand and getting only "Failed to register
OAuth client" left nothing to act on: the server names the rule it broke (a
scope, the ten-client cap, a redirect URI it will not take) and the toast
swallowed it in a bare catch. Both shells show the server's message now and
keep the generic text for a failure that carries none.
This commit is contained in:
Maurice
2026-09-22 11:55:43 +02:00
parent da32eb8a3c
commit b0c19bfac1
4 changed files with 36 additions and 11 deletions
@@ -644,11 +644,11 @@ describe('IntegrationsTab', () => {
expect(createBtn).toBeDisabled();
});
it('FE-COMP-INTEGRATIONS-032: error toast shown when create OAuth client fails', async () => {
it('FE-COMP-INTEGRATIONS-032: a refused client registration toasts the reason the server gave', async () => {
const user = userEvent.setup();
server.use(
http.post('/api/oauth/clients', () =>
HttpResponse.json({ error: 'server error' }, { status: 500 })
HttpResponse.json({ error: 'Redirect URI must use HTTPS, loopback HTTP, or a private custom scheme: http://192.168.1.5/cb' }, { status: 400 })
)
);
enableMcp();
@@ -657,9 +657,24 @@ describe('IntegrationsTab', () => {
await user.click(screen.getByRole('button', { name: /New Client/i }));
await screen.findByText('Register OAuth Client');
await user.type(screen.getByPlaceholderText(/Claude Web, My MCP App/i), 'Fail Client');
await user.type(screen.getByPlaceholderText(/https:\/\/your-app/i), 'http://192.168.1.5/cb');
await user.click(screen.getByRole('button', { name: /Register Client/i }));
expect(await screen.findByText(/Redirect URI must use HTTPS/)).toBeInTheDocument();
expect(screen.queryByText('Failed to register OAuth client')).not.toBeInTheDocument();
});
it('FE-COMP-INTEGRATIONS-032b: a failure the server says nothing about keeps the generic text', async () => {
const user = userEvent.setup();
server.use(http.post('/api/oauth/clients', () => HttpResponse.error()));
enableMcp();
render(<><ToastContainer /><IntegrationsTab /></>);
await screen.findByText('MCP Configuration');
await user.click(screen.getByRole('button', { name: /New Client/i }));
await screen.findByText('Register OAuth Client');
await user.type(screen.getByPlaceholderText(/Claude Web, My MCP App/i), 'Fail Client');
await user.type(screen.getByPlaceholderText(/https:\/\/your-app/i), 'http://localhost');
await user.click(screen.getByRole('button', { name: /Register Client/i }));
expect(await screen.findByText(/Failed to register/i)).toBeInTheDocument();
expect(await screen.findByText('Failed to register OAuth client')).toBeInTheDocument();
});
});
@@ -13,6 +13,7 @@ import ApiKeysSection from './ApiKeysSection'
import { PRESET_SCOPES_DEFAULT, PRESET_SCOPES_READONLY } from '../../api/oauthScopes'
import ScopeGroupPicker from '../OAuth/ScopeGroupPicker'
import { useAuthStore } from '../../store/authStore'
import { getApiErrorMessage } from '../../utils/apiError'
interface OAuthPreset {
id: string
@@ -266,8 +267,11 @@ function useIntegrations() {
setOauthNewUris('')
setOauthNewScopes([])
setOauthIsMachine(false)
} catch {
toast.error(t('settings.oauth.toast.createError'))
} catch (err) {
// The server names the rule that refused the client (a scope, the
// ten-client cap, a redirect URI it will not take); swallowing it left
// "Failed to register OAuth client" and nothing to act on.
toast.error(getApiErrorMessage(err, t('settings.oauth.toast.createError')))
} finally {
setOauthCreating(false)
}