Main Our Publications
NestJS Authentication: Access Tokens, Refresh Rotation, and Session Revocation

NestJS Authentication: Access Tokens, Refresh Rotation, and Session Revocation

  • NestJS authentication
  • access token
  • refresh token
  • token rotation
  • session revocation
  • JWT security
  • logout all devices
NestJS Authentication: Access Tokens, Refresh Rotation, and Session Revocation

Design production NestJS authentication with verified short-lived access tokens, server-owned refresh sessions, atomic rotation, replay detection, browser protections, device logout, and incident-ready revocation.

NestJS Authentication: Access Tokens, Refresh Rotation, and Session Revocation

Production authentication is a security system with a lifecycle, not a controller that signs a JWT. It must limit the value of a stolen credential, distinguish sessions and devices, handle concurrent renewal, revoke long-lived access after logout or compromise, protect browser flows, rotate keys, preserve audit evidence, and fail safely when databases or identity providers are unavailable.

A practical first-party design separates short- and long-lived authority. A short-lived signed access token proves claims to an API without a session lookup on every ordinary request. A high-entropy refresh credential continues one server-owned session and rotates whenever used. The database remains authoritative for long-lived access, while the access-token lifetime defines the maximum normal delay before revocation reaches otherwise stateless endpoints.

This model is not universal. A mature OpenID Connect provider may be safer than operating passwords and recovery in-house. High-risk operations may require recent authentication, MFA, transaction-specific authorization, or a live session check. Native, browser, machine, and partner clients have different storage and sender-constraining options. Begin with a threat model: credential theft, XSS, CSRF, database leakage, token replay, account enumeration, session fixation, insider access, key compromise, and denial of service.

Define the promised outcomes before choosing token lifetimes: which actions need immediate denial, how users view and end sessions, what password reset revokes, whether API keys are separate, how support may impersonate, which events trigger alerts, and what evidence incident responders need. Our NestJS development services adapt these controls to the clients, risks, compliance obligations, and operating capacity of the product.

Define Identity and Token Trust Boundaries

Login begins at an untrusted edge. Validate the DTO at runtime, normalize identifiers carefully, rate-limit by several defensible signals, and return responses that do not reveal whether an account exists. Verify passwords with a current password-hashing function and calibrated cost, then apply account status, verified-channel, MFA, risk, and recovery policies. Create a session only after every required factor succeeds; rotate any pre-authentication session identifier to prevent fixation.

Issue access tokens with an explicit signing algorithm and managed key. Verification must constrain the algorithm, signature, issuer, audience, expiration, not-before time when used, and token type; decoding is not verification. Keep claims minimal: subject, session or family identifier, token identifier, issuer, audience, issued-at, expiration, and only authorization facts that may safely remain stale for that short lifetime. Authentication identifies a principal; NestJS guards and application behavior must still authorize the operation and resource.

Plan key ownership and rotation from the start. Symmetric signing can suit one issuer-verifier boundary; asymmetric keys make independent verification and constrained distribution easier. Store private or shared secrets in a secret manager, attach a key identifier where appropriate, publish only intended verification keys, and accept old and new keys for a documented overlap. Reject unknown algorithms and keys. An emergency compromise runbook must identify who rotates, how verifiers update, which sessions or tokens are invalidated, and how customers are informed.

Treat Refresh Credentials as Server-Owned Sessions

Represent long-lived access as a server-side session or token family. Generate an opaque refresh secret with a cryptographically secure source, return it once, and store only a keyed digest so a database leak does not immediately reveal usable credentials. Record user, family, current token, creation, last use, idle expiry, absolute expiry, replacement link, revocation time and reason, authentication strength, and minimized device metadata. Preserve the original absolute expiry through rotation so activity cannot extend access forever.

For browser clients, an appropriately scoped Secure and HttpOnly cookie usually protects the refresh secret from ordinary JavaScript reads. Choose SameSite from the actual cross-site login and application topology, constrain Path and Domain where practical, require HTTPS, and add CSRF protection to every cookie-authenticated state-changing request. HttpOnly does not neutralize XSS: injected code can still act through the browser. Do not place tokens in URLs or logs, and send sensitive responses with cache policy appropriate to the product.

Native and desktop clients need platform-protected storage and a public-client threat model. Confidential server clients may authenticate themselves, while public clients cannot keep a shared secret reliably. Consider sender-constrained credentials when the protocol and client support them. Device name, user agent, IP, and geography can help users recognize activity and help risk analysis, but they are changeable signals rather than identity. Avoid automatic lockouts based on normal travel, carrier networks, privacy relays, or software updates.

CREATE TABLE auth_session (
  id UUID PRIMARY KEY,
  user_id UUID NOT NULL,
  family_id UUID NOT NULL,
  refresh_token_digest CHAR(64) NOT NULL UNIQUE,
  replaced_by_session_id UUID NULL,
  created_at TIMESTAMPTZ NOT NULL,
  last_used_at TIMESTAMPTZ NOT NULL,
  idle_expires_at TIMESTAMPTZ NOT NULL,
  absolute_expires_at TIMESTAMPTZ NOT NULL,
  revoked_at TIMESTAMPTZ NULL,
  revoke_reason VARCHAR(64) NULL,
  CONSTRAINT auth_session_replacement_fk
    FOREIGN KEY (replaced_by_session_id) REFERENCES auth_session(id)
);

CREATE INDEX auth_session_user_active_idx
  ON auth_session (user_id, revoked_at);
CREATE INDEX auth_session_family_idx
  ON auth_session (family_id);

Rotate Atomically and Treat Reuse as a Security Event

Each accepted refresh credential should become unusable and produce exactly one successor linked to the same family. Perform digest lookup, expiry and revocation checks, invalidation, successor creation, and replacement linking in one database transaction with a row lock or compare-and-set rule. Otherwise two concurrent requests can both validate the same secret and create competing active branches. Enforce a uniqueness rule that matches the model, and make client code coalesce concurrent refresh attempts rather than refreshing once per failed API request.

If a previously replaced token appears again, treat it as possible compromise. RFC 9700 describes rotation as a replay-detection mechanism because the authorization server retains the relationship between old and new tokens. The server cannot reliably know whether the attacker or legitimate client used the credential first. Revoke the active family, reject further refresh, record a security event without the secret, and require fresh authentication. Notify the user or security team according to risk and product policy.

Unreliable networks create ambiguity: the server may commit rotation while the response is lost. A narrowly bounded, explicitly analyzed grace mechanism may improve usability, but freely accepting an old token defeats replay detection. Model the state machine and its invariants before adding grace: maximum uses, time window, same-client evidence, response recovery, and behavior after a second presentation. Preserve revocation when returning an error; throwing inside a transaction that rolls back the family revocation is a subtle but consequential implementation failure.

async rotate(presented: string): Promise<TokenPair> {
  const digest = this.tokenDigest(presented);

  const outcome = await this.dataSource.transaction(async (manager) => {
    const current = await manager.findOne(AuthSession, {
      where: { refreshTokenDigest: digest },
      lock: { mode: 'pessimistic_write' },
    });

    if (!current || this.isExpired(current)) {
      throw new UnauthorizedException();
    }
    if (current.replacedBySessionId) {
      await this.revokeFamily(manager, current.familyId, 'refresh_reuse');
      return { kind: 'reused' } as const;
    }
    if (current.revokedAt) throw new UnauthorizedException();

    const rawNext = randomBytes(32).toString('base64url');
    const next = manager.create(AuthSession, {
      id: randomUUID(),
      userId: current.userId,
      familyId: current.familyId,
      refreshTokenDigest: this.tokenDigest(rawNext),
      createdAt: new Date(),
      lastUsedAt: new Date(),
      idleExpiresAt: this.nextIdleExpiry(),
      absoluteExpiresAt: current.absoluteExpiresAt,
    });

    await manager.save(next);
    current.replacedBySessionId = next.id;
    current.lastUsedAt = new Date();
    await manager.save(current);
    return { kind: 'rotated', userId: current.userId, next, rawNext } as const;
  });

  // Throw after commit so replay-triggered family revocation survives.
  if (outcome.kind === 'reused') throw new UnauthorizedException();
  return this.issuePair(outcome.userId, outcome.next.id, outcome.rawNext);
}

Make Revocation Semantics Explicit

Define different operations clearly. “Logout this device” revokes the current session or family and clears its browser cookie using matching Path, Domain, Secure, SameSite, and expiration attributes. It should be idempotent: a repeated request neither restores nor reveals session state. “Logout all devices” revokes every applicable session for the account in one auditable operation. Let users view recent sessions and terminate a selected one without trusting display metadata as identity.

Password reset, confirmed takeover, account disablement, removal of a factor, administrator action, or a high-risk credential change may require wider revocation and recent reauthentication. Decide separately whether API keys, delegated grants, trusted devices, recovery codes, and partner sessions end. Do not imply that clearing client storage is revocation; OWASP guidance requires invalidation on the server side. Consider cache headers and browser data cleanup for sensitive surfaces, while recognizing that cleanup headers are not a substitute for server authority.

Self-contained access JWTs may remain usable until expiration. If the business requires immediate denial, check live session status or a user security version at sensitive boundaries, or maintain a short-lived revocation cache. This deliberately introduces state, latency, and availability dependencies, so document fail-open or fail-closed behavior. Another option is narrower access-token lifetime with step-up verification for consequential actions. Choose from loss and threat evidence rather than copying a universal duration.

Test the Adversarial and Concurrent Paths

Test tokens as hostile input. Cover malformed structures, oversized headers, invalid signatures, algorithm confusion attempts, unknown key IDs, wrong issuer or audience, expired and not-yet-valid times, missing required claims, wrong token type, revoked sessions, idle and absolute expiry, user disablement, and authorization against another tenant or record. Confirm that decoding helpers never become authentication and that every transport—HTTP, GraphQL, WebSocket, and messages where applicable—uses equivalent policy.

Test concurrency and partial failure. Submit the same refresh credential simultaneously and prove that exactly one successor survives. Replay a replaced token and confirm that family revocation persists. Lose the rotation response, repeat logout, race logout with refresh, rotate signing keys during traffic, fail the session database, exhaust rate limits, and retry after timeouts. Verify cookie clearing, CORS, CSRF rejection, cache behavior, clock skew handling, and accessible client messaging.

Operate authentication with privacy-conscious telemetry. Measure sign-in failures, refresh outcomes, reuse detection, revocations, MFA failures, session creation, key verification errors, and latency without logging passwords, raw tokens, reset links, secrets, or unnecessary personal data. Protect audit records from tampering and restrict access. Alerts need an owner and response playbook; weak signals such as IP or user-agent change should support investigation, not automatically prove compromise.

Rehearse compromise: leaked signing key, exposed refresh-token database, malicious dependency, stolen device, credential stuffing, and identity-provider outage. The response should name containment, key and session invalidation, customer communication, evidence preservation, recovery, and post-incident changes. Authentication is ready for production when its failure behavior is as deliberately designed and tested as login success.

Production Authentication Review

  • Verified access: access tokens have a short, justified lifetime; verification constrains algorithm, key, signature, issuer, audience, time, type, and required claims before authorization.
  • Protected refresh: refresh credentials are high-entropy secrets, protected in transit and client storage, never placed in URLs or logs, and persisted only as keyed digests.
  • Atomic rotation: one transaction consumes the presented token, creates one successor, preserves absolute expiry, and retains family history so reuse can revoke the active branch.
  • Explicit revocation: device logout, all-device logout, password reset, takeover, account disablement, administrator action, API keys, and access-JWT delay each have documented behavior.
  • Browser boundary: cookie attributes, trusted origins, CORS, CSRF defenses, CSP, cache controls, XSS mitigations, and logout cleanup match the deployed topology rather than local defaults.
  • Operated security: key rotation, credential-free telemetry, rate limits, audit protection, recovery drills, alert ownership, compromise response, and dependency patching are tested responsibilities.
Turn the Authentication Threat Model into Testable Controls

Share the clients, identity flows, token storage, session model, sensitive operations, compliance constraints, and incident scenarios. We will identify concrete gaps and define proportionate rotation, revocation, browser, telemetry, and recovery controls.

We use cookies to ensure the security and proper functioning of our website. With your consent, we also use non-essential cookies for analytics and advertising purposes. You can accept or reject the use of non-essential cookies. You can change your preferences at any time. Learn more in our Cookie Policy.