Authentication vs Authorization in JavaScript
January 24, 2026
Authentication vs Authorization in JavaScript
Overview
Authentication and Authorization are two fundamental security concepts in web development. While often confused, they serve different purposes: Authentication verifies who you are, while Authorization determines what you can do.
Authentication
Authentication means the user needs to have username and password to access the portal or system. Basically, it means who can access the system.
Examples:
- OAuth (Google, Facebook login)
- Two-Step Authentication (2FA)
- Username and password
- Biometric authentication
- JWT tokens
- Session-based authentication
Implementation Example
// Authentication - Verifying user identity
async function authenticateUser(username, password) {
const user = await db.findUser(username);
if (!user) {
throw new Error("User not found");
}
const isValid = await bcrypt.compare(password, user.password);
if (!isValid) {
throw new Error("Invalid credentials");
}
// User is authenticated - create session/token
const token = generateToken(user);
return { user, token };
}
Authorization
Authorization means that the authenticated user is allowed to do what things. It determines what permissions the authenticated user has.
Examples:
- Manager role
- Admin role
- Stakeholder role
- Read-only access
- Write access
- Delete permissions
Implementation Example
// Authorization - Checking permissions
function checkPermission(user, action, resource) {
const userRole = user.role;
const permissions = getRolePermissions(userRole);
return permissions.includes(`${action}:${resource}`);
}
// Usage
if (checkPermission(user, "delete", "users")) {
// Allow deletion
} else {
throw new Error("Unauthorized");
}
Key Differences
| Aspect | Authentication | Authorization | |--------|---------------|---------------| | Purpose | Verify identity | Verify permissions | | Question | Who are you? | What can you do? | | Timing | Happens first | Happens after authentication | | Methods | Login, OAuth, 2FA | Roles, Permissions, ACL | | Result | User identity | Access rights |
Complete Example
Authentication System
class AuthSystem {
async login(username, password) {
// Authentication: Verify who you are
const user = await this.authenticate(username, password);
if (!user) {
throw new Error("Authentication failed");
}
// Create session/token
const token = this.generateToken(user);
return { user, token };
}
async authenticate(username, password) {
const user = await db.findUser(username);
if (!user) return null;
const isValid = await bcrypt.compare(password, user.hashedPassword);
return isValid ? user : null;
}
generateToken(user) {
return jwt.sign(
{ userId: user.id, role: user.role },
SECRET_KEY,
{ expiresIn: "24h" }
);
}
}
Authorization System
class AuthorizationSystem {
constructor() {
this.permissions = {
admin: ["read", "write", "delete", "manage"],
manager: ["read", "write"],
user: ["read"],
guest: []
};
}
// Authorization: Check what you can do
can(user, action, resource) {
const role = user.role;
const rolePermissions = this.permissions[role] || [];
return rolePermissions.includes(action);
}
require(user, action, resource) {
if (!this.can(user, action, resource)) {
throw new Error(`Unauthorized: Cannot ${action} ${resource}`);
}
}
}
// Usage
const auth = new AuthSystem();
const authz = new AuthorizationSystem();
// Step 1: Authenticate
const { user, token } = await auth.login("john", "password123");
// Step 2: Authorize
authz.require(user, "delete", "users"); // Check permission before action
Middleware Pattern
Express.js Example
// Authentication Middleware
function authenticate(req, res, next) {
const token = req.headers.authorization;
if (!token) {
return res.status(401).json({ error: "Authentication required" });
}
try {
const decoded = jwt.verify(token, SECRET_KEY);
req.user = decoded; // Attach user to request
next();
} catch (error) {
return res.status(401).json({ error: "Invalid token" });
}
}
// Authorization Middleware
function authorize(...allowedRoles) {
return (req, res, next) => {
if (!req.user) {
return res.status(401).json({ error: "Authentication required" });
}
if (!allowedRoles.includes(req.user.role)) {
return res.status(403).json({ error: "Insufficient permissions" });
}
next();
};
}
// Usage
app.delete("/users/:id",
authenticate, // First: Who are you?
authorize("admin"), // Then: Can you do this?
deleteUser
);
Role-Based Access Control (RBAC)
class RBAC {
constructor() {
this.roles = {
admin: {
users: ["create", "read", "update", "delete"],
posts: ["create", "read", "update", "delete"],
settings: ["read", "update"]
},
editor: {
posts: ["create", "read", "update"],
users: ["read"]
},
viewer: {
posts: ["read"],
users: ["read"]
}
};
}
hasPermission(user, resource, action) {
const role = user.role;
const rolePermissions = this.roles[role];
if (!rolePermissions) return false;
if (!rolePermissions[resource]) return false;
return rolePermissions[resource].includes(action);
}
checkPermission(user, resource, action) {
if (!this.hasPermission(user, resource, action)) {
throw new Error(`User ${user.id} cannot ${action} ${resource}`);
}
}
}
JWT Implementation
// Authentication: Create token
function authenticate(user) {
const payload = {
userId: user.id,
email: user.email,
role: user.role
};
return jwt.sign(payload, SECRET_KEY, { expiresIn: "24h" });
}
// Authorization: Verify token and check permissions
function authorize(requiredRole) {
return (req, res, next) => {
const token = req.headers.authorization?.split(" ")[1];
if (!token) {
return res.status(401).json({ error: "No token provided" });
}
try {
const decoded = jwt.verify(token, SECRET_KEY);
req.user = decoded;
// Check authorization
if (requiredRole && decoded.role !== requiredRole) {
return res.status(403).json({ error: "Insufficient permissions" });
}
next();
} catch (error) {
return res.status(401).json({ error: "Invalid token" });
}
};
}
Best Practices
- Always Authenticate First: Verify identity before checking permissions
- Principle of Least Privilege: Grant minimum necessary permissions
- Separate Concerns: Keep auth and authz logic separate
- Token Expiration: Set appropriate token lifetimes
- Secure Storage: Store tokens securely (httpOnly cookies)
- Role Validation: Always validate roles on server side
- Audit Logging: Log authentication and authorization events
Common Patterns
Pattern 1: Protected Route
function protectRoute(handler) {
return async (req, res) => {
// Authenticate
const user = await authenticateUser(req);
if (!user) {
return res.status(401).json({ error: "Unauthenticated" });
}
// Authorize
if (!hasPermission(user, req.route.path, req.method)) {
return res.status(403).json({ error: "Unauthorized" });
}
// Execute handler
return handler(req, res);
};
}
Pattern 2: Permission Decorator
function requirePermission(resource, action) {
return (target, propertyKey, descriptor) => {
const originalMethod = descriptor.value;
descriptor.value = function(...args) {
const user = this.getCurrentUser();
if (!this.authz.can(user, action, resource)) {
throw new Error("Unauthorized");
}
return originalMethod.apply(this, args);
};
return descriptor;
};
}
Real-World Example
class SecureAPI {
constructor() {
this.users = new Map();
this.sessions = new Map();
}
// Authentication
async login(username, password) {
const user = this.users.get(username);
if (!user || user.password !== password) {
throw new Error("Authentication failed");
}
const sessionId = generateSessionId();
this.sessions.set(sessionId, {
userId: user.id,
role: user.role,
expiresAt: Date.now() + 3600000
});
return { sessionId, user: { id: user.id, role: user.role } };
}
// Authorization
canAccess(sessionId, resource, action) {
const session = this.sessions.get(sessionId);
if (!session) return false;
const permissions = this.getPermissions(session.role);
return permissions[resource]?.includes(action) || false;
}
getPermissions(role) {
const permissionMap = {
admin: {
users: ["read", "write", "delete"],
posts: ["read", "write", "delete"]
},
user: {
posts: ["read", "write"],
users: ["read"]
}
};
return permissionMap[role] || {};
}
}