Authentication & Authorization

Security is the invisible foundation of any professional software project. Verifying who a user is and determining what they are allowed to do are two distinct but deeply intertwined processes. Misunderstanding the boundary between identity and permissions can lead to data breaches. A robust architecture handles these layers separately to ensure predictable and secure access across the entire application.

Authentication: Verifying identity

Authentication answers the question of who you are. It is the process of challenging the user to prove their identity before they can enter the system.

  • Credentials: The standard method of verifying a username against a hashed password. Passwords must never be stored in plain text; they require strong hashing algorithms like bcrypt or Argon2.
  • Multi-factor authentication (MFA): Requiring a second form of proof, such as a time-based code generated on a mobile device. This protects the account even if the primary password is leaked.
  • OAuth2 and OpenID Connect: Delegating identity verification to established external providers like Google or GitHub. This simplifies the user experience and offloads credential storage risks.
  • Passkeys: Utilizing device-level biometrics, such as a fingerprint or facial recognition, to authenticate users and eliminate the need for traditional passwords entirely.

Authorization: Managing permissions

Authorization answers the question of what you are allowed to do. It occurs strictly after a user is successfully authenticated and dictates their level of access to system resources.

  • Role-based access control (RBAC): Grouping users into defined roles, such as admin, editor, or viewer. Permissions are then assigned to the role rather than the individual user.
  • Attribute-based access control (ABAC): Evaluating dynamic rules and context. For example, a user might only be authorized to edit a specific document if their user ID matches the document’s author ID.
  • Scope-based access: Defining boundaries for programmatic access. An API token might be granted a “read-only” scope, preventing it from making destructive changes regardless of the user’s overarching role.

The access lifecycle

Understanding how these two concepts interact is critical for system design. The flow from an anonymous visitor to an authorized user follows a strict path. First, the client submits credentials. The server authenticates the identity and issues a secure token. On subsequent requests, the client presents this token. The server validates the token, extracts the user’s role, and authorizes the specific action against a permissions matrix before returning the requested data.

Access lifecycle

State management for user sessions

Once a user is verified, the server needs a reliable way to remember them across multiple HTTP requests. There are two primary architectural patterns for maintaining this continuity.

  • Stateful sessions: The server generates a unique session ID, stores it in a database or an in-memory store like Redis, and sends a matching cookie to the browser. The server must query this store on every incoming request.
  • Stateless tokens: The server issues a JSON Web Token (JWT) that contains cryptographically signed user data. The client sends this token with every request, allowing the server to verify the user’s identity without querying the database.

Architectural Trade-off: Stateful sessions provide total control, allowing administrators to instantly revoke access if an account is compromised. Stateless tokens scale infinitely because they do not require database lookups, but they are difficult to invalidate before their set expiration time.

Securing the transport layer

Authentication data is highly sensitive. If a token is intercepted during transmission, the attacker gains full control of the user’s session.

  • TLS/SSL encryption: All authentication traffic must route through HTTPS. This encrypts the data in transit, preventing attackers from reading passwords or session tokens over public networks.
  • Secure cookies: Tokens should be delivered via cookies flagged with the HttpOnly attribute to block malicious JavaScript from reading them. The Secure flag ensures the cookie is only transmitted over HTTPS.
  • SameSite policies: Configuring cookies with SameSite=Strict or Lax to prevent cross-site request forgery (CSRF) attacks, where a malicious website attempts to execute actions using the victim’s active session.

Learning Point: Never store sensitive JWTs or session identifiers in the browser’s local storage. Cross-site scripting (XSS) attacks can easily extract data from local storage, leading to immediate account takeover.

Implementing access control logic

In a professional backend environment, authorization is handled via middleware. This creates a protective barrier that intercepts the request before it ever reaches the core business logic.

				
					/**
 * Express.js middleware for Role-Based Access Control.
 * This ensures the request has a valid user object and checks 
 * if their assigned role meets the requirements for the route.
 */
const requireRole = (allowedRoles) => {
  return (req, res, next) => {
    // 1. Verify the user was authenticated by previous middleware
    if (!req.user) {
      return res.status(401).json({ error: "Authentication required." });
    }

    // 2. Authorize the user based on their assigned role
    if (!allowedRoles.includes(req.user.role)) {
      return res.status(403).json({ 
        error: "Forbidden. Insufficient permissions to access this resource." 
      });
    }

    // 3. Grant access to the business logic controller
    next();
  };
};

// Usage: router.delete('/posts/:id', requireRole(['admin', 'editor']), deletePost);

				
			

Revocation and logout mechanics

Ending an authenticated session cleanly is just as important as starting one. A poorly implemented logout process leaves orphaned tokens that can be exploited.

  • Clearing the client: The frontend application must destroy its local cache of user data and remove any relevant routing states.
  • Invalidating the server: For stateful architectures, the session record must be permanently deleted from the database or Redis cache.
  • Token blacklisting: For stateless JWT architectures, expired or manually revoked tokens should be temporarily placed in a high-speed denylist until their natural expiration timestamp passes.

By strictly separating the verification of identity from the enforcement of permissions, you build a secure engine capable of protecting sensitive data against both external attacks and internal misconfigurations.