How do you implement secure authentication and authorization?

Security

The 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-side
const bcrypt = require('bcrypt');
// When user registers
const hash = await bcrypt.hash(password, 10);
// Store hash in database
// When user logs in
const 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=Strict
  • HttpOnly — JavaScript cannot read the cookie (protects against XSS)
  • Secure — Only sent over HTTPS
  • SameSite=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 check
if (user.role === 'admin') {
showDeleteButton();
}
// Good — server checks too
app.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.