From harness-claude
Implements OWASP-compliant authentication: Argon2/bcrypt password hashing, secure JWT handling, refresh token rotation against credential stuffing, session hijacking, token theft. For login flows, API auth, session management.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Implement authentication that resists credential stuffing, session hijacking, and token theft
Provides patterns for JWT/session/OAuth auth, RBAC authorization, encryption, secrets, CORS, rate limiting, and security checklists for web apps.
Guides authentication implementation with JWT best practices, OAuth 2.0/OIDC flows, Passkeys/FIDO2/WebAuthn, MFA patterns, and secure session management. Use for login systems, SSO, passwordless auth, or security reviews.
Implements authentication and authorization patterns including JWT, OAuth2, session management, and RBAC for secure APIs and apps. Use when building auth systems, securing endpoints, or debugging issues.
Share bugs, ideas, or general feedback.
Implement authentication that resists credential stuffing, session hijacking, and token theft
Never store plaintext or MD5/SHA1 passwords. Use adaptive hashing:
import * as argon2 from 'argon2';
import * as bcrypt from 'bcrypt';
// Argon2id — preferred (winner of Password Hashing Competition)
async function hashPassword(password: string): Promise<string> {
return argon2.hash(password, {
type: argon2.argon2id,
memoryCost: 65536, // 64 MiB
timeCost: 3,
parallelism: 4,
});
}
async function verifyPassword(hash: string, password: string): Promise<boolean> {
return argon2.verify(hash, password);
}
// bcrypt — acceptable alternative, saltRounds >= 12
const hash = await bcrypt.hash(password, 12);
const valid = await bcrypt.compare(password, hash);
import jwt from 'jsonwebtoken';
// Sign — short-lived access tokens + long-lived refresh tokens
function signAccessToken(payload: { sub: string; roles: string[] }): string {
return jwt.sign(payload, process.env.JWT_SECRET!, {
expiresIn: '15m', // short-lived
algorithm: 'HS256',
issuer: 'api.example.com',
audience: 'api.example.com',
});
}
// Verify — always validate algorithm explicitly to prevent alg:none attacks
function verifyToken(token: string): jwt.JwtPayload {
return jwt.verify(token, process.env.JWT_SECRET!, {
algorithms: ['HS256'], // NEVER omit this
issuer: 'api.example.com',
audience: 'api.example.com',
}) as jwt.JwtPayload;
}
Rotate refresh tokens on every use. Detect reuse (theft signal).
async function refreshAccessToken(refreshToken: string) {
const stored = await db.refreshToken.findUnique({ where: { token: refreshToken } });
if (!stored) throw new UnauthorizedException('Invalid refresh token');
if (stored.usedAt) {
// Reuse detected — potential theft. Revoke all tokens for this user.
await db.refreshToken.deleteMany({ where: { userId: stored.userId } });
throw new UnauthorizedException('Token reuse detected');
}
if (stored.expiresAt < new Date()) {
throw new UnauthorizedException('Refresh token expired');
}
// Mark old token as used
await db.refreshToken.update({ where: { id: stored.id }, data: { usedAt: new Date() } });
// Issue new token pair
const newRefreshToken = crypto.randomBytes(32).toString('hex');
await db.refreshToken.create({
data: {
token: newRefreshToken,
userId: stored.userId,
expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000), // 30 days
},
});
return {
accessToken: signAccessToken({ sub: stored.userId, roles: stored.roles }),
refreshToken: newRefreshToken,
};
}
For cookie-based sessions:
import session from 'express-session';
import RedisStore from 'connect-redis';
app.use(
session({
store: new RedisStore({ client: redisClient }),
secret: process.env.SESSION_SECRET!,
resave: false,
saveUninitialized: false,
cookie: {
httpOnly: true, // prevent JS access
secure: true, // HTTPS only
sameSite: 'strict', // CSRF protection
maxAge: 15 * 60 * 1000, // 15 minutes
},
genid: () => crypto.randomUUID(), // cryptographically random IDs
})
);
// Regenerate session ID after login to prevent session fixation
app.post('/login', async (req, res) => {
const user = await authenticate(req.body);
req.session.regenerate((err) => {
if (err) throw err;
req.session.userId = user.id;
res.json({ ok: true });
});
});
import rateLimit from 'express-rate-limit';
// Limit login attempts per IP
const loginLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 5,
skipSuccessfulRequests: true,
standardHeaders: true,
message: 'Too many login attempts, please try again later',
});
app.post('/login', loginLimiter, loginHandler);
Token storage:
Credential stuffing defenses:
Logout:
jti claim, or use short-lived tokens with refresh rotationMulti-factor authentication:
otplib@simplewebauthn/server for phishing-resistant authhttps://owasp.org/www-project-top-ten/