NestJS Refresh Token Tutorial for Secure Authentication
Short-lived JWT access tokens are more secure, since a stolen token becomes useless quickly, but they create an obvious usability problem: should a user really need to log in again every 15 minutes? Refresh tokens solve this tension, letting an application obtain new access tokens silently, without repeatedly bothering the user for their credentials, while still keeping each individual access token short-lived.
This lesson explains the access-token-plus-refresh-token pattern in depth and walks through implementing a secure refresh endpoint in NestJS.
NestJS Refresh Token Tutorial: Learning Objectives
- Understand why access tokens and refresh tokens serve different purposes.
- Explain the security reasoning behind keeping access tokens short-lived.
- Implement a login flow that issues both an access token and a refresh token.
- Build a /auth/refresh endpoint that exchanges a valid refresh token for a new access token.
- Understand refresh token rotation and why it improves security further.
NestJS Refresh Token Tutorial: Key Terms and Definitions
- Access token: A short-lived JWT used to authenticate individual API requests, typically expiring in minutes.
- Refresh token: A longer-lived, separately stored token used specifically to obtain a new access token without requiring the user to log in again.
- Token rotation: A security practice where each use of a refresh token invalidates it and issues a brand-new refresh token alongside the new access token.
- Token theft: A scenario where an attacker obtains a valid token (access or refresh) and can use it to impersonate the legitimate user until it expires or is revoked.
- Refresh token store: A server-side record (often in a database) of currently valid refresh tokens, enabling explicit revocation.
How NestJS Refresh Token Tutorial Works: Detailed Explanation
The core tension refresh tokens resolve is this: short-lived access tokens limit the damage from a stolen token, since it naturally expires quickly, but if that were the only token available, users would need to re-enter their credentials constantly, creating a frustrating experience. The solution is issuing two tokens at login instead of one: a short-lived access token (used for actual API requests) and a separate, longer-lived refresh token (used only for one specific purpose: obtaining a new access token).
When the access token eventually expires, rather than forcing the user to log in again, the client application sends the refresh token to a dedicated endpoint, commonly /auth/refresh. The server verifies this refresh token is still valid, typically checking both its signature/expiration and, ideally, whether it matches a record still marked as valid in a server-side store (such as a database table of currently issued refresh tokens), and if everything checks out, issues a brand-new access token, allowing the user's session to continue seamlessly without any visible interruption.
A critical security enhancement on top of this basic pattern is refresh token rotation. Instead of allowing the same refresh token to be reused indefinitely until its own (longer) expiration, each time a refresh token is used to obtain a new access token, the server also issues a brand-new refresh token and immediately invalidates the old one. This means if an attacker somehow steals a refresh token and uses it, the legitimate user's next refresh attempt (using their now-invalidated original token) will fail, providing a signal that something is wrong, and in more sophisticated implementations, this can trigger automatically revoking the entire token family as a precaution.
Storing refresh tokens server-side (rather than relying purely on their own expiration) is what makes explicit revocation possible: if a user logs out, changes their password, or an administrator needs to force a specific session to end, deleting or invalidating the corresponding refresh token record immediately prevents that token from being used to obtain further access tokens, something a pure, stateless JWT-only approach cannot achieve without additional infrastructure like a token blocklist.
Interview-Friendly Explanation
A strong interview or viva answer for nestjs refresh token tutorial must go beyond a one-line definition. Interviewers evaluating BCA, MCA, B.Tech, and self-taught backend developers reward answers that combine a precise definition, a clear working explanation, a real-world example, and a conclusion that highlights why it matters for securing production Node.js applications. Use the four-point framework below when answering under time pressure.
- Definition point: Access token: A short-lived JWT used to authenticate individual API requests, typically expiring in minutes.
- Working point: Explain the security reasoning behind keeping access tokens short-lived.
- Example point: Banking and fintech apps almost universally implement refresh token rotation exactly as shown here, since detecting refresh token reuse is a strong signal of token theft in high-security contexts.
- Conclusion point: The access-token-plus-refresh-token pattern balances security (short-lived access tokens) with usability (no constant re-login).
How to Answer This in a Technical Interview
- Give a two-to-three sentence definition using correct authentication and security terminology.
- Add one specific example drawn from a real backend scenario such as a banking, SaaS, or e-commerce login flow.
- Mention any security trade-offs (e.g. JWT vs sessions, access vs refresh tokens) since interviewers often probe this contrast.
- Close with one benefit, limitation, or production security consideration.
- Avoid vague answers like "it just checks if the user is logged in" — interviewers filter these out immediately.
Practical Scenario
Imagine you are securing a production SaaS backend, similar to systems used at companies like Razorpay, Freshworks, or Zomato. Every lesson in this module maps directly to a decision you will make while protecting that backend: how users prove who they are, how long that proof stays valid, who is allowed to do what, and how you defend against abuse.
NestJS Refresh Token Tutorial: Architecture and Flow Diagram
Visualize the access + refresh token flow:
[Login] --> [Server issues: Access Token (15 min) + Refresh Token (7 days, stored server-side)]
[Access token expires after 15 min] --> [Client sends Refresh Token to /auth/refresh] --> [Server verifies refresh token against its store] --> [Valid? Issue new Access Token (+ new rotated Refresh Token) | Invalid/revoked? Force re-login]
Access Token vs Refresh Token: Comparison Table
| Aspect | Access Token | Refresh Token |
|---|---|---|
| Typical lifespan | Minutes (e.g. 15 min) | Days to weeks (e.g. 7 days) |
| Used for | Authenticating individual API requests | Obtaining a new access token |
| Storage requirement | Usually stateless (JWT only) | Often stored server-side to enable revocation |
| Exposure risk if stolen | Limited, expires quickly | Higher impact, mitigated by rotation and server-side revocation |
NestJS Refresh Token Tutorial: NestJS Code Example
// auth.service.ts — issuing both tokens at login, and handling refresh
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
@Injectable()
export class AuthService {
constructor(private readonly jwtService: JwtService) {}
// Simplified in-memory store; a real app uses a database table
private validRefreshTokens = new Map<number, string>();
async login(userId: number, email: string) {
const accessToken = await this.jwtService.signAsync(
{ sub: userId, email },
{ expiresIn: '15m' },
);
const refreshToken = await this.jwtService.signAsync(
{ sub: userId },
{ expiresIn: '7d', secret: process.env.JWT_REFRESH_SECRET },
);
this.validRefreshTokens.set(userId, refreshToken); // store for later verification/revocation
return { accessToken, refreshToken };
}
async refresh(refreshToken: string) {
let payload: { sub: number };
try {
payload = await this.jwtService.verifyAsync(refreshToken, {
secret: process.env.JWT_REFRESH_SECRET,
});
} catch {
throw new UnauthorizedException('Invalid or expired refresh token');
}
const storedToken = this.validRefreshTokens.get(payload.sub);
if (storedToken !== refreshToken) {
throw new UnauthorizedException('Refresh token has been revoked');
}
// Rotation: issue a brand-new access token AND a new refresh token
const newAccessToken = await this.jwtService.signAsync(
{ sub: payload.sub },
{ expiresIn: '15m' },
);
const newRefreshToken = await this.jwtService.signAsync(
{ sub: payload.sub },
{ expiresIn: '7d', secret: process.env.JWT_REFRESH_SECRET },
);
this.validRefreshTokens.set(payload.sub, newRefreshToken); // rotate: invalidate the old one
return { accessToken: newAccessToken, refreshToken: newRefreshToken };
}
logout(userId: number) {
this.validRefreshTokens.delete(userId); // explicit revocation
}
}
login() issues two separately signed tokens: a 15-minute access token and a 7-day refresh token, signed with a distinct secret (JWT_REFRESH_SECRET) specifically for refresh tokens, and stores the refresh token in a server-side map keyed by user ID (a real application would use a database table). refresh() first verifies the incoming refresh token's signature and expiration, then critically checks it against the stored value, rejecting it if it doesn't match, which catches cases where a token has already been rotated or explicitly revoked. On success, it demonstrates rotation: issuing both a new access token and a brand-new refresh token, storing the new refresh token and effectively invalidating the old one. logout() shows explicit revocation, simply removing the stored refresh token so it can never be used again, even if it hasn't expired yet.
Real-World NestJS Refresh Token Tutorial Industry Examples
- Banking and fintech apps almost universally implement refresh token rotation exactly as shown here, since detecting refresh token reuse is a strong signal of token theft in high-security contexts.
- Mobile applications rely heavily on refresh tokens to keep users logged in across app restarts for days or weeks, without needing to store long-lived, high-risk access tokens on the device.
- SaaS platforms commonly tie refresh token revocation to security-sensitive actions like password changes, immediately invalidating all existing refresh tokens for a user the moment their password is updated.
- Enterprise applications with strict session management requirements often build an admin interface allowing support staff to explicitly revoke a specific user's refresh token, effectively force-logging them out remotely.
NestJS Refresh Token Tutorial Interview Questions and Answers
Q1. Why do applications use both an access token and a separate refresh token instead of just one long-lived token?
Short answer: A single long-lived token would create serious security risk if stolen, since it remains valid for a long time. Splitting responsibilities lets the access token stay short-lived, limiting damage from theft, while the refresh token, used only to obtain new access tokens, can be more carefully controlled and revoked server-side without forcing the user to log in constantly.
Detailed explanation: The core tension refresh tokens resolve is this: short-lived access tokens limit the damage from a stolen token, since it naturally expires quickly, but if that were the only token available, users would need to re-enter their credentials constantly, creating a frustrating experience. The solution is issuing two tokens at login instead of one: a short-lived access token (used for actual API requests) and a separate, longer-lived refresh token (used only for one specific purpose: obtaining a new access token). When the access token eventually expires, rather than forcing the user to log in again, the client application sends the refresh token to a dedicated endpoint, commonly /auth/refresh. The server verifies this refresh token is still valid, typically checking both its signature/expiration and, ideally, whether it matches a record still marked as valid in a server-side store (such as a database table of currently issued refresh tokens), and if everything checks out, issues a brand-new access token, allowing the user's session to continue seamlessly without any visible interruption. A critical security enhancement on top of this basic pattern is refresh token rotation. Instead of allowing the same refresh token to be reused indefinitely until its own (longer) expiration, each time a refresh token is used to obtain a new access token, the server also issues a brand-new refresh token and immediately invalidates the old one. This means if an attacker somehow steals a refresh token and uses it, the legitimate user's next refresh attempt (using their now-invalidated original token) will fail, providing a signal that something is wrong, and in more sophisticated implementations, this can trigger automatically revoking the entire token family as a precaution. Storing refresh tokens server-side (rather than relying purely on their own expiration) is what makes explicit revocation possible: if a user logs out, changes their password, or an administrator needs to force a specific session to end, deleting or invalidating the corresponding refresh token record immediately prevents that token from being used to obtain further access tokens, something a pure, stateless JWT-only approach cannot achieve without additional infrastructure like a token blocklist.
Practical example: Banking and fintech apps almost universally implement refresh token rotation exactly as shown here, since detecting refresh token reuse is a strong signal of token theft in high-security contexts.
Interview tip: Access tokens: short-lived, used for API requests. Refresh tokens: longer-lived, used only to obtain new access tokens.
Revision hook: The access-token-plus-refresh-token pattern balances security (short-lived access tokens) with usability (no constant re-login).
Q2. What is refresh token rotation and why does it improve security?
Short answer: Refresh token rotation issues a brand-new refresh token every time the current one is used to obtain a new access token, immediately invalidating the previous refresh token. This improves security because if a stolen refresh token is ever used by an attacker, the legitimate user's next refresh attempt with their now-invalid original token fails, providing a detectable signal of compromise.
Detailed explanation: login() issues two separately signed tokens: a 15-minute access token and a 7-day refresh token, signed with a distinct secret (JWT_REFRESH_SECRET) specifically for refresh tokens, and stores the refresh token in a server-side map keyed by user ID (a real application would use a database table). refresh() first verifies the incoming refresh token's signature and expiration, then critically checks it against the stored value, rejecting it if it doesn't match, which catches cases where a token has already been rotated or explicitly revoked. On success, it demonstrates rotation: issuing both a new access token and a brand-new refresh token, storing the new refresh token and effectively invalidating the old one. logout() shows explicit revocation, simply removing the stored refresh token so it can never be used again, even if it hasn't expired yet.
Practical example: Mobile applications rely heavily on refresh tokens to keep users logged in across app restarts for days or weeks, without needing to store long-lived, high-risk access tokens on the device.
Interview tip: Refresh token rotation: each use invalidates the old refresh token and issues a new one, detecting token theft.
Revision hook: Rotation transforms refresh tokens from a static long-lived risk into a self-detecting security mechanism.
Q3. Why is it important to store refresh tokens server-side rather than relying purely on their expiration?
Short answer: Server-side storage enables explicit revocation: if a user logs out, changes their password, or an account is compromised, the corresponding refresh token record can be immediately invalidated, preventing further use even though the token itself hasn't technically expired yet, something a purely stateless approach cannot achieve.
Detailed explanation: Refresh tokens solve the tension between security and usability created by short-lived JWT access tokens: rather than forcing users to log in every time their access token expires, a separate, longer-lived refresh token can be exchanged at a dedicated endpoint for a new access token. Storing refresh tokens server-side, rather than trusting them purely based on their own expiration, enables explicit revocation on events like logout or password changes. Refresh token rotation, issuing a brand-new refresh token (and invalidating the old one) on every use, adds a further security layer, since a stolen refresh token being reused after the legitimate user has already rotated it provides a clear, actionable signal of compromise. Together, this pattern forms the standard, security-conscious foundation for real-world JWT-based authentication systems.
Practical example: SaaS platforms commonly tie refresh token revocation to security-sensitive actions like password changes, immediately invalidating all existing refresh tokens for a user the moment their password is updated.
Interview tip: Server-side refresh token storage enables explicit revocation (logout, password change) unlike pure JWT-only approaches.
Revision hook: Server-side storage of refresh tokens is what actually makes revocation possible, a capability pure JWTs lack on their own.
Q4. How would you implement a /auth/refresh endpoint in NestJS?
Short answer: The endpoint accepts a refresh token, verifies its signature and expiration using JwtService (typically with a separate secret from access tokens), checks it against a server-side store to confirm it hasn't already been rotated or revoked, and if valid, issues both a new access token and, following rotation best practices, a new refresh token, updating the stored value accordingly.
Detailed explanation: The core tension refresh tokens resolve is this: short-lived access tokens limit the damage from a stolen token, since it naturally expires quickly, but if that were the only token available, users would need to re-enter their credentials constantly, creating a frustrating experience. The solution is issuing two tokens at login instead of one: a short-lived access token (used for actual API requests) and a separate, longer-lived refresh token (used only for one specific purpose: obtaining a new access token). When the access token eventually expires, rather than forcing the user to log in again, the client application sends the refresh token to a dedicated endpoint, commonly /auth/refresh. The server verifies this refresh token is still valid, typically checking both its signature/expiration and, ideally, whether it matches a record still marked as valid in a server-side store (such as a database table of currently issued refresh tokens), and if everything checks out, issues a brand-new access token, allowing the user's session to continue seamlessly without any visible interruption. A critical security enhancement on top of this basic pattern is refresh token rotation. Instead of allowing the same refresh token to be reused indefinitely until its own (longer) expiration, each time a refresh token is used to obtain a new access token, the server also issues a brand-new refresh token and immediately invalidates the old one. This means if an attacker somehow steals a refresh token and uses it, the legitimate user's next refresh attempt (using their now-invalidated original token) will fail, providing a signal that something is wrong, and in more sophisticated implementations, this can trigger automatically revoking the entire token family as a precaution. Storing refresh tokens server-side (rather than relying purely on their own expiration) is what makes explicit revocation possible: if a user logs out, changes their password, or an administrator needs to force a specific session to end, deleting or invalidating the corresponding refresh token record immediately prevents that token from being used to obtain further access tokens, something a pure, stateless JWT-only approach cannot achieve without additional infrastructure like a token blocklist.
Practical example: Enterprise applications with strict session management requirements often build an admin interface allowing support staff to explicitly revoke a specific user's refresh token, effectively force-logging them out remotely.
Interview tip: Refresh and access tokens should ideally use different secrets for independent security management.
Revision hook: This pattern is the industry-standard baseline for any application that takes authentication security seriously.
NestJS Refresh Token Tutorial MCQs and Practice Questions
1. What is the primary purpose of a refresh token?
- To authenticate individual API requests directly
- To obtain a new access token without requiring the user to log in again
- To replace passwords entirely
- To encrypt the access token
Answer: B. To obtain a new access token without requiring the user to log in again
Explanation: A refresh token's sole purpose is to be exchanged for a new access token once the current one expires, allowing a user's session to continue without repeated logins.
Concept link: Access tokens: short-lived, used for API requests. Refresh tokens: longer-lived, used only to obtain new access tokens.
Why this matters: The access-token-plus-refresh-token pattern balances security (short-lived access tokens) with usability (no constant re-login).
2. Why are access tokens typically kept short-lived?
- To reduce server load
- To limit the window of exposure if the token is stolen
- Because JWTs cannot be long-lived
- To save database storage space
Answer: B. To limit the window of exposure if the token is stolen
Explanation: Keeping access tokens short-lived means a stolen token naturally becomes useless quickly, significantly reducing the potential damage compared to a long-lived token.
Concept link: Refresh token rotation: each use invalidates the old refresh token and issues a new one, detecting token theft.
Why this matters: Rotation transforms refresh tokens from a static long-lived risk into a self-detecting security mechanism.
3. What is refresh token rotation?
- Encrypting the refresh token differently each time
- Issuing a brand-new refresh token every time the current one is used, invalidating the old one
- Rotating which server handles refresh requests
- Changing the refresh token's expiration randomly
Answer: B. Issuing a brand-new refresh token every time the current one is used, invalidating the old one
Explanation: Refresh token rotation invalidates the previous refresh token and issues a new one on every use, helping detect and limit the impact of a stolen refresh token being reused.
Concept link: Server-side refresh token storage enables explicit revocation (logout, password change) unlike pure JWT-only approaches.
Why this matters: Server-side storage of refresh tokens is what actually makes revocation possible, a capability pure JWTs lack on their own.
4. What does storing refresh tokens server-side enable that a purely stateless approach cannot?
- Faster token verification
- Explicit revocation before the token's natural expiration
- Automatic password resets
- Elimination of the need for access tokens
Answer: B. Explicit revocation before the token's natural expiration
Explanation: A server-side record of valid refresh tokens allows immediate revocation, such as on logout or password change, which a purely stateless JWT verification approach (checking only signature and expiry) cannot provide on its own.
Concept link: Refresh and access tokens should ideally use different secrets for independent security management.
Why this matters: This pattern is the industry-standard baseline for any application that takes authentication security seriously.
Common NestJS Refresh Token Tutorial Mistakes to Avoid
- Using the same secret to sign both access and refresh tokens, which reduces the ability to independently manage or invalidate each token type.
- Storing refresh tokens purely client-side without any server-side record, making explicit revocation on logout or password change impossible.
- Skipping refresh token rotation, allowing the same refresh token to be reused indefinitely until its own long expiration, increasing the impact of a stolen token.
- Setting an excessively long refresh token lifespan without any server-side revocation mechanism, effectively creating a very long-lived credential with the risks of a permanent token.
NestJS Refresh Token Tutorial: Interview Notes and Exam Tips
- Access tokens: short-lived, used for API requests. Refresh tokens: longer-lived, used only to obtain new access tokens.
- Refresh token rotation: each use invalidates the old refresh token and issues a new one, detecting token theft.
- Server-side refresh token storage enables explicit revocation (logout, password change) unlike pure JWT-only approaches.
- Refresh and access tokens should ideally use different secrets for independent security management.
Key NestJS Refresh Token Tutorial Takeaways
- The access-token-plus-refresh-token pattern balances security (short-lived access tokens) with usability (no constant re-login).
- Rotation transforms refresh tokens from a static long-lived risk into a self-detecting security mechanism.
- Server-side storage of refresh tokens is what actually makes revocation possible, a capability pure JWTs lack on their own.
- This pattern is the industry-standard baseline for any application that takes authentication security seriously.
NestJS Refresh Token Tutorial: Summary
Refresh tokens solve the tension between security and usability created by short-lived JWT access tokens: rather than forcing users to log in every time their access token expires, a separate, longer-lived refresh token can be exchanged at a dedicated endpoint for a new access token. Storing refresh tokens server-side, rather than trusting them purely based on their own expiration, enables explicit revocation on events like logout or password changes. Refresh token rotation, issuing a brand-new refresh token (and invalidating the old one) on every use, adds a further security layer, since a stolen refresh token being reused after the legitimate user has already rotated it provides a clear, actionable signal of compromise. Together, this pattern forms the standard, security-conscious foundation for real-world JWT-based authentication systems.