Backend Development with Node.js/Express

Chapter 9
Authentication, Authorization and Backend Security

Diploma in Full Stack Programming
Backend Development with Node.js/Express   •   Diploma

Authentication, Authorization and Backend Security

Introduction

A backend application is not secure merely because it can accept requests and return correct responses. It must also establish who is making a request, determine what that user is permitted to do, protect confidential information, and remain dependable when requests are malicious, excessive, or malformed. Authentication answers the question “Who are you?” Authorization answers “What may you do?” Security engineering addresses both questions together with the protection of data, services, infrastructure, and users.

In an Express application, security is distributed across the entire request lifecycle: input parsing, authentication middleware, authorization checks, database queries, response construction, cookie handling, logging, deployment, and operational monitoring. A failure at any one stage can undermine the others. For example, a strong password hash does not prevent an insecure direct object reference, and a correctly signed JWT does not make an endpoint safe if it accepts an unauthorized resource identifier.

This chapter develops practical foundations for password hashing, sessions, cookies, JSON Web Tokens (JWTs), OAuth concepts, role-based access control, and common backend threats. Examples use Node.js and Express conventions. Production systems should also follow current OWASP guidance, framework security advisories, applicable privacy laws, and organizational policies.

Learning Objectives

By the end of this chapter, you should be able to:

  • distinguish authentication, authorization, accounting, and non-repudiation;
  • implement password storage using a slow, salted password-hashing algorithm;
  • explain session-based authentication, cookies, JWTs, and OAuth 2.0 at a conceptual and practical level;
  • design role-based and permission-based authorization middleware;
  • mitigate injection, cross-site scripting (XSS), cross-site request forgery (CSRF), broken access control, data exposure, and denial-of-service risks;
  • select an authentication model according to application architecture and threat model; and
  • evaluate security controls using layered defense, least privilege, and secure defaults.

1. Security Foundations

1.1 The security objectives

The classic information-security objectives are confidentiality, integrity, and availability. Confidentiality prevents unauthorized disclosure; integrity prevents unauthorized alteration; availability ensures that authorized users can obtain services when needed. Authentication and authorization support all three but do not replace them.

A useful security model is:

  1. Identity: an account, service, or device is represented by an identifier.
  2. Authentication: evidence is checked, such as a password, possession of a device, or a cryptographic credential.
  3. Authorization: policy determines whether the authenticated principal may perform an action on a resource.
  4. Audit and accountability: security-relevant events are recorded sufficiently to investigate misuse.
  5. Recovery: credentials, sessions, and compromised accounts can be revoked or restored safely.

Authentication factors are commonly classified as knowledge (password or PIN), possession (security key or authenticator device), and inherence (biometric characteristic). Multi-factor authentication combines factors from different categories. Two passwords are not two independent factors because both are knowledge factors.

1.2 Threat modeling

A threat model identifies assets, actors, trust boundaries, attack paths, and consequences. For an online store, assets may include passwords, payment references, personal data, administrator functions, and service availability. Attackers may be anonymous internet users, compromised customers, malicious insiders, or attackers who have stolen a session token.

Security controls should be selected according to risk rather than fashion. A public read-only endpoint has different requirements from an administrative endpoint that deletes customer records. A threat model should ask:

  • What must be protected?
  • From whom and under which assumptions?
  • What happens if a credential, token, or server is compromised?
  • How will misuse be detected and contained?
  • What is the recovery and revocation procedure?

2. Password Hashing and Account Credentials

Passwords must never be stored as plaintext or encrypted in a reversible form. If an attacker obtains an encryption key, reversible encryption exposes every password. Instead, a password is processed by a password-hashing function designed to be deliberately expensive and resistant to guessing.

A secure password hash normally includes a unique random salt and work parameters. The salt prevents identical passwords from producing identical stored values and defeats precomputed rainbow tables. It does not need to be secret. The work factor makes each guess costly for an attacker while remaining acceptable for legitimate login operations.

Recommended modern choices include Argon2id, scrypt, and bcrypt where appropriate. Argon2id is often preferred for new systems because it can require both computation and memory. The exact parameters should be selected through benchmarking on production-like hardware and reviewed as hardware changes. A password hash is not encryption and cannot be “decrypted.” Verification recomputes the algorithm using the stored parameters and compares the result using the library’s safe verification method.

A typical Node.js implementation with Argon2 might be conceptually represented as:

import argon2 from 'argon2';

const passwordHash = await argon2.hash(password, {
  type: argon2.argon2id
});

const valid = await argon2.verify(passwordHash, submittedPassword);

The application should not log passwords, hashes, reset tokens, or access tokens. Password reset tokens should be generated with a cryptographically secure random generator, stored as hashes where practical, assigned short expirations, and invalidated after use. Account-enumeration risks should be considered: registration, login, and reset responses should avoid revealing whether an email address exists.

Password policy should encourage long, unique passwords and use breached-password screening where lawful and appropriate. Arbitrary composition rules, such as requiring several punctuation characters, can encourage predictable patterns. Rate limiting, multi-factor authentication, and secure recovery processes are generally more valuable than excessive complexity rules.

3. Sessions and Cookies

3.1 Server-side sessions

In session-based authentication, the server creates a session record after successful login. The browser receives an opaque session identifier, usually in a cookie. On later requests, the server uses that identifier to retrieve session state. The identifier must contain no sensitive information and must be generated with a cryptographically secure random source.

A simplified flow is:

Client ── credentials ──> Express server
Client <─ session cookie ── Express server
Client ── cookie ────────> Express server
Server ── session lookup ─> session store
Server ── authorized response ─> Client

The session store should be shared when an application runs on multiple instances; an in-memory store is unsuitable for production because it is not durable or coordinated. Redis or a database-backed store may be used with suitable availability and access controls.

On login, the application should regenerate the session identifier to prevent session fixation. On logout, the session should be destroyed or invalidated server-side. Idle and absolute expiration limits reduce the value of stolen sessions. High-risk operations may require recent authentication or step-up authentication.

3.2 Cookie security attributes

Important cookie attributes include:

AttributePurpose
SecureSends the cookie only over HTTPS.
HttpOnlyPrevents JavaScript from reading the cookie, reducing token theft through some XSS attacks.
SameSiteRestricts cross-site sending and helps mitigate CSRF.
Path and DomainLimit where the cookie is sent. Narrow scope is safer.
Max-Age or ExpiresDefines lifetime; short lifetimes reduce exposure.

HttpOnly does not stop an XSS payload from making requests in the victim’s browser; it only prevents direct script access to the cookie. Therefore XSS defenses remain necessary. For sensitive applications, use HTTPS throughout, configure HSTS carefully, avoid broad domains, and consider the __Host- cookie prefix when its requirements can be met.

4. JSON Web Tokens

A JWT is a compact representation of claims. A signed JWT commonly contains a header, payload, and signature. The payload may contain claims such as sub (subject), iss (issuer), aud (audience), iat (issued-at time), and exp (expiration time). The payload is encoded, not automatically encrypted; anyone who obtains the token can usually read it.

The server must verify the signature using an explicitly configured algorithm and trusted key, then validate issuer, audience, expiration, and any application-specific claims. It must reject algorithm confusion and unsigned tokens. Token libraries should be kept current.

A JWT flow may be useful for stateless APIs, but “stateless” does not mean risk-free. A stolen bearer token can generally be used until it expires. Revocation is more difficult than with a server-side session. Common designs use short-lived access tokens and a refresh-token mechanism with rotation and server-side reuse detection. Refresh tokens require particularly careful storage and revocation.

Do not place passwords, payment data, or unnecessary personal data in JWT claims. Never trust a role claim merely because it is present; trust it only after signature and policy validation, and consider whether authorization data has changed since issuance. For browser applications, storing tokens in localStorage increases exposure to XSS. A secure, appropriately scoped cookie-based design or a standards-based authorization-code flow with PKCE is often preferable.

Sessions and JWTs compared

ConsiderationServer-side sessionJWT access token
RevocationDirect and immediate through storeMore complex until expiry or denylisting
Horizontal scalingRequires shared session storeVerification can be distributed
Data visibilityState remains server-sideClaims are readable unless separately encrypted
Token sizeSmall identifierCan become large with claims
Best fitTraditional web apps and controlled clientsCarefully designed APIs and distributed services

Neither approach is universally superior. The decision should follow client type, revocation needs, infrastructure, and threat model.

5. OAuth 2.0 and OpenID Connect Concepts

OAuth 2.0 is an authorization framework for delegated access. It allows a resource owner to authorize a client to obtain limited access to a resource server through an authorization server. OAuth is not, by itself, an identity protocol. OpenID Connect (OIDC) adds an identity layer and an ID token for authentication.

The authorization-code flow is generally appropriate for web applications and native applications. The client redirects the user to the authorization server. After consent and authentication, the authorization server returns a short-lived code to the registered redirect URI. The client exchanges the code for tokens. Proof Key for Code Exchange (PKCE) binds the exchange to a secret generated by the client and protects against interception of the authorization code, especially for public clients.

Important practices include:

  • register exact redirect URIs; do not accept arbitrary redirect destinations;
  • use state to bind the response to the initiating session and mitigate request forgery;
  • use PKCE with a strong code verifier;
  • request the minimum scopes required;
  • validate issuer, audience, nonce, signature, and token expiration for OIDC;
  • protect client secrets and never expose confidential client credentials in browser code; and
  • understand that an access token’s audience and scopes determine where it may be used.

OAuth does not eliminate authorization in the application. A valid token proves that the authorization server issued it, not that the user may access every record in the application.

6. Authorization and Role-Based Access Control

Authorization should be enforced on the server for every protected operation. Client-side hiding of buttons is a usability feature, not a security control.

Role-based access control (RBAC) assigns permissions to roles and roles to users. For example, support_agent may view support tickets, while administrator may manage users. A permission-based model is often more precise: ticket:read, ticket:update, and user:deactivate can be assigned to roles.

A robust decision contains at least three dimensions:

  1. Subject: who is acting?
  2. Action: what operation is requested?
  3. Resource and context: on which object, under what conditions?

This prevents a common mistake: checking only whether a user is logged in, or checking a role without checking ownership. A customer may be permitted to read their own order but not another customer’s order even when both are authenticated.

Example Express middleware:

function requireAuth(req, res, next) {
  if (!req.user) return res.status(401).json({ error: 'Authentication required' });
  next();
}

function requirePermission(permission) {
  return (req, res, next) => {
    if (!req.user?.permissions?.includes(permission)) {
      return res.status(403).json({ error: 'Forbidden' });
    }
    next();
  };
}

app.delete('/users/:id',
  requireAuth,
  requirePermission('user:delete'),
  deleteUser
);

The handler must still verify resource-level rules, for example whether the target user belongs to an administrator’s permitted organization. Use 401 Unauthorized when authentication is absent or invalid and 403 Forbidden when identity is known but access is denied. Avoid leaking sensitive details through error messages.

Authorization policies should follow deny-by-default, least privilege, separation of duties, and fail-closed behavior. Avoid trusting user-supplied role fields, URL parameters, hidden form fields, or client-side state.

7. Major Backend Threats and Mitigations

7.1 Injection

Injection occurs when untrusted input changes the meaning of an interpreter command. SQL injection is prevented primarily through parameterized queries or a correctly used query builder, not by manually escaping strings.

const result = await db.query(
  'SELECT id, email FROM users WHERE email = $1',
  [email]
);

The same principle applies to NoSQL queries, shell commands, LDAP, template engines, and operating-system APIs. Validate type, length, range, and allowed structure at the boundary, but do not treat validation as a substitute for parameterization. Avoid passing user input to shell execution; if unavoidable, use safe APIs, strict allowlists, and least-privileged service accounts.

7.2 Cross-site scripting

XSS occurs when attacker-controlled content is interpreted as executable browser content. Contextual output encoding, safe templating, input constraints, and a carefully designed Content Security Policy reduce risk. Do not insert untrusted strings with innerHTML. On the backend, avoid returning unsanitized HTML and sanitize rich text with a maintained, context-aware library. HttpOnly cookies reduce token extraction but do not prevent all XSS consequences.

7.3 Cross-site request forgery

CSRF exploits browsers’ tendency to send ambient cookies with cross-site requests. It primarily affects cookie-authenticated state-changing operations. Defenses include SameSite cookies, anti-CSRF tokens bound to the user session, origin or referer checks where appropriate, and requiring non-simple request headers for API designs. State-changing operations should not use GET. Bearer tokens held outside automatically attached cookies change the CSRF profile but introduce token-storage and XSS concerns.

7.4 Broken access control

Broken access control includes missing authentication checks, predictable identifiers, privilege escalation, insecure direct object references, and method-level omissions. Centralize authorization middleware, test every route and HTTP method, use policy functions for object ownership, and log denied high-risk actions. Random identifiers can make enumeration harder but do not replace authorization.

7.5 Data exposure

Return only fields required by the client. Explicitly select database columns rather than serializing complete user objects. Protect secrets in environment configuration and secret managers, not source control. Use TLS, redact logs, classify data, apply retention limits, and ensure backups are encrypted and access-controlled. Error responses should be useful to legitimate developers without exposing stack traces, SQL statements, tokens, or internal topology in production.

7.6 Denial of service

Availability attacks include request floods, expensive password verification, huge request bodies, slow uploads, algorithmic complexity attacks, and resource exhaustion. Apply reverse-proxy and application rate limits, request-size limits, timeouts, connection limits, pagination, bounded computational work, and queues for expensive tasks. Rate limits should account for identity, IP, endpoint, and operational context; a single global limit can unfairly block users behind shared networks.

Password hashing is intentionally expensive, so login endpoints need especially careful rate limiting and monitoring. Distributed denial of service may require upstream protection such as a CDN, load balancer, or provider-level mitigation.

8. Secure Express Implementation Practices

Use security middleware such as Helmet with reviewed configuration, disable unnecessary technology-identifying headers, enforce HTTPS at the edge, and configure trusted proxies correctly. Parse only expected content types and impose body-size limits. Validate request schemas using a maintained validation library. Keep dependencies updated and use lockfiles, vulnerability scanning, and software composition review.

Separate authentication from authorization. Authentication middleware should establish a trusted principal; authorization middleware should make a policy decision. Keep these concerns testable and avoid silently treating parsing failures as anonymous requests when that could hide attacks.

Use structured security logs containing event type, actor identifier where appropriate, target, result, timestamp, request correlation identifier, and source context. Do not log passwords, session identifiers, raw JWTs, reset links, or unnecessary personal data. Monitoring should detect repeated failures, impossible travel patterns where relevant, privilege changes, token reuse, mass downloads, and unusual administrative activity.

Security testing should include unit tests for policy functions, integration tests for every protected route, negative tests for horizontal and vertical privilege escalation, dependency scanning, static analysis, dynamic testing, and periodic penetration testing. Test the deployed configuration, not only local development.

9. Illustrative Case: Securing a Multi-Tenant Support Platform

Background

Consider a fictional company, HelpDesk Cloud, providing a support platform to several organizations. Users can create tickets, agents can respond, and administrators can manage organization settings. The initial Express application stores passwords using a fast hash, places user objects in cookies, and checks only whether a user is logged in.

Problem

An assessment identifies several weaknesses. A user can change an order identifier in a URL and view another organization’s ticket. A support endpoint returns password-reset metadata. Login has no rate limit. A state-changing browser request is vulnerable to CSRF, and an administrator role is accepted from a request body during profile updates.

Analysis

The principal risks are credential compromise, tenant isolation failure, excessive data exposure, and privilege escalation. The central design error is confusing authentication with authorization. Even if the login mechanism is repaired, every resource access still needs a tenant and ownership decision.

Approach and solution

HelpDesk Cloud migrates passwords to Argon2id with calibrated parameters and forces reset for legacy accounts. It uses server-side sessions in a shared store, regenerates the session after login, and configures secure, HttpOnly, SameSite cookies. Login and reset endpoints receive identity-aware rate limits.

A policy layer checks subject, action, resource, and tenantId. Ticket queries include the tenant constraint in the database query rather than retrieving a record first and checking later. For example, the conceptual query is:

SELECT id, subject, status
FROM tickets
WHERE id = $1 AND tenant_id = $2;

The role is changed only through an administrative workflow; profile updates cannot modify it. Responses use explicit field selection. CSRF tokens protect cookie-authenticated mutations, and GET is made read-only. Security logs record denied cross-tenant access attempts without recording session values.

Outcome

The revised system reduces the consequences of a stolen session, prevents cross-tenant ticket access, removes a privilege-escalation path, and improves detection of credential attacks. Performance testing confirms that password verification and authorization queries remain within operational targets, while rate limits and queueing protect the service during bursts.

Learning points

  • Authentication is only the beginning of access control.
  • Tenant boundaries must be enforced in policy and data queries.
  • Sensitive output should be deliberately designed, not inherited from database objects.
  • Security controls must be tested negatively, including attempts that should fail.
  • Operational controls such as logging, rate limiting, and revocation are part of application security.

10. Ethical, Legal, and Contemporary Considerations

Security engineering involves personal data, identity, and power. Collect only data necessary for a stated purpose, provide appropriate transparency, restrict internal access, and define retention and deletion procedures. Privacy regulations may impose requirements for breach notification, access rights, lawful processing, and data minimization; legal advice is needed for jurisdiction-specific compliance.

Organizations should provide responsible vulnerability-reporting channels and avoid retaliating against good-faith researchers. Automated and AI-assisted coding tools can suggest middleware or configuration, but generated security code must be reviewed, tested, and checked against current documentation. AI systems can reproduce insecure patterns, mishandle secrets, or generate plausible but incorrect authorization logic. Never submit production credentials or personal data to an unapproved tool.

Contemporary systems increasingly use passkeys based on public-key cryptography, phishing-resistant multi-factor authentication, workload identity, centralized policy engines, and continuous risk evaluation. These developments do not remove fundamental requirements: verify identity, minimize privilege, validate context, protect secrets, and maintain recovery and audit processes.

Best-Practice Checklist

Before releasing an Express backend, verify that:

  • passwords use a modern password-hashing algorithm with unique salts;
  • login, reset, and MFA flows have rate limits and safe error messages;
  • sessions or tokens have secure lifetimes, validation, rotation, and revocation plans;
  • cookies use appropriate Secure, HttpOnly, and SameSite settings;
  • JWT algorithms, issuer, audience, expiration, and key rotation are validated;
  • OAuth redirect URIs, state, PKCE, scopes, and token audiences are checked;
  • every protected route has server-side authorization and resource-level checks;
  • database and interpreter operations use parameterization;
  • request bodies, uploads, responses, and logs are bounded and minimized;
  • CSRF and XSS defenses match the client authentication model;
  • dependencies, secrets, configuration, backups, and TLS are managed securely; and
  • monitoring, incident response, testing, and account-recovery procedures are documented.

Chapter Summary

Authentication establishes the identity of a principal; authorization determines whether that principal may perform a particular action on a particular resource. Secure systems hash passwords with deliberately expensive, salted algorithms, protect sessions and cookies, validate JWTs rather than merely decoding them, and use OAuth 2.0 and OpenID Connect according to their distinct purposes. RBAC provides a useful starting structure, but professional authorization also evaluates ownership, tenant, action, resource, and context.

Backend security requires layered controls. Parameterized queries mitigate injection; safe output handling and Content Security Policy reduce XSS; SameSite cookies and CSRF tokens protect cookie-authenticated mutations; deny-by-default policies address broken access control; data minimization limits exposure; and rate limits, size limits, timeouts, and upstream defenses improve availability. Secure design also includes logging, monitoring, dependency management, privacy governance, testing, revocation, and incident response. No single token format or middleware package substitutes for a complete threat-informed security process.

Key Terms

Authentication: Process of verifying the identity of a principal.

Authorization: Decision about whether an authenticated principal may perform an action.

Argon2id: Password-hashing variant designed to resist GPU and memory-based guessing attacks.

Salt: Unique random value combined with a password before hashing.

Session: Server-maintained state associated with a client through an identifier.

Cookie: Browser-managed data sent with requests according to scope and security attributes.

JWT: Signed, compact token containing claims; its payload is normally readable.

Refresh token: Longer-lived credential used to obtain new access tokens.

OAuth 2.0: Framework for delegated authorization.

OpenID Connect: Identity layer built on OAuth 2.0.

PKCE: Proof Key for Code Exchange, protecting authorization-code exchanges.

RBAC: Authorization model assigning permissions through roles.

Least privilege: Granting only the access required for a task.

CSRF: Attack that causes a browser to send an unwanted authenticated request.

XSS: Injection of content that executes as browser-side code.

SQL injection: Manipulation of a database query through untrusted input.

Rate limiting: Restricting request frequency or volume.

Session fixation: Attack that causes a victim to use an attacker-known session identifier.

Deny by default: Refusing access unless an explicit policy grants it.

Threat model: Structured description of assets, attackers, trust boundaries, and risks.

Review Questions

Short Answer Questions

  1. What is the difference between authentication and authorization?
  2. Why is a salt used in password hashing?
  3. State three important security attributes of cookies.
  4. Why is a JWT payload not suitable for storing secrets?
  5. What problem does parameterized SQL solve?

Long Answer Questions

  1. Compare server-side sessions and JWT-based authentication, including revocation, scaling, storage, and browser risks.
  2. Explain how OAuth 2.0 authorization code flow with PKCE works and distinguish OAuth authentication from OpenID Connect.
  3. Describe a professional RBAC design and explain why resource ownership checks are still necessary.
  4. Explain the causes and mitigations of XSS, CSRF, and broken access control in an Express application.
  5. Design a secure password-reset process, including token generation, storage, expiration, response behavior, and auditing.

Analytical / Application Questions

  1. A customer can read /tickets/481 after changing the number from their own ticket. Identify the vulnerability and propose database, middleware, and testing changes.
  2. An API uses short-lived JWTs but stores refresh tokens in a database. Analyze the benefits, remaining risks, and controls needed for rotation and reuse detection.
  3. During a traffic spike, login requests consume most CPU because password verification is expensive. Propose a balanced availability and security response without weakening password hashing indiscriminately.

Multiple Choice Questions with Answers

Question 1

Which practice is most appropriate for storing user passwords?

A. Store plaintext passwords so users can recover them

B. Encrypt passwords with one application-wide key

C. Hash passwords with a salted, deliberately expensive password-hashing algorithm

D. Encode passwords with Base64

Correct Answer: C. Hash passwords with a salted, deliberately expensive password-hashing algorithm

Question 2

What does the HttpOnly cookie attribute primarily prevent?

A. The cookie being sent over an unencrypted network

B. Client-side JavaScript directly reading the cookie

C. All cross-site requests from being generated

D. The server from deleting the cookie

Correct Answer: B. Client-side JavaScript directly reading the cookie

Question 3

What must an application do before trusting claims in a JWT?

A. Decode the payload only

B. Verify its signature and validate relevant claims such as issuer and expiration

C. Store the token in a URL

D. Accept whichever algorithm the token specifies

Correct Answer: B. Verify its signature and validate relevant claims such as issuer and expiration

Question 4

Which statement best describes OAuth 2.0?

A. A password-hashing algorithm

B. A delegated authorization framework

C. A database encryption standard

D. A replacement for all application authorization checks

Correct Answer: B. A delegated authorization framework

Question 5

A logged-in user changes an object ID and sees another customer’s record. What is the primary issue?

A. Broken access control

B. Password salting

C. Content negotiation

D. DNS failure

Correct Answer: A. Broken access control

Question 6

Which technique is the primary defense against SQL injection?

A. Increasing the browser cookie lifetime

B. Using parameterized queries

C. Returning more detailed database errors

D. Hiding the submit button

Correct Answer: B. Using parameterized queries

Question 7

Which combination most directly reduces CSRF risk for cookie-authenticated state-changing requests?

A. GET requests and verbose errors

B. SameSite cookies and anti-CSRF tokens

C. Long-lived bearer tokens in URLs

D. Disabling server-side authorization

Correct Answer: B. SameSite cookies and anti-CSRF tokens

Question 8

Why should a login endpoint commonly have rate limiting?

A. To make passwords reversible

B. To reduce online guessing and resource-exhaustion attacks

C. To expose whether an account exists

D. To bypass password verification

Correct Answer: B. To reduce online guessing and resource-exhaustion attacks

Question 9

What is the principle of least privilege?

A. Every authenticated user receives administrator access

B. Access is granted only to the extent required for the task

C. All security checks are performed in the browser

D. Database tables are made public for transparency

Correct Answer: B. Access is granted only to the extent required for the task

Question 10

Which statement about JWT payloads is correct in a conventional signed JWT?

A. The payload is automatically encrypted

B. The payload is normally readable by anyone who obtains the token

C. The payload cannot contain expiration information

D. The payload is trusted without signature verification

Correct Answer: B. The payload is normally readable by anyone who obtains the token

Backend Development with Node.js/Express
Chapter 9 of 15