Secure Auth Patterns

Implement secure authentication and authorization using JWT, OAuth2, session management, RBAC, permissions, and resource ownership verification.

MIT-0 · Free to use, modify, and redistribute. No attribution required.
0 · 501 · 0 current installs · 0 all-time installs
MIT-0
Security Scan
VirusTotalVirusTotal
Benign
View report →
OpenClawOpenClaw
Suspicious
high confidence
Purpose & Capability
Name and description align with the included content: the SKILL.md and implementation playbook provide JWT, session, OAuth2, RBAC, and refresh-token patterns and code samples consistent with an authentication/authorization guidance skill. There are no unrelated binaries or unrelated capabilities requested.
!
Instruction Scope
The runtime instructions contain many concrete code samples that reference environment secrets and runtime components (e.g., process.env.JWT_SECRET, JWT_REFRESH_SECRET, SESSION_SECRET, REDIS_URL, GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, FRONTEND_URL) but the skill does not declare these as required. The playbook also demonstrates redirecting access tokens in a query parameter (res.redirect(...?token=...)), which is an insecure pattern that can leak tokens via referrers or logs. Overall the instructions stay within auth/authz scope but assume access to secrets and external services without making those requirements explicit.
Install Mechanism
This is an instruction-only skill with no install spec and no code files to execute: lowest install risk. Nothing is downloaded or written to disk by the skill itself.
!
Credentials
Registry metadata lists no required environment variables or credentials, yet the code samples assume multiple sensitive environment values (JWT_SECRET, JWT_REFRESH_SECRET, SESSION_SECRET, REDIS_URL, GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, FRONTEND_URL, etc.). The absence of declared required secrets is an incoherence: if you run or copy these examples into a runtime, you'll need secrets; the skill does not request or document how those should be provided safely.
Persistence & Privilege
The skill does not request persistent presence (always: false), does not modify other skills or global agent config, and does not request elevated platform privileges. Autonomous invocation remains allowed (platform default), but there are no extra persistence flags set by this skill.
What to consider before installing
This playbook appears to be legitimate guidance on implementing auth/authz, but be aware of two things before you use it: (1) the examples assume many secrets and external endpoints (JWT_SECRET, JWT_REFRESH_SECRET, SESSION_SECRET, REDIS_URL, GOOGLE_CLIENT_ID/SECRET, FRONTEND_URL, etc.) even though the skill metadata lists none — you should not paste real secrets into the agent or public prompts and you should plan how to supply these values securely (environment-only, secret manager, etc.); (2) some shown patterns can leak tokens (redirecting access tokens in a URL query string is risky). If you plan to adopt the examples, review and adapt them to your platform's secure secret storage, avoid placing tokens in URLs (prefer HttpOnly secure cookies or one-time server-side exchanges), and validate the code in a controlled dev environment before deploying.

Like a lobster shell, security has layers — review code before you run it.

Current versionv1.0.0
Download zip
latestvk97273j4mk1y781mbqfcdbjdgh819qr1

License

MIT-0
Free to use, modify, and redistribute. No attribution required.

SKILL.md

Authentication & Authorization Patterns

Master authentication and authorization patterns including JWT, OAuth2, session management, and RBAC to build secure, scalable access control systems.

Description

USE WHEN:

  • Implementing user authentication systems
  • Securing REST or GraphQL APIs
  • Adding OAuth2/social login or SSO
  • Designing session management
  • Implementing RBAC or permission systems
  • Debugging authentication issues

DON'T USE WHEN:

  • Only need UI/login page styling
  • Task is infrastructure-only without identity concerns
  • Cannot change auth policies

Authentication vs Authorization

AuthN (Authentication)AuthZ (Authorization)
"Who are you?""What can you do?"
Verify identityCheck permissions
Issue credentialsEnforce policies
Login/logoutAccess control

Authentication Strategies

StrategyProsConsBest For
SessionSimple, secureStateful, scalingTraditional web apps
JWTStateless, scalableToken size, revocationAPIs, microservices
OAuth2/OIDCDelegated, SSOComplex setupSocial login, enterprise

JWT Implementation

Generate Tokens

import jwt from 'jsonwebtoken';

function generateTokens(user: User) {
  const accessToken = jwt.sign(
    { userId: user.id, email: user.email, role: user.role },
    process.env.JWT_SECRET!,
    { expiresIn: '15m' }  // Short-lived
  );

  const refreshToken = jwt.sign(
    { userId: user.id },
    process.env.JWT_REFRESH_SECRET!,
    { expiresIn: '7d' }  // Long-lived
  );

  return { accessToken, refreshToken };
}

Verify Middleware

function authenticate(req: Request, res: Response, next: NextFunction) {
  const authHeader = req.headers.authorization;
  if (!authHeader?.startsWith('Bearer ')) {
    return res.status(401).json({ error: 'No token provided' });
  }

  const token = authHeader.substring(7);
  try {
    const payload = jwt.verify(token, process.env.JWT_SECRET!);
    req.user = payload;
    next();
  } catch (error) {
    return res.status(401).json({ error: 'Invalid token' });
  }
}

Refresh Token Flow

app.post('/api/auth/refresh', async (req, res) => {
  const { refreshToken } = req.body;
  
  try {
    // Verify refresh token
    const payload = jwt.verify(refreshToken, process.env.JWT_REFRESH_SECRET!);
    
    // Check if token exists in database (not revoked)
    const storedToken = await db.refreshTokens.findOne({
      token: await hash(refreshToken),
      expiresAt: { $gt: new Date() }
    });
    
    if (!storedToken) {
      return res.status(403).json({ error: 'Token revoked' });
    }
    
    // Generate new access token
    const user = await db.users.findById(payload.userId);
    const accessToken = jwt.sign(
      { userId: user.id, email: user.email, role: user.role },
      process.env.JWT_SECRET!,
      { expiresIn: '15m' }
    );
    
    res.json({ accessToken });
  } catch {
    res.status(401).json({ error: 'Invalid refresh token' });
  }
});

Session-Based Authentication

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: {
    secure: process.env.NODE_ENV === 'production',  // HTTPS only
    httpOnly: true,   // No JavaScript access
    maxAge: 24 * 60 * 60 * 1000,  // 24 hours
    sameSite: 'strict'  // CSRF protection
  }
}));

// Login
app.post('/api/auth/login', async (req, res) => {
  const { email, password } = req.body;
  const user = await db.users.findOne({ email });
  
  if (!user || !(await verifyPassword(password, user.passwordHash))) {
    return res.status(401).json({ error: 'Invalid credentials' });
  }
  
  req.session.userId = user.id;
  req.session.role = user.role;
  res.json({ user: { id: user.id, email: user.email } });
});

// Logout
app.post('/api/auth/logout', (req, res) => {
  req.session.destroy(() => {
    res.clearCookie('connect.sid');
    res.json({ message: 'Logged out' });
  });
});

OAuth2 / Social Login

import passport from 'passport';
import { Strategy as GoogleStrategy } from 'passport-google-oauth20';

passport.use(new GoogleStrategy({
  clientID: process.env.GOOGLE_CLIENT_ID!,
  clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
  callbackURL: '/api/auth/google/callback'
}, async (accessToken, refreshToken, profile, done) => {
  // Find or create user
  let user = await db.users.findOne({ googleId: profile.id });
  
  if (!user) {
    user = await db.users.create({
      googleId: profile.id,
      email: profile.emails?.[0]?.value,
      name: profile.displayName
    });
  }
  
  return done(null, user);
}));

// Routes
app.get('/api/auth/google', 
  passport.authenticate('google', { scope: ['profile', 'email'] }));

app.get('/api/auth/google/callback',
  passport.authenticate('google', { session: false }),
  (req, res) => {
    const tokens = generateTokens(req.user);
    res.redirect(`${FRONTEND_URL}/auth/callback?token=${tokens.accessToken}`);
  });

Authorization: RBAC

enum Role {
  USER = 'user',
  MODERATOR = 'moderator',
  ADMIN = 'admin'
}

const roleHierarchy: Record<Role, Role[]> = {
  [Role.ADMIN]: [Role.ADMIN, Role.MODERATOR, Role.USER],
  [Role.MODERATOR]: [Role.MODERATOR, Role.USER],
  [Role.USER]: [Role.USER]
};

function hasRole(userRole: Role, requiredRole: Role): boolean {
  return roleHierarchy[userRole].includes(requiredRole);
}

function requireRole(...roles: Role[]) {
  return (req: Request, res: Response, next: NextFunction) => {
    if (!req.user) {
      return res.status(401).json({ error: 'Not authenticated' });
    }
    if (!roles.some(role => hasRole(req.user.role, role))) {
      return res.status(403).json({ error: 'Insufficient permissions' });
    }
    next();
  };
}

// Usage
app.delete('/api/users/:id',
  authenticate,
  requireRole(Role.ADMIN),
  async (req, res) => {
    await db.users.delete(req.params.id);
    res.json({ message: 'User deleted' });
  }
);

Permission-Based Access

enum Permission {
  READ_USERS = 'read:users',
  WRITE_USERS = 'write:users',
  DELETE_USERS = 'delete:users',
  READ_POSTS = 'read:posts',
  WRITE_POSTS = 'write:posts'
}

const rolePermissions: Record<Role, Permission[]> = {
  [Role.USER]: [Permission.READ_POSTS, Permission.WRITE_POSTS],
  [Role.MODERATOR]: [Permission.READ_POSTS, Permission.WRITE_POSTS, Permission.READ_USERS],
  [Role.ADMIN]: Object.values(Permission)
};

function requirePermission(...permissions: Permission[]) {
  return (req: Request, res: Response, next: NextFunction) => {
    if (!req.user) return res.status(401).json({ error: 'Not authenticated' });
    
    const hasAll = permissions.every(p => 
      rolePermissions[req.user.role]?.includes(p)
    );
    
    if (!hasAll) return res.status(403).json({ error: 'Insufficient permissions' });
    next();
  };
}

Resource Ownership

function requireOwnership(resourceType: 'post' | 'comment') {
  return async (req: Request, res: Response, next: NextFunction) => {
    if (!req.user) return res.status(401).json({ error: 'Not authenticated' });
    
    // Admins can access anything
    if (req.user.role === Role.ADMIN) return next();
    
    const resource = await db[resourceType].findById(req.params.id);
    if (!resource) return res.status(404).json({ error: 'Not found' });
    
    if (resource.userId !== req.user.userId) {
      return res.status(403).json({ error: 'Not authorized' });
    }
    
    next();
  };
}

// Usage: Users can only update their own posts
app.put('/api/posts/:id', authenticate, requireOwnership('post'), updatePost);

Password Security

import bcrypt from 'bcrypt';
import { z } from 'zod';

const passwordSchema = z.string()
  .min(12, 'Password must be at least 12 characters')
  .regex(/[A-Z]/, 'Must contain uppercase')
  .regex(/[a-z]/, 'Must contain lowercase')
  .regex(/[0-9]/, 'Must contain number')
  .regex(/[^A-Za-z0-9]/, 'Must contain special character');

async function hashPassword(password: string): Promise<string> {
  return bcrypt.hash(password, 12);  // 12 rounds
}

async function verifyPassword(password: string, hash: string): Promise<boolean> {
  return bcrypt.compare(password, hash);
}

Best Practices

✅ Do

  • Use HTTPS everywhere
  • Hash passwords with bcrypt (12+ rounds)
  • Use short-lived access tokens (15-30 min)
  • Store refresh tokens in database (revocable)
  • Validate all input
  • Rate limit auth endpoints
  • Log security events
  • Use secure cookie flags (httpOnly, secure, sameSite)

❌ Don't

  • Store passwords in plain text
  • Store JWT in localStorage (XSS vulnerable)
  • Use weak JWT secrets
  • Trust client-side auth checks only
  • Expose stack traces in errors
  • Skip server-side validation
  • Ignore rate limiting

Common Pitfalls

IssueSolution
JWT in localStorageUse httpOnly cookies
No token expirationSet short TTL + refresh tokens
Weak passwordsEnforce strong policy with zod
No rate limitingUse express-rate-limit + Redis
Client-only authAlways validate server-side

Files

2 total
Select a file
Select a file to preview.

Comments

Loading comments…