Implement secure JWT (JSON Web Token) authentication in Node.js applications with access/refresh tokens and role-based access control
Adds secure JWT authentication with access/refresh tokens and role-based access control to Node.js apps. Triggers when building user authentication systems, protecting API routes, or implementing token-based security in Express applications.
/plugin marketplace add pluginagentmarketplace/custom-plugin-nodejs/plugin install nodejs-developer-plugin@pluginagentmarketplace-nodejsThis skill inherits all available tools. When active, it can use any tool Claude has access to.
assets/config.yamlreferences/GUIDE.mdscripts/helper.pyImplement secure, scalable authentication in Node.js applications using JSON Web Tokens.
JWT authentication in 4 steps:
npm install jsonwebtoken bcryptjsconst jwt = require('jsonwebtoken');
function generateToken(userId) {
return jwt.sign(
{ id: userId },
process.env.JWT_SECRET,
{ expiresIn: '1h' }
);
}
function generateRefreshToken(userId) {
return jwt.sign(
{ id: userId },
process.env.JWT_REFRESH_SECRET,
{ expiresIn: '7d' }
);
}
const bcrypt = require('bcryptjs');
async function register(req, res) {
const { email, password, name } = req.body;
// Check if user exists
const existingUser = await User.findOne({ email });
if (existingUser) {
return res.status(409).json({ error: 'User already exists' });
}
// Hash password
const hashedPassword = await bcrypt.hash(password, 10);
// Create user
const user = await User.create({
email,
password: hashedPassword,
name
});
// Generate tokens
const accessToken = generateToken(user._id);
const refreshToken = generateRefreshToken(user._id);
res.status(201).json({
user: { id: user._id, email, name },
accessToken,
refreshToken
});
}
async function login(req, res) {
const { email, password } = req.body;
// Find user
const user = await User.findOne({ email });
if (!user) {
return res.status(401).json({ error: 'Invalid credentials' });
}
// Verify password
const isValid = await bcrypt.compare(password, user.password);
if (!isValid) {
return res.status(401).json({ error: 'Invalid credentials' });
}
// Generate tokens
const accessToken = generateToken(user._id);
const refreshToken = generateRefreshToken(user._id);
res.json({
user: { id: user._id, email: user.email },
accessToken,
refreshToken
});
}
async function authenticate(req, res, next) {
try {
// Get token from header
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({ error: 'No token provided' });
}
const token = authHeader.split(' ')[1];
// Verify token
const decoded = jwt.verify(token, process.env.JWT_SECRET);
// Get user
const user = await User.findById(decoded.id).select('-password');
if (!user) {
return res.status(401).json({ error: 'User not found' });
}
req.user = user;
next();
} catch (error) {
res.status(401).json({ error: 'Invalid token' });
}
}
// Usage
router.get('/profile', authenticate, getProfile);
const authorize = (...roles) => {
return (req, res, next) => {
if (!req.user) {
return res.status(401).json({ error: 'Not authenticated' });
}
if (!roles.includes(req.user.role)) {
return res.status(403).json({ error: 'Insufficient permissions' });
}
next();
};
};
// Usage
router.delete('/users/:id',
authenticate,
authorize('admin', 'moderator'),
deleteUser
);
async function refreshAccessToken(req, res) {
const { refreshToken } = req.body;
try {
const decoded = jwt.verify(refreshToken, process.env.JWT_REFRESH_SECRET);
// Verify refresh token exists in database
const stored = await RefreshToken.findOne({
token: refreshToken,
userId: decoded.id
});
if (!stored) {
return res.status(401).json({ error: 'Invalid refresh token' });
}
// Generate new access token
const accessToken = generateToken(decoded.id);
res.json({ accessToken });
} catch (error) {
res.status(401).json({ error: 'Token refresh failed' });
}
}
async function requestPasswordReset(req, res) {
const { email } = req.body;
const user = await User.findOne({ email });
if (!user) {
return res.status(404).json({ error: 'User not found' });
}
// Generate reset token (short expiry)
const resetToken = jwt.sign(
{ id: user._id, purpose: 'reset' },
process.env.JWT_SECRET,
{ expiresIn: '15m' }
);
// Send email with reset link
await sendResetEmail(user.email, resetToken);
res.json({ message: 'Reset email sent' });
}
async function resetPassword(req, res) {
const { token, newPassword } = req.body;
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
if (decoded.purpose !== 'reset') {
return res.status(400).json({ error: 'Invalid token' });
}
const hashedPassword = await bcrypt.hash(newPassword, 10);
await User.findByIdAndUpdate(decoded.id, { password: hashedPassword });
res.json({ message: 'Password reset successful' });
} catch (error) {
res.status(400).json({ error: 'Invalid or expired token' });
}
}
header.payload.signature
Header (base64):
{
"alg": "HS256",
"typ": "JWT"
}
Payload (base64):
{
"id": "user123",
"iat": 1516239022,
"exp": 1516242622
}
Signature:
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)
iss - Issuersub - Subject (user ID)aud - Audienceexp - Expiration timeiat - Issued atnbf - Not beforeUse JWT authentication when:
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.