How do you implement secure authentication and authorization?
SecurityThe short answer
Authentication verifies who you are (login). Authorization determines what you can do (permissions). Secure authentication uses hashed passwords, HTTPS, and httpOnly cookies or secure tokens. Secure authorization checks permissions on the server for every request — never trust the client.
Authentication vs authorization
- Authentication — "Who are you?" The user proves their identity with a username/password, OAuth, or biometrics.
- Authorization — "What can you do?" The system checks if the authenticated user has permission to access a resource or perform an action.
Authentication always comes first. You cannot authorize someone you have not authenticated.
Secure authentication practices
1. Never store passwords in plain text
Always hash passwords with a strong algorithm like bcrypt:
// Server-sideconst bcrypt = require('bcrypt');// When user registersconst hash = await bcrypt.hash(password, 10);// Store hash in database// When user logs inconst isValid = await bcrypt.compare(password, storedHash);2. Use HTTPS everywhere
Without HTTPS, passwords and tokens are sent in plain text over the network. Anyone on the same network can read them.
3. Store tokens securely
For session-based auth, use httpOnly cookies:
Set-Cookie: session=abc123; HttpOnly; Secure; SameSite=StrictHttpOnly— JavaScript cannot read the cookie (protects against XSS)Secure— Only sent over HTTPSSameSite=Strict— Not sent with cross-site requests (protects against CSRF)
For JWT-based auth, prefer httpOnly cookies over localStorage. Storing JWTs in localStorage makes them accessible to any script on the page.
4. Implement rate limiting
Limit login attempts to prevent brute-force attacks. After 5 failed attempts, require a CAPTCHA or lock the account temporarily.
Secure authorization practices
1. Always check permissions on the server
Never rely on the frontend to enforce authorization. A user can modify the frontend or call your API directly.
// Bad — client-side only checkif (user.role === 'admin') { showDeleteButton();}// Good — server checks tooapp.delete('/api/users/:id', (req, res) => { if (req.user.role !== 'admin') { return res.status(403).json({ error: 'Forbidden' }); } // proceed with deletion});2. Use the principle of least privilege
Give users the minimum permissions they need. Do not give everyone admin access "just in case."
3. Check ownership, not just role
A logged-in user should only access their own data, not other users' data:
app.get('/api/orders/:id', (req, res) => { const order = getOrder(req.params.id); if (order.userId !== req.user.id) { return res.status(403).json({ error: 'Forbidden' }); } res.json(order);});Common Pitfalls
A common mistake is hiding UI elements (like admin buttons) and thinking that is enough for authorization. It is not. Hiding the button does not prevent someone from calling the API directly. Always enforce authorization on the server. The frontend can hide things for better UX, but the server is the real gatekeeper.
Interview Tip
Start by clearly defining authentication vs authorization — many candidates mix them up. Then cover the key practices: hashed passwords, HTTPS, httpOnly cookies, and server-side permission checks. The most important point is that authorization must happen on the server, never just the client.
Why interviewers ask this
Authentication and authorization are critical for any application that has users. Interviewers want to see if you understand the difference between the two, if you know the common security mistakes, and if you can explain why server-side enforcement is necessary. This is especially important for frontend developers who build login flows and access-controlled UIs.