๐How JWT, Access Tokens & Refresh Tokens Work โ Node.js vs AWS Lambda

๐ง What Are Access Tokens and Refresh Tokens?
| Token Type | Purpose | Lifespan | Stored Where |
| Access Token | Grants access to protected resources | Short-lived (e.g., 15 min) | In memory or localStorage |
| Refresh Token | Gets a new access token when expires | Long-lived (e.g., 7 days) | Secure httpOnly cookie / DB |
๐ฏ Real-Life Analogy: The Movie Hall Entry System
Imagine walking into a movie theater.
๐๏ธ At the entrance, you show your movie ticket to the guard โ this is your Access Token.
It allows you to enter and enjoy the movie.๐ But the ticket is only valid for one movie (say 2 hours). If you want to watch another movie or re-enter after stepping out, you need a new ticket.
๐ค Instead of going to the main counter again and proving your identity (logging in), you carry a VIP pass or membership card (Refresh Token).
๐ซ You present this refresh token at a special desk, and they issue you a new movie ticket (new Access Token) without requiring you to log in again.
๐ Key Takeaway
Access Token = Movie Ticket
Short-lived, used to access services
Shown often (like at the theatre gate)
Refresh Token = Membership Card
Long-lived, used to get new access tokens
Hidden and securely stored
๐ JWT Authentication Flow with Access + Refresh Tokens
User logs in โ Receives both tokens
An access token is used to call protected APIs
If the access token expires, the client sends the refresh token to get a new access token
Refresh token is stored securely (e.g., in cookies)
If the refresh token is invalid or expired, โ user must log in again
๐๏ธ Implementation in Node.js (Express)
๐ฆ Install
npm install express jsonwebtoken cookie-parser
๐ Token Functions (auth.js)
const jwt = require("jsonwebtoken");
const ACCESS_SECRET = "access-secret";
const REFRESH_SECRET = "refresh-secret";
function generateTokens(user) {
const accessToken = jwt.sign(user, ACCESS_SECRET, { expiresIn: "15m" });
const refreshToken = jwt.sign(user, REFRESH_SECRET, { expiresIn: "7d" });
return { accessToken, refreshToken };
}
function verifyAccess(token) {
return jwt.verify(token, ACCESS_SECRET);
}
function verifyRefresh(token) {
return jwt.verify(token, REFRESH_SECRET);
}
module.exports = { generateTokens, verifyAccess, verifyRefresh };
๐ง Express Server (index.js)
const express = require("express");
const cookieParser = require("cookie-parser");
const { generateTokens, verifyAccess, verifyRefresh } = require("./auth");
const app = express();
app.use(express.json());
app.use(cookieParser());
app.post("/login", (req, res) => {
const user = { email: req.body.email };
const { accessToken, refreshToken } = generateTokens(user);
res.cookie("refreshToken", refreshToken, { httpOnly: true });
res.json({ accessToken });
});
app.get("/protected", (req, res) => {
const auth = req.headers.authorization;
if (!auth) return res.sendStatus(401);
try {
const user = verifyAccess(auth.split(" ")[1]);
res.json({ message: "Access granted", user });
} catch {
res.status(403).json({ message: "Access token expired" });
}
});
app.post("/refresh", (req, res) => {
const refreshToken = req.cookies.refreshToken;
if (!refreshToken) return res.sendStatus(401);
try {
const user = verifyRefresh(refreshToken);
const { accessToken } = generateTokens(user);
res.json({ accessToken });
} catch {
res.status(403).json({ message: "Refresh token invalid" });
}
});
๐ JWT with Refresh Token in AWS Lambda
Lambda is stateless โ so no sessions! But you can use JWTs the same way.
๐๏ธ Structure
pythonCopyEditlambda-jwt-auth/
โโโ handler.js
โโโ auth.js
๐ auth.js (Same logic as Express)
const jwt = require("jsonwebtoken");
const ACCESS_SECRET = "access-secret";
const REFRESH_SECRET = "refresh-secret";
function generateTokens(user) {
const accessToken = jwt.sign(user, ACCESS_SECRET, { expiresIn: "15m" });
const refreshToken = jwt.sign(user, REFRESH_SECRET, { expiresIn: "7d" });
return { accessToken, refreshToken };
}
function verifyAccess(token) {
return jwt.verify(token, ACCESS_SECRET);
}
function verifyRefresh(token) {
return jwt.verify(token, REFRESH_SECRET);
}
module.exports = { generateTokens, verifyAccess, verifyRefresh };
๐ง handler.js
const { generateTokens, verifyAccess, verifyRefresh } = require("./auth");
exports.login = async (event) => {
const body = JSON.parse(event.body);
const user = { email: body.email };
const { accessToken, refreshToken } = generateTokens(user);
return {
statusCode: 200,
headers: {
"Set-Cookie": `refreshToken=${refreshToken}; HttpOnly; Path=/; Max-Age=604800`,
"Content-Type": "application/json",
},
body: JSON.stringify({ accessToken }),
};
};
exports.protected = async (event) => {
const authHeader = event.headers.authorization;
try {
const user = verifyAccess(authHeader.split(" ")[1]);
return {
statusCode: 200,
body: JSON.stringify({ message: "Access granted", user }),
};
} catch {
return { statusCode: 403, body: JSON.stringify({ error: "Token expired" }) };
}
};
exports.refresh = async (event) => {
const cookie = event.headers.cookie || "";
const token = cookie.split("refreshToken=")[1];
try {
const user = verifyRefresh(token);
const { accessToken } = generateTokens(user);
return {
statusCode: 200,
body: JSON.stringify({ accessToken }),
};
} catch {
return { statusCode: 403, body: JSON.stringify({ error: "Refresh token invalid" }) };
}
};
๐ Node.js Server vs AWS Lambda (with Refresh Token)
| Feature | Node.js + JWT + Refresh Token | AWS Lambda + API Gateway + JWT |
| Token Handling | In memory or a cookie | Via API Gateway headers/cookies |
| Refresh Logic | Simple via cookie | Cookie needs to be manually parsed |
| Storage Option | Can use Redis for refresh token blacklist | Store tokens in DB or skip blacklist |
| Deployment | Runs continuously | Cold start but lower cost |
| Use Case | Complex backend apps | Scalable microservices, low-traffic auth |
๐ง Summary
Access tokens are short-lived and used to access protected resources.
Refresh tokens are long-lived and used to generate new access tokens without login.
JWTs help us implement stateless authentication in both Node.js servers and AWS Lambda functions.
Store refresh tokens securely (e.g., httpOnly cookies) and never expose them on the frontend.



