akira/laravel-auth-logs
Laravel package for tracking authentication events such as logins, logouts, and failed attempts. It records the user, IP address, device/user agent, and timestamps in a configurable database table, making account activity easy to audit and suspicious access easier to investigate.
At 7 stars and no dependents, treat this as a small, readable package where the source is the documentation. Budget 20 minutes to read src/ before wiring it in.
Install and publish. composer require akira/laravel-auth-logs, then publish the config and migration:
php artisan vendor:publish --provider="Akira\AuthLogs\AuthLogsServiceProvider"
php artisan migrate
If the provider isn't auto-discovered (older Laravel or a non-standard namespace), register it manually in config/app.php.
Confirm the listeners are wired. The package advertises automatic recording of successful and failed sign-ins. It does this by listening to Laravel's Illuminate\Auth\Events\Login and Illuminate\Auth\Events\Failed. Check src/ for the listener registrations and verify that the events you care about (e.g. Logout, Lockout, PasswordReset) are actually covered — don't assume.
Find the model and table. Locate the AuthLog (or similarly named) Eloquent model and the migration's table name. This is your primary interface for everything downstream.
First use case: a "recent sign-ins" panel. The fastest win is querying the model directly:
use Akira\AuthLogs\Models\AuthLog;
$recent = AuthLog::latest()->take(20)->get();
Render this on an admin page next to the user's current session. For a security-conscious app, this single widget justifies the install.
Verify ingestion. Log in and out a few times, trigger a bad password, then AuthLog::count() — if it's still zero, the event listeners aren't firing and you should stop and debug that before building features on top.
Query scopes for common questions. Rather than scattering where() chains, expect (or add) scopes for the recurring queries: failed attempts in a window, activity by user, activity by IP. If the package ships scopes, prefer them; if not, add them locally in a subclass.
// Failed attempts in the last hour, grouped by IP — credential stuffing signal
AuthLog::where('event', 'failed')
->where('created_at', '>=', now()->subHour())
->selectRaw('ip_address, count(*) as attempts')
->groupBy('ip_address')
->having('attempts', '>', 10)
->get();
Notify on suspicious events, not every login. The package supports optional notifications. Wire notifications to failed attempts and new-device logins, not successful ones from known devices — otherwise you train users to ignore them. Use a queued notification (implements ShouldQueue) so the auth flow isn't blocked by mail delivery.
Admin security dashboard. Combine with your user model:
$user->authLogs()->latest()->paginate(50);
$failedToday = AuthLog::failed()->whereDate('created_at', today())->count();
Surface "last login IP / time / device" on the user profile page — this is the highest-signal, lowest-effort placement.
Geolocation as enrichment, not truth. The geolocation context is derived from IP. Use it to flag "login from a country you've never signed in from" and to display a friendly location in the UI, but never use it for access control. Store the raw IP too so you can re-resolve later if the geo provider changes.
Custom actions on detection. If you want to lock accounts after N failed attempts, do it in your own listener reacting to the package's records (or to Laravel's Failed event directly). Keep the reactive logic in your app, not the package — you'll want different thresholds per environment (e.g. disabled in local/test).
Retention. Auth logs grow unbounded. Add a scheduled prune in app/Console/Kernel.php and configure the retention window from the package config if supported:
$schedule->command('model:prune', ['--model' => [AuthLog::class]])->daily();
The recorded IP may be your proxy, not the user. Laravel's $request->ip() trusts TrustProxies configuration. Behind a load balancer or CDN, auth logs will show the balancer IP unless you configure trusted proxies correctly. Verify with a real external login before trusting any IP data — this is the most common reason auth log data is useless in production.
Behind a queue, notifications lag. Queued notifications mean a failed-login alert may arrive minutes later. For "someone is attacking your account" alerts, consider onConnection('sync') for just that notification, or accept the delay knowingly.
Geolocation adds an outbound HTTP call. If the package resolves IPs synchronously inside the login listener, every login pays the latency, and a provider outage can break auth. Check whether it's queued; if not, either disable the feature and enrich asynchronously, or configure a local MaxMind database instead of a remote API. Also confirm what happens to private/reserved IPs (local dev will log 127.0.0.1 and may throw or record empty location).
Failed logins include non-existent users. Illuminate\Auth\Events\Failed fires even when the email doesn't exist. Check whether the package stores the attempted email/username — if so, be deliberate about retention and access, since it's PII even though no account matched.
Don't log credentials. Confirm the package does not persist passwords or session tokens. Read the migration and model $fillable/$casts; a small package can still surprise you here, and this is the kind of thing that fails a security review.
Small-codebase extension points. With no dependents and limited docs, expect to extend rather than configure. Typical seams: a custom model (swap via config/auth-logs.php or service-container binding), a custom listener registration, and a toArray()/toJson() shape you may want to override. If the model isn't swappable, fork or wrap — don't edit vendor/.
Testing. Use Event::fake() carefully — faking Login/Failed will prevent the package's listeners from running, so your auth-log assertions will silently pass with zero records. If you need to assert records were created, either don't fake those events in that test or call the listeners explicitly.
Config quirks to check on first read: which events are enabled, whether notifications default to on (and to whom), the geo provider and its key/endpoint expectations, the table name (collides with existing auth_logs if you're migrating from a homemade solution), and whether the migration is published vs. loaded from the package (re-running it after an upgrade can drop data).
Version pinning. Last release is dated 2026-06-26 with a single maintainer and low adoption. Pin an exact version in composer.json and read the changelog before upgrading; you're the most likely person to hit a regression.
How can I help you explore Laravel packages today?