Implement authentication phase with Cidaas OAuth2 integration
- Add authentication middleware to protect routes - Create API endpoints for login, logout, registration, and user info - Develop UI components for login and registration forms - Integrate VeeValidate for form validation - Update environment configuration for Cidaas settings - Add i18n support for English and German languages - Enhance Tailwind CSS for improved styling of auth components - Document authentication flow and testing procedures
This commit is contained in:
69
app/components/Auth/LoginForm.vue
Normal file
69
app/components/Auth/LoginForm.vue
Normal file
@@ -0,0 +1,69 @@
|
||||
<!-- app/components/Auth/LoginForm.vue -->
|
||||
|
||||
<script setup lang="ts">
|
||||
import { z } from 'zod'
|
||||
import { useForm } from 'vee-validate'
|
||||
import { toTypedSchema } from '@vee-validate/zod'
|
||||
|
||||
const { login } = useAuth()
|
||||
|
||||
// Validation schema
|
||||
const loginSchema = toTypedSchema(
|
||||
z.object({
|
||||
email: z.string().email('Bitte geben Sie eine gültige E-Mail-Adresse ein'),
|
||||
})
|
||||
)
|
||||
|
||||
// Form state
|
||||
const { handleSubmit, isSubmitting, errors } = useForm({
|
||||
validationSchema: loginSchema,
|
||||
})
|
||||
|
||||
// Success/error state
|
||||
const submitError = ref<string | null>(null)
|
||||
|
||||
// Form submit handler
|
||||
const onSubmit = handleSubmit(async (values) => {
|
||||
submitError.value = null
|
||||
|
||||
try {
|
||||
await login(values.email)
|
||||
// Redirect happens in login() function
|
||||
} catch (error: any) {
|
||||
console.error('Login error:', error)
|
||||
submitError.value = error.data?.message || 'Anmeldung fehlgeschlagen. Bitte versuchen Sie es erneut.'
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<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 -->
|
||||
<FormField v-slot="{ componentField }" name="email">
|
||||
<FormItem>
|
||||
<FormLabel class="text-white/90 text-base font-medium">E-Mail-Adresse</FormLabel>
|
||||
<FormControl>
|
||||
<Input type="email" placeholder="ihre.email@beispiel.de" v-bind="componentField" />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
|
||||
<!-- Submit Button -->
|
||||
<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" />
|
||||
{{ isSubmitting ? 'Wird angemeldet...' : 'Anmelden' }}
|
||||
</Button>
|
||||
|
||||
<!-- Info Text -->
|
||||
<p class="text-sm text-white/70 text-center">
|
||||
Sie werden zur sicheren Anmeldeseite weitergeleitet
|
||||
</p>
|
||||
</form>
|
||||
</template>
|
||||
151
app/components/Auth/RegisterForm.vue
Normal file
151
app/components/Auth/RegisterForm.vue
Normal file
@@ -0,0 +1,151 @@
|
||||
<!-- app/components/Auth/RegisterForm.vue -->
|
||||
|
||||
<script setup lang="ts">
|
||||
import { z } from 'zod'
|
||||
import { useForm } from 'vee-validate'
|
||||
import { toTypedSchema } from '@vee-validate/zod'
|
||||
|
||||
const { register } = useAuth()
|
||||
|
||||
// Validation schema
|
||||
const registerSchema = toTypedSchema(
|
||||
z.object({
|
||||
email: z.string().email('Bitte geben Sie eine gültige E-Mail-Adresse ein'),
|
||||
password: z
|
||||
.string()
|
||||
.min(8, 'Das Passwort muss mindestens 8 Zeichen lang sein')
|
||||
.regex(/[A-Z]/, 'Das Passwort muss mindestens einen Großbuchstaben enthalten')
|
||||
.regex(/[a-z]/, 'Das Passwort muss mindestens einen Kleinbuchstaben enthalten')
|
||||
.regex(/[0-9]/, 'Das Passwort muss mindestens eine Zahl enthalten'),
|
||||
firstName: z.string().min(2, 'Der Vorname muss mindestens 2 Zeichen lang sein'),
|
||||
lastName: z.string().min(2, 'Der Nachname muss mindestens 2 Zeichen lang sein'),
|
||||
})
|
||||
)
|
||||
|
||||
// Form state
|
||||
const { handleSubmit, isSubmitting } = useForm({
|
||||
validationSchema: registerSchema,
|
||||
})
|
||||
|
||||
// Success/error state
|
||||
const submitError = ref<string | null>(null)
|
||||
const submitSuccess = ref(false)
|
||||
|
||||
// Form submit handler
|
||||
const onSubmit = handleSubmit(async (values) => {
|
||||
submitError.value = null
|
||||
submitSuccess.value = false
|
||||
|
||||
try {
|
||||
const result = await register(values)
|
||||
|
||||
submitSuccess.value = true
|
||||
|
||||
// Show success message for 3 seconds, then switch to login tab
|
||||
setTimeout(() => {
|
||||
navigateTo('/auth?tab=login')
|
||||
}, 3000)
|
||||
} catch (error: any) {
|
||||
console.error('Registration error:', error)
|
||||
|
||||
if (error.status === 409) {
|
||||
submitError.value = 'Diese E-Mail-Adresse ist bereits registriert.'
|
||||
} else {
|
||||
submitError.value = error.data?.message || 'Registrierung fehlgeschlagen. Bitte versuchen Sie es erneut.'
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<form @submit="onSubmit" class="space-y-5">
|
||||
<!-- Success Alert -->
|
||||
<Alert v-if="submitSuccess" class="border-success bg-success/10 text-white">
|
||||
<CheckCircle class="h-5 w-5 text-success" />
|
||||
<AlertTitle class="text-success font-medium">Registrierung erfolgreich!</AlertTitle>
|
||||
<AlertDescription class="text-white/90">
|
||||
Bitte bestätigen Sie Ihre E-Mail-Adresse über den Link, den wir Ihnen gesendet haben.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
|
||||
<!-- 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>
|
||||
|
||||
<!-- First Name -->
|
||||
<FormField v-slot="{ componentField }" name="firstName">
|
||||
<FormItem>
|
||||
<FormLabel class="text-white/90 text-base font-medium">Vorname</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
type="text"
|
||||
placeholder="Max"
|
||||
v-bind="componentField"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
|
||||
<!-- Last Name -->
|
||||
<FormField v-slot="{ componentField }" name="lastName">
|
||||
<FormItem>
|
||||
<FormLabel class="text-white/90 text-base font-medium">Nachname</FormLabel>
|
||||
<FormControl>
|
||||
<Input type="text" placeholder="Mustermann" v-bind="componentField" />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
|
||||
<!-- Email -->
|
||||
<FormField v-slot="{ componentField }" name="email">
|
||||
<FormItem>
|
||||
<FormLabel class="text-white/90 text-base font-medium">E-Mail-Adresse</FormLabel>
|
||||
<FormControl>
|
||||
<Input type="email" placeholder="ihre.email@beispiel.de" v-bind="componentField" />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
|
||||
<!-- Password -->
|
||||
<FormField v-slot="{ componentField }" name="password">
|
||||
<FormItem>
|
||||
<FormLabel class="text-white/90 text-base font-medium">Passwort</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
type="password"
|
||||
placeholder="Mindestens 8 Zeichen"
|
||||
v-bind="componentField"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormDescription class="text-white/60 text-sm">
|
||||
Mindestens 8 Zeichen, Groß-/Kleinbuchstaben und eine Zahl
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
</FormField>
|
||||
|
||||
<!-- Submit Button -->
|
||||
<Button type="submit" variant="experimenta" size="experimenta" class="w-full" :disabled="isSubmitting || submitSuccess">
|
||||
<Loader2 v-if="isSubmitting" class="mr-2 h-5 w-5 animate-spin" />
|
||||
{{ isSubmitting ? 'Wird registriert...' : 'Konto erstellen' }}
|
||||
</Button>
|
||||
|
||||
<!-- Terms & Privacy -->
|
||||
<p class="text-sm text-white/70 text-center">
|
||||
Mit der Registrierung stimmen Sie unserer
|
||||
<a href="/datenschutz" class="text-accent font-medium transition-all duration-300 hover:brightness-125">
|
||||
Datenschutzerklärung
|
||||
</a>
|
||||
und den
|
||||
<a href="/agb" class="text-accent font-medium transition-all duration-300 hover:brightness-125">
|
||||
Nutzungsbedingungen
|
||||
</a>
|
||||
zu.
|
||||
</p>
|
||||
</form>
|
||||
</template>
|
||||
Reference in New Issue
Block a user