Browse Source

Refactor login error handling and improve user feedback

- Update LoginForm component to display error messages directly from the authentication response.
- Modify useAuth composable to handle login errors more effectively, ensuring proper error messages are thrown.
- Enhance login API response to return structured error messages for invalid credentials.
- Adjust Cidaas utility to throw specific errors for invalid username/password scenarios.
main
Bastian Masanek 2 months ago
parent
commit
c2c706ebcf
  1. 3
      .claude/settings.local.json
  2. 15
      app/components/Auth/LoginForm.vue
  3. 18
      app/composables/useAuth.ts
  4. 21
      server/api/auth/login.post.ts
  5. 8
      server/utils/cidaas.ts

3
.claude/settings.local.json

@ -49,7 +49,8 @@
"WebFetch(domain:www.shadcn-vue.com)", "WebFetch(domain:www.shadcn-vue.com)",
"WebFetch(domain:docs.cidaas.com)", "WebFetch(domain:docs.cidaas.com)",
"WebFetch(domain:articles.cidaas.de)", "WebFetch(domain:articles.cidaas.de)",
"WebFetch(domain:pre-release-docs.cidaas.com)" "WebFetch(domain:pre-release-docs.cidaas.com)",
"mcp__playwright__browser_console_messages"
], ],
"deny": [], "deny": [],
"ask": [] "ask": []

15
app/components/Auth/LoginForm.vue

@ -32,19 +32,14 @@ const onSubmit = handleSubmit(async (values) => {
// Redirect happens in login() function // Redirect happens in login() function
} catch (error: any) { } catch (error: any) {
console.error('Login error:', error) console.error('Login error:', error)
submitError.value = error.data?.message || 'Anmeldung fehlgeschlagen. Bitte versuchen Sie es erneut.' // Error message is now directly in error.message (thrown by useAuth composable)
submitError.value = error.message || 'Anmeldung fehlgeschlagen. Bitte versuchen Sie es erneut.'
} }
}) })
</script> </script>
<template> <template>
<form @submit="onSubmit" class="space-y-6"> <form @submit="onSubmit" class="space-y-6">
<!-- Error Alert -->
<Alert v-if="submitError" class="border-destructive bg-destructive/10 text-white">
<AlertCircle class="h-5 w-5 text-destructive" />
<AlertDescription class="text-white/90">{{ submitError }}</AlertDescription>
</Alert>
<!-- Email Field --> <!-- Email Field -->
<FormField v-slot="{ componentField }" name="email"> <FormField v-slot="{ componentField }" name="email">
<FormItem> <FormItem>
@ -67,6 +62,12 @@ const onSubmit = handleSubmit(async (values) => {
</FormItem> </FormItem>
</FormField> </FormField>
<!-- Error Alert -->
<Alert v-if="submitError" class="border-destructive bg-destructive/10 text-white">
<AlertCircle class="h-5 w-5 text-destructive" />
<AlertDescription class="text-white/90">{{ submitError }}</AlertDescription>
</Alert>
<!-- Submit Button --> <!-- Submit Button -->
<Button type="submit" variant="experimenta" size="experimenta" class="w-full" :disabled="isSubmitting"> <Button type="submit" variant="experimenta" size="experimenta" class="w-full" :disabled="isSubmitting">
<Loader2 v-if="isSubmitting" class="mr-2 h-5 w-5 animate-spin" /> <Loader2 v-if="isSubmitting" class="mr-2 h-5 w-5 animate-spin" />

18
app/composables/useAuth.ts

@ -17,13 +17,23 @@ export function useAuth() {
* Direct authentication via Cidaas API (no redirect) * Direct authentication via Cidaas API (no redirect)
*/ */
async function login(email: string, password: string) { async function login(email: string, password: string) {
try {
// Call login endpoint - creates session directly // Call login endpoint - creates session directly
await $fetch('/api/auth/login', { const response = await $fetch<{ success: boolean; message?: string }>('/api/auth/login', {
method: 'POST', method: 'POST',
body: { email, password }, body: { email, password },
// Don't throw on 4xx/5xx, we handle the response ourselves
ignoreResponseError: true,
}) })
// Check if login was successful
if (!response.success) {
// Throw error with the server's message (contains German text with umlauts)
const error: any = new Error(response.message || 'Login failed')
error.data = response
error.statusCode = 401
throw error
}
// Refresh user session // Refresh user session
await fetch() await fetch()
@ -33,10 +43,6 @@ export function useAuth() {
redirectAfterLogin.value = null // Clear cookie redirectAfterLogin.value = null // Clear cookie
navigateTo(destination) navigateTo(destination)
} catch (error) {
console.error('Login failed:', error)
throw error
}
} }
/** /**

21
server/api/auth/login.post.ts

@ -21,6 +21,7 @@
import { z } from 'zod' import { z } from 'zod'
import { eq } from 'drizzle-orm' import { eq } from 'drizzle-orm'
import { users } from '../../database/schema'
const loginSchema = z.object({ const loginSchema = z.object({
email: z.string().email('Invalid email address'), email: z.string().email('Invalid email address'),
@ -93,15 +94,19 @@ export default defineEventHandler(async (event) => {
// Handle specific error cases // Handle specific error cases
if (error.statusCode === 401) { if (error.statusCode === 401) {
throw createError({ // Set response status and return error as JSON body
statusCode: 401, // This ensures umlauts are properly encoded in the response body
statusMessage: 'Ungültige E-Mail-Adresse oder Passwort', setResponseStatus(event, 401, 'Invalid credentials')
}) return {
success: false,
message: 'Ungültige E-Mail-Adresse oder Passwort',
}
} }
throw createError({ setResponseStatus(event, 500, 'Login failed')
statusCode: 500, return {
statusMessage: 'Anmeldung fehlgeschlagen', success: false,
}) message: 'Anmeldung fehlgeschlagen',
}
} }
}) })

8
server/utils/cidaas.ts

@ -256,6 +256,14 @@ export async function loginWithPassword(
console.error('Cidaas password login failed:', errorData) console.error('Cidaas password login failed:', errorData)
// Handle specific errors // Handle specific errors
// Cidaas returns 400 with error: 'invalid_username_password' for invalid credentials
if (response.status === 400 && errorData.error === 'invalid_username_password') {
throw createError({
statusCode: 401,
statusMessage: 'Invalid email or password',
})
}
if (response.status === 401) { if (response.status === 401) {
throw createError({ throw createError({
statusCode: 401, statusCode: 401,

Loading…
Cancel
Save