ithilbert / userauth
User Auth system with Roles, Permissions, 2FA, Audit-Log, Teams.
Requires
- php: ^8.5
- illuminate/auth: ^11.0 || ^12.0
- illuminate/console: ^11.0 || ^12.0
- illuminate/database: ^11.0 || ^12.0
- illuminate/http: ^11.0 || ^12.0
- illuminate/mail: ^11.0 || ^12.0
- illuminate/routing: ^11.0 || ^12.0
- illuminate/support: ^11.0 || ^12.0
- illuminate/validation: ^11.0 || ^12.0
- nesbot/carbon: ^2.0 || ^3.0
- yajra/laravel-datatables-oracle: ^11.0 || ^12.0
Requires (Dev)
None
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Beschreibung: User Auth System mit integrierter Rollenverwaltung, Impersonate-Funktion, erweiterten Passwort-Regeln, Passwort-Reset, Zwei-Faktor-Anmeldung, Social Login (Google) und Mandanten- / Teams-Fähigkeit.
Dokumentation
Die ausführliche Dokumentation befindet sich im Ordner docs/:
Installation
composer require ithilbert/userauth
Voraussetzungen und Abhängigkeiten
| Abhängigkeit | Anforderung | Wofür |
|---|---|---|
| PHP | 8.5 | Paket-Code |
Laravel (illuminate/*) |
12 oder 13 | Framework |
ithilbert/laravel-kit |
dev-main |
Trait VueComboBox, das UserAuth verwendet |
laravel/socialite |
^5.0 (automatisch installiert) |
Social Login (Google) |
yajra/laravel-datatables-oracle |
^12.0 oder ^13.0 (automatisch installiert) |
Nutzerliste (AJAX) |
nesbot/carbon |
^2.0 oder ^3.0 (automatisch installiert) |
Datumsberechnungen |
| Mailserver | in config/mail.php konfiguriert und erreichbar |
Versand der 2FA-Codes (synchron) und der Reset-Links |
| Cache-Store | nicht array (z. B. file, database, redis) |
Zähler der 2FA-Fehlversuche, Sperre „Code erneut senden“, Drosselungen |
| Session-Treiber | speichert zwischen Requests (nicht array) |
Offene Zwei-Faktor-Anmeldung liegt in der Session |
ithilbert/laravel-kit als dev-main: Das Repo hat keine Releases. Ist minimum-stability in der composer.json des Hosts stable, verlangt der Host das Paket selbst als dev-main:
composer require ithilbert/laravel-kit:dev-main
Eigene Testumgebung (Paket-Entwicklung)
Das Paket hat eine eigene Testbench-App (tests/TestCase.php) und ist unabhängig vom Host testbar:
cd packages/userauth
composer install
vendor/bin/phpunit
phpunit.xml.dist: SQLite:memory:, Cache und Sessionarray, Mailarray, Queuesync.- Fixture-User mit Trait
UserAuth:tests/Fixtures/User.php; Paket-Migrationen laufen automatisch. - Die Laravel-Version kommt aus dem
vendor/des Pakets. Ein Lauf gegen Laravel 12 und einer gegen Laravel 13 erfolgen je mit eigenervendor/. vendor/undcomposer.locksind in.gitignoredes Pakets eingetragen.- Der Host führt die Paket-Tests über
phpunit.xmlmit aus (Suitepackages/userauth/tests/Feature).
Konfiguration
.env Optionen prüfen
Um bestimmte Features global in einem Projekt zu aktivieren, füge folgende Zeilen zu deiner .env Datei hinzu und passe sie nach deinen Bedürfnissen an:
# Logging USERAUTH_AUDIT_LOG_ENABLED=false # Sicherheits-Features USERAUTH_2FA_ENABLED=false USERAUTH_PASSWORD_POLICY_ENABLED=true # Mandantenfähigkeit (Teams) USERAUTH_TEAMS_ENABLED=true # Theme der Auth-Views (Login, Passwort-Reset): 'tailwind' (Default) oder 'bootstrap' # Nur in ÄLTEREN Bootstrap-/Phoenix-Projekten setzen: USERAUTH_THEME=bootstrap
Pflichten des Hosts
Diese Punkte muss der Host erfüllen, sonst schlagen Login, Passwort-Reset oder Rechteprüfung fehl:
- User-Model: verwendet den Trait
ITHilbert\UserAuth\Traits\UserAuthund implementiertIlluminate\Contracts\Auth\CanResetPassword(z. B. durch Erweiterung vonIlluminate\Foundation\Auth\User). OhneCanResetPasswordwirft der Passwort-ResetUserModelMisconfiguredException. - Model-Zuordnung:
auth.providers.users.modelzeigt auf das User-Model. Fehlt der Eintrag, wirft das PaketUserModelMisconfiguredException; es fällt nicht aufApp\Models\Userzurück. - Standard-Guard:
auth.defaults.guardist ein Session-Guard (Treibersession). Sonst wirft das PaketAuthGuardMisconfiguredException. - Cache-Store:
CACHE_STOREist nichtarray. Mitarraygelten die Zähler (2FA-Fehlversuche, 60-Sekunden-Sperre, Reset-Drosselung) nur für einen Request. - Absender:
mail.fromist gesetzt. Reset-Link und 2FA-Code gehen an diesen Absender. - Eigene Routen auf den Passwort-Reset: Die Route
password.sendtockenträgt die MiddlewareInvokeDeferredCallbacksselbst. Eigene Routen, die den Reset-UseCase aufrufen, brauchen sie ebenfalls, sonst wird die Reset-Mail nie verschickt. - Rechte vergeben:
user_readfür die Nutzerliste,user_create/user_edit/role_create/role_editnur an Vertrauenspersonen (siehe Rollen und Rechte). - APP_KEY: Der Schlüssel für die 2FA-Codes ist
app.key. Ein Wechsel desAPP_KEYmacht alle offenen Zwei-Faktor-Anmeldungen ungültig. - Veröffentlichte Views und Sprachdateien: Hosts mit veröffentlichten Dateien übernehmen die Änderungen (siehe Upgrade-Hinweise).
- Migrationen:
php artisan migrateausführen.
Auth-View-Theme (Login & Passwort-Reset)
Die Auth-Views (Login, Passwort-vergessen/-zurücksetzen) liegen in zwei Theme-Varianten vor und werden über config('userauth.theme') gewählt:
tailwind(Default) — Tailwind CSS. Das Package liefert eine vorkompilierte CSS mit (Routeuserauth.assets.css, erreichbar unter/userauth/assets/userauth.css?v=<hash>). Der Host-Browser cached diese mitCache-Control: immutable. Neue Projekte brauchen nichts zu konfigurieren — weder Tailwind, noch@source, nochvendor:publish.bootstrap— das ältere Phoenix/Bootstrap-Markup. In bestehenden Bootstrap-Projekten perUSERAUTH_THEME=bootstrapin der.envaktivieren. Wichtig: Das Package liefert kein Bootstrap oder Phoenix mit. Daher ist eine Konfiguration erforderlich (siehe unten unter Auth-Assets-Konfiguration).
Auth-Assets-Konfiguration
Beide Layouts (Tailwind und Bootstrap) laden zusätzliche CSS und JavaScript über die Config auth_assets. Dies wird v.a. beim Bootstrap-Theme relevant, da das Package selbst kein Bootstrap oder Phoenix mitbringt.
Konfiguriere in deiner veröffentlichten config/userauth.php:
'auth_assets' => [ 'css' => [ 'assets/css/theme.min.css', // z.B. Phoenix Theme 'assets/css/user.min.css', // z.B. projektspezifische Styles ], 'js' => [ // optional: projektspezifische Scripts ], ],
Pfade: Alle Pfade sind relativ zu public/ — z.B. 'assets/css/theme.min.css' lädt /public/assets/css/theme.min.css.
Tailwind-Theme: Das Package-eigene Tailwind wird bereits geladen (Route userauth.assets.css). Die auth_assets sind hier optional und können für zusätzliche projektspezifische Styles genutzt werden.
Bootstrap-Theme: Die auth_assets sind erforderlich, um Phoenix/Bootstrap und weitere Styles zu laden. Ohne auth_assets bleibt die Login-Seite ungestylt.
Projektspezifisches Design: Die Tailwind-Views erweitern das Layout userauth::layouts.auth. Um das Login-Design an das jeweilige Projekt anzupassen, überschreibe dieses Layout im Projekt:
resources/views/vendor/userauth/layouts/auth.blade.php
Das Package liefert eine neutrale Default-Hülle; das überschriebene Layout bestimmt Branding, Farben und Hintergrund. So bleibt die Login-Struktur im Package, das Aussehen pro Projekt.
Globale Auth-Übersetzungen: Die Standard-Laravel-Auth-Keys (auth.failed, auth.password, auth.throttle) werden vom Package namespace-los registriert und sind damit in jedem Projekt übersetzt (de/en), ohne dass eine lang/-Datei im Projekt nötig ist.
Für Package-Entwickler
Nach Änderungen an den Tailwind-Views im Package (z.B. neue Klassen hinzufügen) musst du die Package-CSS neu bauen:
cd packages/userauth
npm install
npm run build:css
Dies kompiliert resources-src/userauth.css zu src/Resources/assets/css/userauth.css. Wichtig: Die gebaute src/Resources/assets/css/userauth.css muss zusammen mit deinen View-Änderungen committed werden. Clients erhalten die neue CSS dann automatisch via Asset-Route mit aktualisiertem Versions-Hash.
Config-Optionen
Nach der Paket-Installation können die Details auch in der veröffentlichten Config-Datei unter config/userauth.php angepasst werden:
Password Policy / Ablauf
Zwingender Wechsel von Passwörtern bei Bedarf aktivieren. Verhindert auch, dass die X letzten Passwörter einfach wiederverwendet werden. Die Passwort-Historie wird nur geführt, solange enabled aktiv ist; das gilt auch beim Zurücksetzen per Reset-Link. Der Ablauf (require_change_every_days) greift erst nach der Zwei-Faktor-Anmeldung.
'password_policy' => [ 'enabled' => env('USERAUTH_PASSWORD_POLICY_ENABLED', false), 'require_change_every_days' => 90, 'prevent_reuse_last_passwords' => 3, ],
Impersonate
System-Admins können temporär ("Impersonate") die Perspektive eines beliebigen Endnutzers einnehmen. Ein Button kann mit @if(auth()->user()->isImpersonated()) ... @endif überwacht oder ein "Zurück"-Button mit Link auf /impersonate/leave gebaut werden. Impersonation durchläuft keine Zwei-Faktor-Anmeldung. Die Rechte prüft das Paket mit der Rolle des Handelnden (siehe Rollen und Rechte).
'impersonate_enabled' => true,
Multi-Tenancy (Teams)
Erlaubt eine Mandanten-Trennung, bei der User zu Teams gehören (inklusive Team-eigenen Rollen).
Wird das Flag USERAUTH_TEAMS_ENABLED=true gesetzt, liest das System Rechte (hasRole, hasPermission) dynamisch vom aktuell aktiven current_team_id des Users aus, statt nur von seiner globalen Rolle. Hat der Nutzer im aktiven Team keine Teamrolle, gilt seine globale role_id.
'teams' => [ 'enabled' => env('USERAUTH_TEAMS_ENABLED', false), ],
Rollen und Rechte
Privilegierte Rollen
Eine Rolle ist privilegiert (hat jedes Recht, erfüllt jede Gate-Prüfung) nur in diesen Fällen:
| Rolle | Bedingung | Bedeutung |
|---|---|---|
| Dev | Rolle mit ID 1 und Schlüssel dev |
Erfüllt jede Rolle und jedes Recht |
| Admin | Rolle mit ID 2 und Schlüssel admin |
Erfüllt jedes Recht und jede Rolle außer dev |
- Eine umbenannte Rolle 1 oder 2 ist nicht privilegiert. Eine Rolle mit Schlüssel
adminund anderer ID ist ebenfalls nicht privilegiert. - Mit Teams gilt die Rolle des aktiven Teams für alle Prüfungen (Gate, Middleware,
isAdmin/isDev, Impersonation). Ohne Teamrolle gilt die globalerole_id. - Fehlt die Rolle (
role_idistnull), ist ungültig (0, negativ) oder zeigt auf eine gelöschte oder nicht vorhandene Rolle, ist der Nutzer nicht privilegiert und hat keine Rechte (fail-closed).
Vergabe von Rollen und Rechten ohne Hierarchie
Entscheidung 0006 (Projekt packages_dev, Ordner .project/doku/decisions/):
- Wer
user_createoderuser_edithat, kann jede existierende Rolle vergeben, auch Dev und Admin, auch an sich selbst. - Wer
role_createoderrole_edithat, kann einer Rolle beliebige Rechte geben, auch solche, die er selbst nicht hat. - Es gibt keine Hierarchie-Prüfung.
- Ausgewählt werden können nur existierende, nicht gelöschte Rollen. Rechte-IDs müssen existieren. Eine Rolle wird mit ihren Rechten in einer Transaktion gespeichert: alles oder nichts.
Konsequenz: Ein Inhaber von user_edit oder role_edit kann sich selbst zum Dev machen und das Passwort jedes Nutzers setzen. Diese Rechte wie Admin-Rechte behandeln und nur Vertrauenspersonen geben.
Rechte in Routen und Views
| Prüfung | Verhalten |
|---|---|
Route hasPermission:a |
Recht a oder Dev/Admin |
Route hasPermissionAnd:a,b |
Rechte a und b (Dev/Admin: immer erlaubt) |
Route hasPermissionOr:a,b |
Recht a oder b |
Route hasRole:x |
Rollenname x (Dev erfüllt jede Rolle, Admin jede außer dev) |
Blade @hasPermissionAnd('a,b') / @hasPermissionOr(['a','b']) |
Komma-Liste und Array sind gleichwertig |
Gate @can('a'), Gate::allows('a') |
wie hasPermission |
Leere, null oder fehlende Rechtenamen gelten als nicht vorhandenes Recht. Sie führen zur Ablehnung, nie zu einem Serverfehler.
Die Route /users verlangt user_read an der Route selbst. Das gilt unabhängig von routes.users_middleware in der veröffentlichten Config. Ohne Recht erhält der Nutzer dieselbe Ablehnung wie bei jeder anderen fehlenden Berechtigung (abhängig von userauth.redirect_on_no_permission: Anmeldung oder Seite no-permission).
Nutzerliste (JSON)
Die AJAX-Liste liefert pro Zeile nur: id, name, email, RoleName und die Spalte action (HTML der Aktionen). Passwort-Hash, Reset-Token, 2FA-Code und 2FA-Ablaufzeit gehören nicht zur Ausgabe. Eigene Erweiterungen der Liste müssen die Felder explizit auswählen, nicht das ganze Model serialisieren.
Passwort zurücksetzen
Entscheidung 0008 (Projekt packages_dev, Ordner .project/doku/decisions/). Der Ablauf läuft über den Laravel-Password-Broker.
- Anfordern:
POST password/sendtocken(Namepassword.sendtocken). Der Nutzer gibt seine E-Mail ein. - Antwort: Bekannte Adresse, unbekannte Adresse und gelöschtes Konto erhalten dieselbe Weiterleitung und dieselbe Meldung. Es entsteht kein Serverfehler.
- Versand: Die Mail geht an die in der Datenbank gespeicherte Adresse, unabhängig von der Schreibweise der Eingabe. Der Versand erfolgt nach der HTTP-Antwort (
defer()). Ein Queue-Worker ist dafür nicht nötig. Die Route trägtInvokeDeferredCallbacks(siehe Pflichten des Hosts). Läuft PHP ohne Server, derdefer()nach der Antwort ausführt (z. B. CLI, Tests), sendet Laravel beim Beenden der Anwendung. - Token: Der Broker erzeugt das Token und speichert nur den Hash in der Tabelle
password_reset_tokens. Tabellenname ausauth.passwords.<broker>.table. Die Migration legt die Tabelle nur an, wenn sie fehlt. - Ablauf:
auth.passwords.<broker>.expire(Default 60 Minuten). Der Reset-Link ist danach ungültig. - Zurücksetzen:
POST password/updatewithtoken(Namepassword.updatewithtoken) mit Token, E-Mail und neuem Passwort. Bei Erfolg:- das Passwort ist geändert,
password_changed_atist gesetzt, - die Passwort-Historie wird fortgeschrieben (nur bei aktiver
userauth.password_policy.enabled), - das Token ist verbraucht; eine zweite Nutzung wird abgelehnt. Falsches, abgelaufenes, fremdes oder nicht-textuelles Token: Hinweis, Passwort bleibt unverändert.
- das Passwort ist geändert,
Drosselung:
| Grenze | Wert | Schlüssel |
|---|---|---|
| Reset-Link je E-Mail | höchstens 1 pro 60 Sekunden | eigene Sperre im Paket, unabhängig von auth.passwords.*.throttle |
| Anfordern je IP | 5 pro Minute | Limiter userauth-passwort-anfordern |
| Zurücksetzen je IP | 5 pro Minute | Limiter userauth-passwort-zuruecksetzen |
Mail: ITHilbert\UserAuth\Mail\PasswortZuruecksetzenLink, Absender aus mail.from.
Zwei-Faktor-Anmeldung
Entscheidung 0009 (Projekt packages_dev, Ordner .project/doku/decisions/). Aktivierung: USERAUTH_2FA_ENABLED=true. Die Zwei-Faktor-Anmeldung gilt für Passwort-Login und Social Login gleichermaßen.
Ablauf
- Passwort oder Google korrekt: Der Nutzer ist noch nicht angemeldet. Das Paket legt eine offene Anmeldung in der Session (Schlüssel
userauth.2fa) an und regeneriert die Session-ID. Es erzeugt einen 6-stelligen Code ausrandom_intund versendet ihn synchron per Mail. Die Antwort wartet also auf den Mailserver. - Code-Eingabe:
GET verify(Nameverify.index) zeigt das Formular,POST verify(Nameverify.store) prüft den Code.- Gültig: 10 Minuten ab Versand.
- Abgelaufen oder falsch: Ablehnung mit Hinweis. Ein abgelaufener Code zählt als Fehlversuch.
- Nach 5 Fehlversuchen ist der Code ungültig, die offene Anmeldung wird verworfen, der Nutzer landet auf der Anmeldung.
- Richtiger Code: Der Nutzer ist angemeldet (neue Session-ID). Er wird zum ursprünglich gewünschten Ziel geleitet. Der Code ist verbraucht. Das Cookie „Angemeldet bleiben“ wird erst jetzt gesetzt. Das Audit-Ereignis
loginwird erst jetzt protokolliert. - Code erneut senden: Nur
POST verify/resend(Nameverify.resend), höchstens einmal pro 60 Sekunden. Der neue Code ersetzt den alten. - Abbrechen oder Abmelden (auch als Gast): Route
logout. Die offene Anmeldung ist verworfen. - Versandfehler (Mailserver nicht erreichbar): kein Serverfehler. Der Nutzer landet auf der Anmeldung mit dem Hinweis, dass der Code nicht versendet werden konnte. Er ist nicht angemeldet.
Vor bestandener 2FA sind Seiten hinter auth nicht erreichbar. Die Routen verify.* sind nur für Gäste erreichbar.
Speicherung der Codes
- Der Code liegt nirgends im Klartext: nicht in der Datenbank, nicht in der Session.
- Gespeichert wird ein HMAC-SHA256 über die zufällige Kennung der offenen Anmeldung und den Code, mit dem Schlüssel
app.key. - Der Vergleich ist zeitkonstant (
hash_equals). - Ein Code gilt nur in der Sitzung, für die er erzeugt wurde. Eine zweite Sitzung mit offener Anmeldung bleibt unangemeldet, wenn die erste bestätigt.
- Fehlversuche und die 60-Sekunden-Sperre zählt der
RateLimiterim Cache je offener Anmeldung. Der Zähler übersteht „Code erneut senden“.
Audit-Log (falls USERAUTH_AUDIT_LOG_ENABLED=true)
login: erst nach bestandener 2FA.two_factor_failed: abgelehnter Code. Eigenes Ereignis, keinlogin.
Bekannte Grenzen
- Fehlversuche zählen je offener Anmeldung. Wer das Passwort kennt, kann pro Anmeldung 5 Codes raten, jeweils mit einer Mail an das Konto. Eine Drosselung je Nutzer über mehrere Anmeldungen hinweg ist nicht umgesetzt.
- Der Versand ist synchron. Ein langsamer Mailserver verzögert die Antwort nach dem Passwort.
Migrationen
| Migration | Wirkung | down() |
|---|---|---|
2019_10_12_000000_create_my_users_table |
legt users an, wenn sie fehlt; sonst ergänzt sie nur fehlende Spalten. Bestehende Nutzer bleiben erhalten. |
ohne Wirkung |
2026_10_23_000000_make_role_id_nullable_on_users_table |
role_id nullable, ohne Standardwert Admin. Nutzer ohne Rolle sind nicht privilegiert. Bestehende Zuordnungen bleiben. |
ohne Wirkung |
2026_10_23_100000_create_password_reset_tokens_table |
legt password_reset_tokens an, wenn sie fehlt |
ohne Wirkung |
2026_10_23_200000_clear_remember_tokens_on_users_table |
leert einmalig remember_token aller Nutzer, auch gelöschter |
ohne Wirkung |
Keine Rückbau-Migrationen: Ob das Paket eine Tabelle angelegt oder nur ergänzt hat, ist beim Zurückrollen nicht feststellbar. Ein Löschen würde die Nutzer des Hosts vernichten. Der alte Zustand (role_id mit Admin als Default) wäre die geschlossene Sicherheitslücke. Die geleerten Angemeldet-bleiben-Tokens lassen sich nicht wiederherstellen (Entscheidungen 0007 und 0009, Projekt packages_dev).
Abhängigkeiten im Überblick
Siehe Voraussetzungen und Abhängigkeiten. Zusätzlich:
| Abhängigkeit | Dev / Laufzeit | Wofür |
|---|---|---|
orchestra/testbench ^10.0 oder ^11.0 |
Dev | eigene Testumgebung |
phpunit/phpunit ^11.5 oder ^12.0 |
Dev | Tests |
| Google OAuth | extern, nur bei USERAUTH_SOCIAL_ENABLED=true |
Social Login |
Upgrade-Hinweise (nächstes Major-Release)
Diese Punkte gelten für Hosts, die auf das nächste Major-Release aktualisieren. Sie sind nach dem Code geprüft.
Berechtigungen und Daten
/usersverlangtuser_read(an der Route). Rollen ohne dieses Recht verlieren den Zugriff auf die Nutzerliste.- Die JSON-Nutzerliste enthält nur noch
id,name,email,RoleNameundaction. - Model-Serialisierung (
toArray,toJson) blendetpassword,remember_token,two_factor_code,two_factor_expires_at,edit_pw_tokenundedit_pw_token_endaus. Ein eigenes$hiddendes Hosts bleibt erhalten. - Dev und Admin gelten nur noch mit ID 1 + Schlüssel
devbzw. ID 2 + Schlüsseladmin. Umbenannte Rollen 1 oder 2 verlieren ihre Privilegien. Mit Teams gilt die Rolle des aktiven Teams. role_idist nullable, ohne Default Admin. Neue Nutzer ohne Rolle haben keine Rechte.- Trait-Methoden
hasPermissionAndundhasPermissionOrerwartenstring|array. Mehrere Argumente (('a', 'b')) sind weiterhin möglich.
Migrationen
7. Die vier Migrationen aus Migrationen haben eine leere down(). migrate:rollback entfernt durch sie keine Spalten und Tabellen. Ältere Paket-Migrationen (2FA-Spalten, Passwort-Richtlinie, Teams, Audit-Log, Social Login) behalten ihren bisherigen Rückbau.
8. Neu hinzugekommen: 2026_10_23_000000_make_role_id_nullable_on_users_table, 2026_10_23_100000_create_password_reset_tokens_table (nur, wenn die Tabelle fehlt) und 2026_10_23_200000_clear_remember_tokens_on_users_table.
9. Die Migration clear_remember_tokens leert remember_token aller Nutzer einmalig. Alle Nutzer, die „Angemeldet bleiben“ genutzt hatten, sind danach einmal abgemeldet. Alte Cookies melden niemanden mehr an.
10. Sitzungen, die vor dem Update über den alten 2FA-Ablauf entstanden sind, werden einmal abgemeldet. Voraussetzung: 2FA ist aktiv und der Nutzer hat noch einen offenen Code in users.two_factor_code. Der Nutzer meldet sich neu an.
Passwort-Reset
11. Die Mail-Klasse App\Mail\ForgottenPassword und ihre View sind entfernt. Den Reset-Link versendet ITHilbert\UserAuth\Mail\PasswortZuruecksetzenLink.
12. Alte Reset-Links (aus edit_pw_token) sind ungültig. Betroffene fordern einen neuen Link an.
13. Der Absender kommt aus mail.from.
14. Das User-Model braucht CanResetPassword (siehe Pflichten des Hosts).
Zwei-Faktor-Anmeldung und Routen
15. verify.resend ist nur noch per POST erreichbar. Die veröffentlichte View resources/views/vendor/userauth/auth/twoFactor.blade.php muss "Code erneut senden" als POST-Formular haben. Ein GET-Link bricht.
16. verify.* ist nur für Gäste erreichbar. logout ist ohne auth-Middleware erreichbar.
17. Das Mailable TwoFactorCode nimmt (code, gueltigMinuten). Host-Overrides des Mailables anpassen.
18. Das Event Login feuert erst nach bestandener 2FA. Listener auf Login werden bei offener 2FA nicht mehr ausgelöst.
19. Der Versand des 2FA-Codes ist synchron. Ein Versandfehler zeigt einen Hinweis auf der Anmeldung statt eines Serverfehlers.
Entfernt
20. Trait TwoFactorAuthenticatable sowie die Methoden generateTwoFactorCode() und resetTwoFactorCode() von UserAuth.
21. Contract AuthSession und Klasse SessionAuth.
22. Hook authenticated() im LoginController. Die Anmeldung läuft über den Controller und den UseCase CompleteAuthentication.
Konfiguration, Views und Sprache
23. Das User-Model wird über auth.providers.users.model aufgelöst. Fehlt der Eintrag, wirft das Paket UserModelMisconfiguredException.
24. Der Standard-Guard muss ein Session-Guard sein (siehe Pflichten des Hosts).
25. Der Cache-Store darf nicht array sein.
26. Neue Sprachdateien zwei_faktor.php (de und en) und Schlüssel in password.php (de und en). Hosts mit veröffentlichten Sprachdateien unter lang/vendor/userauth/{de,en}/ übernehmen sie.
27. Nach dem Update php artisan view:clear ausführen.
Social Login (Google)
Das Package unterstützt Social Login über Google mit sicherer Account-Verknüpfung für bestehende Nutzer.
Installation & Vorbereitung
Abhängigkeiten sind bereits in der composer.json des Packages enthalten:
composer require laravel/socialite
Datenbank: Führe folgende Migration aus, um die neuen Tabellen zu erstellen:
php artisan migrate
Dies erzeugt zwei neue Tabellen:
user_social_accounts— verknüpfte Social-Konten pro Useruser_pending_social_links— Bestätigungstokens für unverifiziierte Accounts
Betrieb — Abgelaufene Bestätigungstokens löschen:
Das Package registriert automatisch einen täglich geplanten Job (userauth:prune-social-links), der abgelaufene Einträge aus user_pending_social_links löscht. Damit dieser Job ausgeführt wird, benötigst du einen laufenden Laravel Scheduler:
* * * * * php artisan schedule:run >> /dev/null 2>&1
Diese Cron-Zeile sollte in dein System-Crontab eingetragen werden (typischerweise crontab -e oder per Hoster-Control-Panel). Sie ruft den Scheduler jede Minute auf; der Scheduler bestimmt selbst, welche Jobs fällig sind.
Ohne laufenden Scheduler können alte Bestätigungstokens in der Datenbank verbleiben. Die Tokens selbst sind mit konfigurierbarer TTL (Default: 60 Minuten) weiterhin ungültig; das Löschen ist eine Datenbankwartung.
Google Cloud Console Setup
- Gehe zu Google Cloud Console und erstelle einen neuen OAuth 2.0 Client (Typ: Web Application).
- Konfiguriere die Redirect-URI exakt als:
{APP_URL}/auth/google/callback - Aktiviere die Scopes (minimal erforderlich):
openidemailprofile
- Konfiguriere den OAuth Consent Screen mit deinen Datenschutzerklärung- und Impressum-Links (erforderlich für Production).
Host-App Konfiguration
Füge deine Google OAuth-Credentials in die .env Datei ein:
GOOGLE_CLIENT_ID=your-client-id-here GOOGLE_CLIENT_SECRET=your-client-secret-here
Registriere diese in deiner config/services.php:
'google' => [ 'client_id' => env('GOOGLE_CLIENT_ID'), 'client_secret' => env('GOOGLE_CLIENT_SECRET'), ],
Wichtig: Die Callback-URL (redirect-Key) ist nicht erforderlich. Das Package registriert die Route userauth.social.callback selbstständig und verwendet sie als Callback-URL gegenüber Google.
Feature-Konfiguration
Aktiviere Social Login in .env:
# Social Login Master-Schalter USERAUTH_SOCIAL_ENABLED=true # Registrierung von neuen Usern per Social Login erlauben (Default: aus) USERAUTH_SOCIAL_REGISTRATION=false # Standard-Rolle für neue Social-Nutzer (Rollen-Key, z.B. 'user') USERAUTH_SOCIAL_DEFAULT_ROLE=user # Optionale Domain-Beschränkung (Komma-getrennt, leer = alle erlaubt) # USERAUTH_SOCIAL_ALLOWED_DOMAINS=company.com,partner.com # Gültigkeitsdauer von Bestätigungstokens (Minuten) # USERAUTH_SOCIAL_PENDING_LINK_TTL=60
Oder verwende die veröffentlichte config/userauth.php:
'social' => [ 'enabled' => env('USERAUTH_SOCIAL_ENABLED', false), 'allow_registration' => env('USERAUTH_SOCIAL_REGISTRATION', false), 'default_role' => env('USERAUTH_SOCIAL_DEFAULT_ROLE', 'user'), 'allowed_domains' => explode(',', env('USERAUTH_SOCIAL_ALLOWED_DOMAINS', '')), 'pending_link_ttl_minutes' => env('USERAUTH_SOCIAL_PENDING_LINK_TTL', 60), ],
Verfügbarkeit von Social-Login Providern
Der Google-Login-Button (auf der Login-Seite) und der Verbinden-Link (auf der Profil-/Kontenseite) erscheinen nur wenn folgende Bedingungen erfüllt sind:
USERAUTH_SOCIAL_ENABLED=trueist aktiviert UNDGOOGLE_CLIENT_IDundGOOGLE_CLIENT_SECRETin.envgesetzt sind
Fehlen die Credentials, werden folgende Fehler geloggt (ohne Werte, nur Feldnamen):
Missing configuration keys: google.client_id, google.client_secret
Dies hilft bei der Fehlersuche. Die UI blendet den Button/Link aus und zeigt keinen visuellen Fehler.
Technische Fehler (z.B. Google nicht erreichbar) während des Login/Verbinden führen den User zurück zur Login-Seite (oder Profil-Seite) mit der Meldung userauth::social.errors.provider_unavailable (benutzersichtbar). Debug-Details landen nur im Log (nicht in der UI).
Views müssen Variable availableSocialProviders prüfen:
Die Views (Login, Profil) erhalten vom View-Composer AvailableSocialProvidersComposer die Variable availableSocialProviders (Array von Provider-Keys). Nutze diese, um zu prüfen, ob ein Provider verfügbar ist:
@if(in_array('google', $availableSocialProviders)) <a href="{{ route('userauth.social.redirect', 'google') }}"> Mit Google anmelden </a> @endif
Ablauf & Sicherheit (für Integratoren)
Gast-Flow (Neuer Login per Google)
- Domain-Prüfung: Falls
allowed_domainsgesetzt, wird die E-Mail-Domain + Googlehd-Claim geprüft. - E-Mail-Verifikation: Google muss
email_verified = truemelden, sonst wird der Login mit Begründung abgelehnt. - Erkennung: System prüft über
(provider, provider_user_id)(Googlesub), nicht über E-Mail. - Bestehendes Konto:
- Bereits verknüpft → Login, danach 2FA, falls aktiv (Nutzer ist vor dem Code nicht angemeldet).
- Nicht verknüpft, aber lokale E-Mail vorhanden:
- E-Mail ist lokal bestätigt (
email_verified_atgesetzt) → sofort verknüpfen + Login (danach 2FA, falls aktiv). - E-Mail ist NOT bestätigt → Bestätigungsmail an die lokale E-Mail. User klickt Link, wird verknüpft,
email_verified_atwird gesetzt. Kein automatischer Login nach Bestätigung.
- E-Mail ist lokal bestätigt (
- Keine lokale E-Mail:
allow_registration = true→ neuer User (zufälliges Passwort-Hash, Standard-Rolle, Login).allow_registration = false→ Fehler mit verständlicher Meldung.
- Gelöschter User: Hat jemand einen Account mit dieser E-Mail gelöscht, wird ein neuer Social-Nutzer nicht automatisch wiederverknüpft.
Bestätigungsablauf (Unverifiziierte E-Mail)
- GET
auth/social/confirm/{token}— zeigt Bestätigungsseite (keine Login). - POST
auth/social/confirm/{token}— verknüpft das Konto, setztemail_verified_at, loggt NICHT ein. - Token ist einmalig gültig, mit konfigurierbarer Gültigkeitsdauer (Default: 60 Minuten).
2FA nach Social Login
Wenn der User two_factor_enabled hat, wird nach dem Google-Login der Code-Flow ausgelöst. Das ist dieselbe Logik wie beim Passwort-Login (siehe Zwei-Faktor-Anmeldung).
Konto-Verwaltung
- GET
social-accounts— listet alle verknüpften Social-Konten (nur für eingeloggte User). - DELETE
social-accounts/{provider}— entfernt eine Verknüpfung. Passwort bleibt erhalten als Fallback-Methode.
Benachrichtigungen & Audit
E-Mails (queued):
SocialLinkConfirmation— Bestätigung für unverifiziierte Konten (GET-Link mit Token).SocialAccountLinked— Info-Mail bei jeder neuen Verknüpfung (auch per Bestätigungsmail).
Audit-Log (falls USERAUTH_AUDIT_LOG_ENABLED=true):
- Event
social_linked— neue Verknüpfung. - Event
social_unlinked— Verknüpfung entfernt.
Wichtig: Queue-Worker & Scheduler
Queue-Worker für Social-Login-Mails:
Die Bestätigungsmails und Info-Mails sind als ShouldQueue definiert. Damit sie versendet werden, muss ein Queue-Worker laufen:
php artisan queue:work
Ohne laufenden Worker werden die Mails in der Queue verbleiben und nicht versendet. Passwort-Reset und 2FA-Codes brauchen keinen Worker (siehe oben).
Scheduler für Wartung:
Abgelaufene Bestätigungstokens werden durch den eingebauten Job userauth:prune-social-links täglich gelöscht (siehe Abschnitt Installation & Vorbereitung oben).
DSGVO / Datenschutz
- Externe Service: Google wird als Authentifizierungsprovider genutzt. Dies muss in deiner Datenschutzerklärung dokumentiert sein (Zweck: Authentifizierung, Datenempfänger: Google LLC).
- User-Kontrolle: Verknüpfungen können jederzeit im Profil unter
social-accountsentfernt werden. - Audit-Trail: Alle Verknüpfungen und Entfernungen werden im Audit-Log protokolliert (wenn aktiv).
Routen-Referenz
| Methode | Route | Name | Beschreibung |
|---|---|---|---|
| GET | auth/{provider}/redirect |
userauth.social.redirect |
Weiterleitung zu Google OAuth |
| GET | auth/{provider}/callback |
userauth.social.callback |
Callback nach Google OAuth |
| GET | auth/social/confirm/{token} |
userauth.social.confirm |
Bestätigungsseite anzeigen |
| POST | auth/social/confirm/{token} |
userauth.social.confirm.store |
Bestätigung verarbeiten |
| GET | social-accounts |
userauth.social.accounts |
Verknüpfte Konten auflisten |
| DELETE | social-accounts/{provider} |
userauth.social.disconnect |
Verknüpfung entfernen |
Alle Routen sind unter throttle-Middleware geschützt. Falls USERAUTH_SOCIAL_ENABLED=false, geben alle Social-Routen einen 404-Fehler zurück.
Zusätzlich für Anmeldung und Passwort:
| Methode | Route | Name | Beschreibung |
|---|---|---|---|
| GET | verify |
verify.index |
Code-Eingabe (nur Gäste mit offener Anmeldung) |
| POST | verify |
verify.store |
Code prüfen |
| POST | verify/resend |
verify.resend |
Code erneut senden (1× je 60 Sekunden) |
| ANY | logout |
logout |
Abmelden bzw. offene 2FA abbrechen |
| POST | password/sendtocken |
password.sendtocken |
Reset-Link anfordern (5/Min. je IP) |
| POST | password/updatewithtoken |
password.updatewithtoken |
Passwort mit Token setzen (5/Min. je IP) |
Namespace
ITHilbert\UserAuth