Skip to content

Commit 3b4e3e5

Browse files
committed
fix: Clearer OAuth error feedback
1 parent 4e0a735 commit 3b4e3e5

3 files changed

Lines changed: 53 additions & 13 deletions

File tree

src/lib/auth/oauth-errors.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
export const OAUTH_ERROR_MESSAGES: Record<string, string> = {
2+
oauthFlowNotStarted:
3+
'No active sign-in attempt was found. The flow may have expired — please try again.',
4+
providerMismatch:
5+
'The sign-in attempt did not match the provider you returned from. Please try again.',
6+
mustBeAnonymous: 'You are already signed in. Sign out before starting a new sign-in attempt.',
7+
mustBeAuthenticated: 'You must be signed in to link an external account.',
8+
emailAlreadyRegistered:
9+
'An account with this email already exists. Sign in to that account to link this provider.',
10+
internalError: 'Something went wrong on our end. Please try again in a moment.',
11+
unknown: 'Unknown error occurred.',
12+
};
13+
14+
export function getOAuthErrorMessage(code: string | null | undefined): string {
15+
return (code && OAUTH_ERROR_MESSAGES[code]) ?? OAUTH_ERROR_MESSAGES.unknown;
16+
}

src/routes/(auth)/login/+page.svelte

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,13 @@
2121
import { backendMetadata } from '$lib/state/backend-metadata-state.svelte';
2222
import { userState } from '$lib/state/user-state.svelte';
2323
import { Skeleton } from '$lib/components/ui/skeleton';
24+
import { page } from '$app/state';
25+
import { getOAuthErrorMessage } from '$lib/auth/oauth-errors';
2426
2527
registerBreadcrumbs(() => [{ label: 'Login' }]);
2628
29+
const oauthError = $derived(page.url.searchParams.get('error'));
30+
2731
let usernameOrEmail = $state('');
2832
let password = $state('');
2933
let turnstileResponse = $state<string | null>(null);
@@ -87,6 +91,11 @@
8791
</Card.Description>
8892
</Card.Header>
8993
<Card.Content>
94+
{#if oauthError}
95+
<p class="text-destructive mb-4 text-center text-sm" role="alert">
96+
{getOAuthErrorMessage(oauthError)}
97+
</p>
98+
{/if}
9099
<FieldGroup>
91100
{#if backendMetadata.state === null}
92101
<Skeleton class="h-9 w-full"></Skeleton>
Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,39 @@
11
<script lang="ts">
2+
import { afterNavigate, goto, replaceState } from '$app/navigation';
23
import { resolve } from '$app/paths';
34
import { page } from '$app/state';
45
import * as Card from '$lib/components/ui/card/index.js';
56
import { FieldDescription } from '$lib/components/ui/field/index.js';
67
import { registerBreadcrumbs } from '$lib/state/breadcrumbs-state.svelte';
8+
import { getOAuthErrorMessage } from '$lib/auth/oauth-errors';
9+
import { tick } from 'svelte';
710
811
registerBreadcrumbs(() => [{ label: 'Authentication Error' }]);
912
10-
let error = $derived(page.url.searchParams.get('error'));
13+
let errorCode = $state<string>();
14+
15+
afterNavigate(async () => {
16+
const err = page.url.searchParams.get('error');
17+
if (err === 'emailAlreadyRegistered') {
18+
await goto(resolve(`/login?error=${encodeURIComponent(err)}`), { replaceState: true });
19+
return;
20+
}
21+
errorCode = err ?? 'unknown';
22+
await tick();
23+
replaceState(resolve('/oauth/error'), {});
24+
});
1125
</script>
1226

13-
<Card.Root>
14-
<Card.Header class="text-center">
15-
<Card.Title class="text-xl">Authentication Error</Card.Title>
16-
<Card.Description>Something went wrong during authentication</Card.Description>
17-
</Card.Header>
18-
<Card.Content>
19-
<p class="text-destructive text-center" role="alert">{error}</p>
20-
<FieldDescription class="mt-4 text-center">
21-
<a href={resolve('/login')}>Back to login</a>
22-
</FieldDescription>
23-
</Card.Content>
24-
</Card.Root>
27+
{#if errorCode}
28+
<Card.Root>
29+
<Card.Header class="text-center">
30+
<Card.Title class="text-xl">Authentication Error</Card.Title>
31+
</Card.Header>
32+
<Card.Content>
33+
<p class="text-destructive text-center" role="alert">{getOAuthErrorMessage(errorCode)}</p>
34+
<FieldDescription class="mt-4 text-center">
35+
<a href={resolve('/login')}>Back to login</a>
36+
</FieldDescription>
37+
</Card.Content>
38+
</Card.Root>
39+
{/if}

0 commit comments

Comments
 (0)