sven/super-basic-auth
A super simple Laravel package for HTTP Basic authentication. Protect routes with middleware and validate against configurable credentials—no database, no login forms, just lightweight basic auth.
What it is: a single-middleware package that puts an HTTP Basic Auth prompt in front of your Laravel routes, checking credentials against config/env values instead of a users table. It is for gating access to an environment, not for authenticating real users.
Install and wire up (5 minutes):
composer require sven/super-basic-auth
php artisan vendor:publish --tag=config # publishes config/super-basic-auth.php
Set credentials in .env (never in the repo):
SUPER_BASIC_AUTH_USERNAME=preview
SUPER_BASIC_AUTH_PASSWORD='a-long-random-string'
Where to look first, in order:
config/super-basic-auth.php — confirm the exact key names and whether it supports multiple users or a single credential pair.super-basic-auth, basic.auth, etc.) and whether it auto-applies to anything.vendor/sven/super-basic-auth/src/ — the middleware is ~30 lines; read it once so you know exactly what it compares and what it returns on failure.First real use case: protect a staging site. Add the middleware to the web group in bootstrap/app.php (Laravel 11+) or app/Http/Kernel.php (10 and below):
// Laravel 11: bootstrap/app.php
->withMiddleware(function (Middleware $middleware) {
$middleware->web(append: [\Sven\SuperBasicAuth\Middleware\SuperBasicAuth::class]);
})
// Laravel 10: app/Http/Kernel.php
protected $middlewareGroups = [
'web' => [
// ...
\Sven\SuperBasicAuth\Middleware\SuperBasicAuth::class,
],
];
Load the site, get a browser prompt, enter the credentials, done. If you get a 401 with no prompt, the config isn't being read — check config:cache.
Environment-scoped gating (the intended pattern). Only set the env vars on environments that need protection — staging, UAT, client preview, demo boxes. Leave them unset locally and in production so the middleware no-ops (verify this behaviour in the source; if it hard-fails when unset, guard it with an if in your middleware registration instead of relying on the package).
Route-group scoping. Instead of the whole app, apply it to specific groups — admin panels, prototypes, a /docs route — so public marketing pages stay untouched:
Route::middleware('super-basic-auth')->group(function () {
Route::get('/preview', PreviewController::class);
});
Layering with real auth. Basic auth is a cheap outer wall; the app's own auth middleware still runs inside it. Order matters: list basic auth before auth so unauthenticated requests are stopped at the door rather than hitting session/DB work. This is a useful pattern for "internal tools behind a shared password, per-user logins inside."
Uptime monitors and health checks. Your monitoring service won't send credentials. Exclude the health endpoint:
Route::get('/up', fn () => response('ok'))->withoutMiddleware(SuperBasicAuth::class);
Or scope the middleware to a route group that excludes /up and /livewire/* (Livewire's endpoint also breaks under basic auth if hit from a page the browser cached credentials for — usually fine, but worth testing).
Preview deployments in CI. Inject the credentials at deploy time rather than committing them: Forge/Envoyer/Vapor env vars, GitHub Actions secrets written into .env, or a php artisan tinker --execute step. Pair with php artisan config:cache in the deploy script so the values are baked in and the config file read is skipped per-request.
API testing convenience. Because it's plain Authorization: Basic ..., curl/Postman/HTTP clients work with no extra setup:
curl -u "$SUPER_BASIC_AUTH_USERNAME:$SUPER_BASIC_AUTH_PASSWORD" https://staging.example.com/api/thing
That also makes it easy to script smoke tests against a protected environment.
Combining with throttle. Basic auth does no rate limiting or lockout. If the host is publicly reachable, add throttle:30,1 next to it, otherwise credential guessing is unlimited (albeit slow over the network).
Config caching will bite you. Change SUPER_BASIC_AUTH_PASSWORD in .env, forget php artisan config:cache, and nothing changes. On any environment with cached config, make the cache rebuild part of the deploy — and remember config:clear locally after editing env values.
Special characters in .env. # starts a comment, spaces need quoting, and $ is interpolated. Always single-quote passwords: SUPER_BASIC_AUTH_PASSWORD='p@ss$word#1'. A silently truncated password is the #1 cause of "the credentials are right but it 401s."
HTTPS only. Basic auth sends base64-encoded credentials on every request — trivially decodable on the wire. Never apply this middleware to an HTTP-only host, and never reuse a password that matters elsewhere.
No logout. Browsers cache Basic credentials for the session/realm. Users can't "log out" without closing the browser or using a private window, and some browsers keep sending the header after a 401. If you need revocation, you can't have it — rotate the password in env and redeploy. Tell stakeholders this up front; it's the most common complaint with preview links shared to clients.
401 vs. redirect. Laravel's auth middleware returns a redirect to a login page; this one returns a bare 401 with a WWW-Authenticate header. If a feature test expects a redirect, it will fail — assert assertUnauthorized() / check the WWW-Authenticate: Basic header instead.
Middleware ordering. If basic auth is appended to the web group after StartSession/ShareErrorsFromSession, sessions and CSRF run before the gate — wasteful and occasionally confusing (you'll see sessions created for rejected requests). Prefer prepend or a route-group-level assignment over append when you care.
Laravel version compatibility. v2.6.0 exists purely to restore Laravel 9 & 10 support — there are no new features or behaviour changes, so nothing in the usage patterns above shifts. Note what it does not claim: the release notes say nothing about Laravel 11/12, so the earlier advice still holds — check composer.json constraints before installing there. The middleware itself is trivial; the risk is the service provider using APIs that moved. If the alias isn't picked up on Laravel 11, register it manually in bootstrap/app.php:
->withMiddleware(fn (Middleware $m) => $m->alias('super-basic-auth', SuperBasicAuth::class))
Because this release only re-widens the version floor, treat the constraint as something to pin deliberately. If you're on Laravel 10 and already running an older 2.x, bumping to ^2.6 is a safe patch-level move; if you're on 11/12, the release gives you no new information and you're still relying on the middleware being framework-agnostic enough to work unaliased.
Single credential pair only. Don't try to model teams, roles, or per-user auditing on top of this — there is no user identity beyond the username string. If requirements grow even slightly ("each client gets their own login"), replace it with Laravel's built-in auth.basic against a users table or a proper package; the migration is one middleware swap because the HTTP contract is identical.
Extension points are thin, by design. If you need to trim the password, look up credentials from a database, or log failed attempts, don't fork — wrap it. Write a three-line middleware that checks your own condition and either returns abort(401, headers: ['WWW-Authenticate' => 'Basic realm="..."']) or calls $next($request). That keeps the package as a reference implementation rather than a dependency you're stuck with.
Testing. In feature tests, pass the header explicitly rather than fighting the browser prompt:
$this->withHeaders([
'Authorization' => 'Basic '.base64_encode(config('super-basic-auth.username').':'.config('super-basic-auth.password')),
])->get('/preview')->assertOk();
And add one test asserting the unauthenticated case returns 401 — that's the behaviour you actually care about preserving when you refactor middleware registration.
How can I help you explore Laravel packages today?