Claude-skill-registry backend-passport-js
Authentication middleware for Express.js and Node.js applications. Use when building Express APIs that need JWT authentication, OAuth, or custom auth strategies. Provides 500+ authentication strategies. Choose Passport.js over Auth.js for Express backends, pure API servers, or when you need maximum control over auth flow.
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/backend-passport-js" ~/.claude/skills/majiayu000-claude-skill-registry-backend-passport-js && rm -rf "$T"
manifest:
skills/data/backend-passport-js/SKILL.mdsource content
Passport.js (Express Authentication)
Overview
Passport.js is the de-facto standard authentication middleware for Express.js. It uses a strategy pattern with 500+ available strategies for different auth mechanisms.
Version: passport@0.7.x, passport-jwt@4.x
Use case: Express.js applications, custom auth flows, pure APIs
Key Benefit: Maximum flexibility, works with any Express app, battle-tested in production.
When to Use This Skill
✅ Use Passport.js when:
- Building Express.js REST APIs
- Need JWT-based authentication for APIs
- Want 500+ auth strategies available
- Require custom authentication logic
- Building microservices or pure backend APIs
- Not using Next.js
❌ Use Auth.js instead when:
- Building Next.js applications
- Want minimal configuration
- Need quick OAuth setup
- Want serverless/Edge support
Quick Start
Installation
npm install passport passport-jwt jsonwebtoken npm install -D @types/passport @types/passport-jwt @types/jsonwebtoken
JWT Strategy Setup
// src/strategies/jwt.strategy.ts import passport from 'passport'; import { Strategy as JwtStrategy, ExtractJwt } from 'passport-jwt'; import { prisma } from '../lib/prisma'; const options = { jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), secretOrKey: process.env.JWT_SECRET!, }; passport.use(new JwtStrategy(options, async (payload, done) => { try { const user = await prisma.user.findUnique({ where: { id: payload.sub } }); if (!user) { return done(null, false); } return done(null, user); } catch (error) { return done(error, false); } })); export default passport;
Express Integration
Complete Setup
// src/app.ts import express from 'express'; import passport from 'passport'; import jwt from 'jsonwebtoken'; import { hash, verify } from 'argon2'; import './strategies/jwt.strategy'; import { prisma } from './lib/prisma'; const app = express(); app.use(express.json()); app.use(passport.initialize()); // Register app.post('/auth/register', async (req, res) => { const { email, password, name } = req.body; const hashedPassword = await hash(password); const user = await prisma.user.create({ data: { email, password: hashedPassword, name }, }); res.status(201).json({ id: user.id, email: user.email }); }); // Login app.post('/auth/login', async (req, res) => { const { email, password } = req.body; const user = await prisma.user.findUnique({ where: { email } }); if (!user || !user.password) { return res.status(401).json({ message: 'Invalid credentials' }); } const valid = await verify(user.password, password); if (!valid) { return res.status(401).json({ message: 'Invalid credentials' }); } const accessToken = jwt.sign( { sub: user.id, email: user.email, role: user.role }, process.env.JWT_SECRET!, { expiresIn: '15m' } ); const refreshToken = jwt.sign( { sub: user.id }, process.env.JWT_REFRESH_SECRET!, { expiresIn: '7d' } ); res.json({ accessToken, refreshToken }); }); // Protected route app.get('/api/profile', passport.authenticate('jwt', { session: false }), (req, res) => { res.json(req.user); } ); // Refresh token app.post('/auth/refresh', async (req, res) => { const { refreshToken } = req.body; try { const payload = jwt.verify(refreshToken, process.env.JWT_REFRESH_SECRET!) as { sub: string }; const user = await prisma.user.findUnique({ where: { id: payload.sub } }); if (!user) { return res.status(401).json({ message: 'Invalid token' }); } const accessToken = jwt.sign( { sub: user.id, email: user.email, role: user.role }, process.env.JWT_SECRET!, { expiresIn: '15m' } ); res.json({ accessToken }); } catch { res.status(401).json({ message: 'Invalid token' }); } }); app.listen(3000);
Middleware Helpers
Role-Based Authorization
// src/middleware/auth.middleware.ts import { Request, Response, NextFunction } from 'express'; import passport from 'passport'; // Authenticate and attach user export const authenticate = passport.authenticate('jwt', { session: false }); // Check specific role export function requireRole(...roles: string[]) { return (req: Request, res: Response, next: NextFunction) => { const user = req.user as { role: string } | undefined; if (!user || !roles.includes(user.role)) { return res.status(403).json({ message: 'Forbidden' }); } next(); }; } // Usage: app.get('/admin/users', authenticate, requireRole('admin'), (req, res) => { /* ... */ } );
Optional Authentication
export function optionalAuth(req: Request, res: Response, next: NextFunction) { passport.authenticate('jwt', { session: false }, (err, user) => { if (user) req.user = user; next(); })(req, res, next); } // Route works with or without auth app.get('/api/posts', optionalAuth, (req, res) => { const userId = (req.user as { id: string })?.id; // Show public posts, or personalized if logged in });
Integration with tRPC (Express)
// src/server/context.ts import { CreateExpressContextOptions } from '@trpc/server/adapters/express'; import { prisma } from '../lib/prisma'; export const createContext = ({ req }: CreateExpressContextOptions) => ({ user: req.user, // Populated by Passport middleware prisma, }); export type Context = ReturnType<typeof createContext>;
// src/server/index.ts import { createExpressMiddleware } from '@trpc/server/adapters/express'; import passport from 'passport'; // Apply Passport to all /trpc routes app.use('/trpc', passport.authenticate('jwt', { session: false, failWithError: false }), createExpressMiddleware({ router: appRouter, createContext, }) );
JWT Token Structure
// Access token payload interface AccessTokenPayload { sub: string; // User ID email: string; role: string; iat: number; // Issued at exp: number; // Expiration } // Generate tokens function generateTokens(user: User) { const accessToken = jwt.sign( { sub: user.id, email: user.email, role: user.role }, process.env.JWT_SECRET!, { expiresIn: '15m' } ); const refreshToken = jwt.sign( { sub: user.id }, process.env.JWT_REFRESH_SECRET!, { expiresIn: '7d' } ); return { accessToken, refreshToken }; }
Type Definitions
// src/types/express.d.ts import { User } from '@prisma/client'; declare global { namespace Express { interface User { id: string; email: string; role: string; } } }
Environment Variables
JWT_SECRET=your-access-token-secret-min-32-chars JWT_REFRESH_SECRET=your-refresh-token-secret-min-32-chars
Rules
Do ✅
- Use
for stateless JWT authsession: false - Hash passwords with argon2 or bcrypt
- Use short-lived access tokens (15min)
- Use refresh tokens for long sessions
- Store refresh tokens securely (httpOnly cookie)
- Validate JWT on every request
Avoid ❌
- Storing sensitive data in JWT payload
- Using long-lived access tokens
- Storing tokens in localStorage (XSS risk)
- Using weak secrets
- Skipping password hashing
Common Strategies
| Strategy | Package | Use Case |
|---|---|---|
| JWT | | API authentication |
| Local | | Username/password |
| OAuth2 | | Generic OAuth |
| Google login | |
| GitHub | | GitHub login |
Auth.js vs Passport.js Decision Tree
| Scenario | Recommendation |
|---|---|
| Next.js App Router | Auth.js |
| Express.js API | Passport.js |
| Quick OAuth setup | Auth.js |
| Custom auth logic | Passport.js |
| Serverless/Edge | Auth.js |
| Multiple strategies | Passport.js |
| Pure JWT API | Passport.js |
Troubleshooting
"401 on every request": → Check Authorization header format: "Bearer <token>" → Verify JWT_SECRET matches between sign and verify → Check token expiration "req.user is undefined": → Ensure passport.initialize() is called → Check authenticate middleware is applied → Verify strategy name matches "CORS issues with auth": → Set credentials: true in CORS config → Include origin whitelist → Allow Authorization header "Token not refreshing": → Check refresh token secret is different → Verify refresh token hasn't expired → Store refresh token in httpOnly cookie
File Structure
src/ ├── strategies/ │ └── jwt.strategy.ts # JWT strategy config ├── middleware/ │ └── auth.middleware.ts # Auth helpers ├── routes/ │ └── auth.routes.ts # Auth endpoints └── types/ └── express.d.ts # Type extensions
References
- https://passportjs.org — Official documentation
- https://www.npmjs.com/package/passport-jwt — JWT strategy
- http://www.passportjs.org/packages/ — All strategies