Without a kit, a login is one Auth call. The Getting Started with Laravel skeleton gets this controller, a Blade form at GET /login (the route auth sends guests to), and a /whoami route that returns the start of the session ID, Auth::check(), Auth::user()?->email and Auth::viaRemember().
public function store(Request $request): RedirectResponse
{
$credentials = $request->validate([
'email' => ['required', 'email'],
'password' => ['required', 'string'],
]);
if (! Auth::attempt($credentials, $request->boolean('remember'))) {
return back()->withErrors(['email' => __('auth.failed')])->onlyInput('email');
}
$request->session()->regenerate();
return redirect()->intended('/account');
}curl -s $J -o /dev/null -w "$W" $B/account
T=$(tok /login); curl -s $J $B/whoami; echo
curl -s $J -o /dev/null -w "$W" -d "_token=$T&$P=correct-horse-9" $B/login
curl -s $J $B/whoami; echo; curl -s $J $B/account; echo302 http://127.0.0.1:8314/login
{"session":"eVtss69m","check":false,"user":null,"via_remember":false}
302 http://127.0.0.1:8314/account
{"session":"DObhFOQB","check":true,"user":"ada@example.com","via_remember":false}
Hello, Adaattempt() turns every credential except password into a where clause, checks the password, rehashes it if needed and calls login(), inside a Timebox so that a missing user and a wrong password take equally long. intended() returned Ada to the URL auth had saved. The session ID changed because SessionGuard::login() itself calls regenerate(true), which defeats session fixation. Auth::login($user) skips the password, Auth::once() authenticates one request without a session, and Auth::attemptWhen($credentials, fn ($u) => ! $u->is_banned) adds a check.