# Login & Authentifizierung ## Übersicht myCRM verwendet **Symfony Security** mit form-based Login und optionaler "Remember Me" Funktionalität. ## Features ✅ **Email-basierter Login** - Benutzer melden sich mit ihrer Email-Adresse an ✅ **Sicheres Password Hashing** - Automatisch mit Symfony Password Hasher ✅ **Remember Me** - "Angemeldet bleiben" für 7 Tage ✅ **CSRF Protection** - Schutz vor Cross-Site Request Forgery ✅ **Automatische Weiterleitung** - Nach Login zum Dashboard ✅ **Last Login Tracking** - Speichert Zeitpunkt des letzten Logins ✅ **Inaktive User Blocking** - User mit `isActive = false` können sich nicht einloggen ✅ **Vue.js Integration** - User-Daten werden an Frontend übergeben ## URLs | Route | Zweck | |-------|-------| | `/login` | Login-Seite | | `/logout` | Logout (POST) | | `/` | Dashboard (nach Login) | ## Test-Benutzer Nach `doctrine:fixtures:load` verfügbar: ``` Administrator: Email: admin@mycrm.local Passwort: admin123 Rechte: Vollzugriff auf alle Module Vertriebsmitarbeiter: Email: sales@mycrm.local Passwort: sales123 Rechte: Kontakte, Deals, Aktivitäten (ohne Löschrechte) ``` ⚠️ **Wichtig:** Diese Passwörter nur für Development verwenden! ## Security Configuration ```yaml # config/packages/security.yaml security: password_hashers: Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto' providers: app_user_provider: entity: class: App\Entity\User property: email firewalls: main: form_login: login_path: app_login check_path: app_login default_target_path: / logout: path: app_logout target: app_login remember_me: secret: '%kernel.secret%' lifetime: 604800 # 1 week access_control: - { path: ^/login, roles: PUBLIC_ACCESS } - { path: ^/api, roles: IS_AUTHENTICATED_FULLY } - { path: ^/, roles: ROLE_USER } ``` ## Backend Usage ### Controller: Aktuellen User abrufen ```php use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; class MyController extends AbstractController { public function index(): Response { // Get current user $user = $this->getUser(); if (!$user) { throw $this->createAccessDeniedException(); } // User properties $email = $user->getEmail(); $fullName = $user->getFullName(); $isActive = $user->isActive(); $lastLogin = $user->getLastLoginAt(); // Check roles if ($this->isGranted('ROLE_ADMIN')) { // Admin stuff } return $this->render('template.html.twig', [ 'user' => $user ]); } } ``` ### Service: User von Security Token ```php use Symfony\Bundle\SecurityBundle\Security; class MyService { public function __construct( private Security $security ) {} public function doSomething(): void { $user = $this->security->getUser(); if ($user instanceof \App\Entity\User) { // Work with user } } } ``` ### Twig: User-Informationen ```twig {% if app.user %}

Angemeldet als: {{ app.user.fullName }}

Email: {{ app.user.email }}

{% if is_granted('ROLE_ADMIN') %} Admin-Bereich {% endif %} {% else %} Login {% endif %} ``` ## Frontend Usage (Vue.js) ### Auth Store (Pinia) ```javascript import { useAuthStore } from '@/stores/auth'; // In Component const authStore = useAuthStore(); // Check authentication if (authStore.isAuthenticated) { console.log('User:', authStore.user); console.log('Full Name:', authStore.fullName); } // Check roles if (authStore.hasRole('ROLE_ADMIN')) { // Show admin features } if (authStore.isAdmin) { // Shortcut for ROLE_ADMIN check } // Logout authStore.logout(); ``` ### In Vue Components ```vue ``` ## Event Listeners ### LoginSuccessListener Wird automatisch nach erfolgreichem Login ausgeführt: ```php // src/EventListener/LoginSuccessListener.php #[AsEventListener(event: LoginSuccessEvent::class)] class LoginSuccessListener { public function __invoke(LoginSuccessEvent $event): void { $user = $event->getUser(); // Update last login timestamp $user->setLastLoginAt(new \DateTimeImmutable()); $this->entityManager->flush(); } } ``` Du kannst weitere Listener hinzufügen für: - Audit Logging - Login-Benachrichtigungen - Session-Tracking - etc. ## Password Management ### Passwort ändern ```php use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface; public function changePassword( User $user, string $newPassword, UserPasswordHasherInterface $passwordHasher ): void { $hashedPassword = $passwordHasher->hashPassword($user, $newPassword); $user->setPassword($hashedPassword); $this->entityManager->flush(); } ``` ### Neuen User erstellen ```php $user = new User(); $user->setEmail('neuer@user.de'); $user->setFirstName('Max'); $user->setLastName('Mustermann'); $user->setIsActive(true); $user->setRoles(['ROLE_USER']); $hashedPassword = $passwordHasher->hashPassword($user, 'passwort123'); $user->setPassword($hashedPassword); $entityManager->persist($user); $entityManager->flush(); ``` ## Security Best Practices 1. ✅ **Password Hashing** - Automatisch mit bcrypt/argon2 2. ✅ **CSRF Protection** - Aktiviert für Login-Form 3. ✅ **Remember Me Cookie** - Sicher mit Secret Key 4. ✅ **Inactive User Check** - User mit `isActive = false` blockiert 5. ✅ **Access Control** - Alle Routen außer `/login` erfordern Authentication 6. ✅ **HTTPS Recommended** - In Production immer HTTPS verwenden 7. ✅ **Session Security** - Symfony Session-Handling ## Troubleshooting ### "Access Denied" nach Login - Prüfe `User::getRoles()` - muss mindestens `['ROLE_USER']` zurückgeben - Prüfe `access_control` in `security.yaml` ### "Bad credentials" - Passwort falsch eingegeben - User existiert nicht - User ist inaktiv (`isActive = false`) ### Remember Me funktioniert nicht - Secret Key in `.env` gesetzt? - Cookie wird vom Browser blockiert? - Lifetime abgelaufen? ### Logout funktioniert nicht - Sicherstellen dass `/logout` als POST Route konfiguriert ist - CSRF-Token prüfen ## Next Steps - [ ] Password Reset Funktionalität - [ ] Two-Factor Authentication (2FA) - [ ] OAuth Integration (Google, Microsoft) - [ ] Rate Limiting für Login-Versuche - [ ] Login History/Audit Log - [ ] Email-Verifizierung bei Registrierung