Why I'm Writing This#
I've worked on survey tools, health trackers, and account dashboards. The common friction point was authentication. Here are the approaches that held up in practice, with working snippets.
The Pain Points I Ran Into#
- LocalStorage tokens broke under SSR and subdomains.
- CORS misconfigurations silently blocked cookies.
- Safari’s cookie rules exposed bugs Chrome hid.
- Service-to-service trust needed signed requests, not user cookies.
- Public forms attracted bots until I hardened them.
Nuxt 3 (Vue)#
export function useAuth() {
const user = useState('user', () => null as any);
async function login(email: string, password: string) {
await $fetch('/api/login', {
method: 'POST',
body: { email, password },
credentials: 'include',
});
await refreshUser();
}
async function refreshUser() {
user.value = await $fetch('/api/me', { credentials: 'include' }).catch(() => null);
}
return { user, login, refreshUser };
}Fact learned: Cookies work naturally with SSR. Avoid storing tokens in LocalStorage: they cause hydration bugs and add XSS risk.
Laravel 10#
Route::post('/login', function (Request $request) {{
$credentials = $request->validate([
'email' => 'required|email',
'password' => 'required|string|min:6',
]);
if (! Auth::attempt($credentials, true)) {{
return response()->json(['message' => 'Invalid credentials'], 422);
}}
$request->session()->regenerate();
return response()->noContent();
}});
Route::get('/me', fn (Request $r) => $r->user())->middleware('auth:sanctum');Fact learned: Sanctum is built for SPAs/SSR. Regenerate sessions on login/logout to prevent fixation.
Express.js#
import crypto from 'crypto';
function sign(body, secret) {{
return crypto.createHmac('sha256', secret).update(body).digest('hex');
}}
function verify(req, res, next) {{
const sig = req.header('x-signature');
const expected = sign(req.rawBody, process.env.S2S_SECRET);
if (sig !== expected) return res.status(401).send('Invalid signature');
next();
}}Fact learned: Don’t pass user cookies between services. Use HMAC or short-lived JWTs; rotate secrets.
React#
async function login(email, password) {{
await fetch('/api/login', {{
method: 'POST',
credentials: 'include',
headers: {{ 'Content-Type': 'application/json' }},
body: JSON.stringify({{ email, password }}),
}});
}}Fact learned: Cookies still win for browser apps. With Next.js, SSR can read the cookie server-side.
Controls That Actually Worked#
- Rate limiting for login/signup endpoints.
- Honeypot fields that bots fill but humans ignore.
- Analytics on failed logins to surface UX issues early.
- SameSite=Lax + Secure cookies to avoid silent drops in Safari.
Final Thoughts#
- Nuxt / React: Cookie sessions are safer and SSR-friendly than LocalStorage tokens.
- Laravel: Sanctum + sessions is the simplest, reliable path for SPA/SSR.
- Express: Prefer HMAC/JWT for service-to-service; never forward user cookies.
- Browsers: Test in Safari early; its stricter rules expose hidden bugs.
- Security + UX: Pair rate limits + honeypots with analytics; passwords alone aren’t “secure.”



