Authentication vs Authorization: Understanding the Two Pillars of Security
Security in modern systems relies on two fundamental concepts: authentication and authorization. While often used interchangeably, they serve entirely different purposes. Authentication answers "Who are you?" while authorization answers "What can you do?". Understanding this distinction is critical for building secure applications and acing system design interviews.
What is Authentication?
Authentication is the process of verifying a user's identity. It confirms that someone is who they claim to be before granting any access. Think of it as showing your passport at airport security — you're proving your identity.
Common Authentication Methods
| Method | How It Works | Security Level | Use Case |
|---|---|---|---|
| Password-Based | User provides username + password | Low-Medium | Most web applications |
| Multi-Factor (MFA) | Combines 2+ factors: something you know, have, or are | High | Banking, enterprise apps |
| Biometric | Fingerprint, face scan, iris recognition | High | Mobile devices, physical access |
| SSO (Single Sign-On) | One login grants access to multiple services | Medium-High | Enterprise ecosystems (Google Workspace) |
| Certificate-Based | Digital certificates verify identity via PKI | Very High | Service-to-service, mTLS |
| Token-Based | JWT or opaque tokens after initial login | Medium-High | APIs, SPAs, mobile apps |
Password-Based Authentication with Hashing
Never store passwords in plain text. Always use a slow hashing algorithm like bcrypt or Argon2. Here is how password hashing works in Node.js:
const bcrypt = require('bcrypt');
const SALT_ROUNDS = 12;
// Registration: Hash the password before storing
async function registerUser(email, password) {
const hashedPassword = await bcrypt.hash(password, SALT_ROUNDS);
await db.users.insert({ email, password: hashedPassword });
}
// Login: Compare provided password with stored hash
async function loginUser(email, password) {
const user = await db.users.findByEmail(email);
if (!user) throw new Error('User not found');
const isValid = await bcrypt.compare(password, user.password);
if (!isValid) throw new Error('Invalid credentials');
return generateToken(user); // Issue JWT or session
}
Use our Security and Crypto Tools to experiment with hashing algorithms and see how different inputs produce different hash outputs.
Multi-Factor Authentication (MFA)
MFA combines two or more independent authentication factors:
- Something you know — password, PIN, security question
- Something you have — phone (TOTP app), hardware key (YubiKey), smart card
- Something you are — fingerprint, face recognition, voice pattern
const speakeasy = require('speakeasy');
// Generate TOTP secret during MFA enrollment
function generateMfaSecret(userId) {
const secret = speakeasy.generateSecret({
name: `MyApp:${userId}`,
issuer: 'MyApp'
});
// Store secret.base32 in database
return { secret: secret.base32, qrUrl: secret.otpauth_url };
}
// Verify TOTP code during login
function verifyMfaCode(storedSecret, userCode) {
return speakeasy.totp.verify({
secret: storedSecret,
encoding: 'base32',
token: userCode,
window: 1 // Allow 30-second clock drift
});
}
What is Authorization?
Authorization determines what an authenticated user is allowed to do. After proving identity, the system checks permissions to decide if the requested action is permitted. This is like having a boarding pass after showing your passport — it determines which flight and seat you can access.
Authorization Models
Role-Based Access Control (RBAC)
RBAC assigns permissions to roles, and roles are assigned to users. It is the most common authorization model in enterprise applications.
| Role | Permissions | Example Users |
|---|---|---|
| Admin | Create, Read, Update, Delete, Manage Users | System administrators |
| Editor | Create, Read, Update | Content managers |
| Viewer | Read only | Regular users |
// RBAC middleware in Express.js
function authorize(...allowedRoles) {
return (req, res, next) => {
const userRole = req.user.role; // Set during authentication
if (!allowedRoles.includes(userRole)) {
return res.status(403).json({ error: 'Insufficient permissions' });
}
next();
};
}
// Usage in routes
app.get('/api/users', authenticate, authorize('admin'), getUsers);
app.get('/api/posts', authenticate, authorize('admin', 'editor', 'viewer'), getPosts);
app.post('/api/posts', authenticate, authorize('admin', 'editor'), createPost);
app.delete('/api/posts/:id', authenticate, authorize('admin'), deletePost);
Attribute-Based Access Control (ABAC)
ABAC makes authorization decisions based on attributes of the user, resource, action, and environment. It is more flexible than RBAC but more complex to implement.
// ABAC policy evaluation
function evaluatePolicy(subject, resource, action, environment) {
const policies = [
{
name: 'Doctors can view patient records in their department',
condition: (s, r, a, e) =>
s.role === 'doctor' &&
r.type === 'patient_record' &&
a === 'read' &&
s.department === r.department
},
{
name: 'Only during business hours for non-emergency',
condition: (s, r, a, e) =>
r.priority === 'emergency' ||
(e.hour >= 8 && e.hour <= 18)
}
];
return policies.every(p => p.condition(subject, resource, action, environment));
}
RBAC vs ABAC Comparison
| Feature | RBAC | ABAC |
|---|---|---|
| Complexity | Simple to implement | Complex, requires policy engine |
| Granularity | Role-level (coarse) | Attribute-level (fine-grained) |
| Scalability | Role explosion with many permissions | Scales well with dynamic policies |
| Context-Aware | No (static roles) | Yes (time, location, device) |
| Best For | Standard enterprise apps | Healthcare, finance, government |
Key Differences: Authentication vs Authorization
| Aspect | Authentication | Authorization |
|---|---|---|
| Purpose | Verifies identity | Verifies permissions |
| Question | Who are you? | What can you do? |
| Order | Always comes first | Happens after authentication |
| HTTP Status | 401 Unauthorized | 403 Forbidden |
| Mechanism | Passwords, tokens, biometrics | Roles, policies, ACLs |
| Visibility | User sees login process | Invisible to user (backend) |
| Changeable | User can change credentials | Admin controls permissions |
OAuth 2.0 Roles in Authentication and Authorization
OAuth 2.0 elegantly separates authentication and authorization concerns across four distinct roles:
- Resource Owner — The user who owns the data (authenticates and grants permission)
- Client — The application requesting access (needs authorization)
- Authorization Server — Issues tokens after authentication (handles both authn and authz)
- Resource Server — Hosts protected data (validates authorization tokens)
When combined with JSON Web Tokens (JWT), OAuth 2.0 can encode both identity (authentication) and permissions (authorization) in a single token.
Real-World Examples
Banking Application
Authentication: Customer logs in with username, password, and MFA code from their phone. The bank verifies their identity against stored credentials.
Authorization: After login, the customer can view their own accounts and transfer money. They cannot view other customers' accounts or modify interest rates — those require different authorization levels.
Healthcare System
Authentication: A doctor scans their badge and enters a PIN at a workstation. The system confirms they are Dr. Smith from the cardiology department.
Authorization: Dr. Smith can view cardiac patient records in their department (ABAC policy) but cannot access records from psychiatry. A nurse on the same floor might have read-only access to vitals but cannot modify treatment plans.
E-Commerce Platform
Authentication: A customer signs in with Google SSO. The platform verifies their Google identity token and creates or links an account.
Authorization: The customer can browse products, place orders, and review their order history. They cannot access the admin dashboard, modify product listings, or view other customers' orders. A seller on the same platform has authorization to manage their own product listings but not others'.
Implementation Best Practices
- Always authenticate before authorizing — Never skip authentication checks. Return 401 for unauthenticated requests and 403 for unauthorized ones.
- Use established protocols — Leverage OAuth 2.0 and OpenID Connect rather than building custom authentication flows.
- Implement defense in depth — Apply authorization checks at multiple layers: API gateway, application middleware, and database level.
- Follow the principle of least privilege — Grant the minimum permissions necessary. Start restrictive and add permissions as needed, as discussed in Zero Trust Architecture.
- Audit everything — Log all authentication attempts and authorization decisions for security monitoring and compliance.
- Protect against common attacks — Implement rate limiting on login endpoints to prevent brute force attacks.
Test your understanding of authentication headers and token formats with our API and Network Tools.
Common Mistakes to Avoid
- Mixing up 401 and 403 — 401 means "not authenticated," 403 means "authenticated but not authorized"
- Client-side authorization only — Always enforce authorization on the server. Client-side checks are only for UX.
- Hardcoding roles — Use a policy engine or database-driven roles rather than hardcoded checks scattered throughout code.
- No session invalidation — Ensure you can revoke access tokens and sessions when a user's permissions change or they are deactivated.
- Storing plain-text passwords — Always hash passwords with bcrypt, scrypt, or Argon2. See our Encryption guide for more on hashing.
Frequently Asked Questions
Can you have authorization without authentication?
Technically, no. Authorization depends on knowing who the user is. However, some systems provide limited access to anonymous users (like viewing public pages), which could be considered a form of default authorization without explicit authentication. In any secure system, meaningful authorization always requires authentication first.
What is the difference between OAuth and OpenID Connect?
OAuth 2.0 is an authorization framework — it grants access to resources. OpenID Connect (OIDC) is an authentication layer built on top of OAuth 2.0 — it verifies identity. When you "Sign in with Google," OIDC handles authentication while OAuth 2.0 handles authorization to access your Google data.
Should I use RBAC or ABAC for my application?
Start with RBAC if your permission model is straightforward (admin, editor, viewer). Move to ABAC when you need context-dependent decisions: access based on time of day, geographic location, resource ownership, or department membership. Many systems use a hybrid approach with RBAC as the foundation and ABAC for specific fine-grained policies.
How do JWTs relate to authentication and authorization?
JWTs serve both purposes. The token itself proves authentication (it was issued after successful login). The claims inside the token (roles, permissions, scopes) drive authorization decisions. This makes JWTs efficient because the server can make authorization decisions without a database lookup on every request.
What is the best way to handle authorization in microservices?
In a microservices architecture, use an API gateway for authentication and coarse-grained authorization. Each service then handles fine-grained authorization locally. Pass user context (roles, claims) via JWT tokens between services. Consider a centralized policy engine like Open Policy Agent (OPA) for consistent authorization across services. Explore more in our tools section.