{
  "schemaVersion": "1.0",
  "item": {
    "slug": "secure-auth-patterns",
    "name": "Secure Auth Patterns",
    "source": "tencent",
    "type": "skill",
    "category": "安全合规",
    "sourceUrl": "https://clawhub.ai/brandonwise/secure-auth-patterns",
    "canonicalUrl": "https://clawhub.ai/brandonwise/secure-auth-patterns",
    "targetPlatform": "OpenClaw"
  },
  "install": {
    "downloadMode": "redirect",
    "downloadUrl": "/downloads/secure-auth-patterns",
    "sourceDownloadUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=secure-auth-patterns",
    "sourcePlatform": "tencent",
    "targetPlatform": "OpenClaw",
    "installMethod": "Manual import",
    "extraction": "Extract archive",
    "prerequisites": [
      "OpenClaw"
    ],
    "packageFormat": "ZIP package",
    "includedAssets": [
      "SKILL.md",
      "resources/implementation-playbook.md"
    ],
    "primaryDoc": "SKILL.md",
    "quickSetup": [
      "Download the package from Yavira.",
      "Extract the archive and review SKILL.md first.",
      "Import or place the package into your OpenClaw setup."
    ],
    "agentAssist": {
      "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
      "steps": [
        "Download the package from Yavira.",
        "Extract it into a folder your agent can access.",
        "Paste one of the prompts below and point your agent at the extracted folder."
      ],
      "prompts": [
        {
          "label": "New install",
          "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Tell me what you changed and call out any manual steps you could not complete."
        },
        {
          "label": "Upgrade existing",
          "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Summarize what changed and any follow-up checks I should run."
        }
      ]
    },
    "sourceHealth": {
      "source": "tencent",
      "slug": "secure-auth-patterns",
      "status": "healthy",
      "reason": "direct_download_ok",
      "recommendedAction": "download",
      "checkedAt": "2026-04-29T14:00:06.655Z",
      "expiresAt": "2026-05-06T14:00:06.655Z",
      "httpStatus": 200,
      "finalUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=secure-auth-patterns",
      "contentType": "application/zip",
      "probeMethod": "head",
      "details": {
        "probeUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=secure-auth-patterns",
        "contentDisposition": "attachment; filename=\"secure-auth-patterns-1.0.0.zip\"",
        "redirectLocation": null,
        "bodySnippet": null,
        "slug": "secure-auth-patterns"
      },
      "scope": "item",
      "summary": "Item download looks usable.",
      "detail": "Yavira can redirect you to the upstream package for this item.",
      "primaryActionLabel": "Download for OpenClaw",
      "primaryActionHref": "/downloads/secure-auth-patterns"
    },
    "validation": {
      "installChecklist": [
        "Use the Yavira download entry.",
        "Review SKILL.md after the package is downloaded.",
        "Confirm the extracted package contains the expected setup assets."
      ],
      "postInstallChecks": [
        "Confirm the extracted package includes the expected docs or setup files.",
        "Validate the skill or prompts are available in your target agent workspace.",
        "Capture any manual follow-up steps the agent could not complete."
      ]
    },
    "downloadPageUrl": "https://openagent3.xyz/downloads/secure-auth-patterns",
    "agentPageUrl": "https://openagent3.xyz/skills/secure-auth-patterns/agent",
    "manifestUrl": "https://openagent3.xyz/skills/secure-auth-patterns/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/secure-auth-patterns/agent.md"
  },
  "agentAssist": {
    "summary": "Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.",
    "steps": [
      "Download the package from Yavira.",
      "Extract it into a folder your agent can access.",
      "Paste one of the prompts below and point your agent at the extracted folder."
    ],
    "prompts": [
      {
        "label": "New install",
        "body": "I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Tell me what you changed and call out any manual steps you could not complete."
      },
      {
        "label": "Upgrade existing",
        "body": "I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Summarize what changed and any follow-up checks I should run."
      }
    ]
  },
  "documentation": {
    "source": "clawhub",
    "primaryDoc": "SKILL.md",
    "sections": [
      {
        "title": "Authentication & Authorization Patterns",
        "body": "Master authentication and authorization patterns including JWT, OAuth2, session management, and RBAC to build secure, scalable access control systems."
      },
      {
        "title": "Description",
        "body": "USE WHEN:\n\nImplementing user authentication systems\nSecuring REST or GraphQL APIs\nAdding OAuth2/social login or SSO\nDesigning session management\nImplementing RBAC or permission systems\nDebugging authentication issues\n\nDON'T USE WHEN:\n\nOnly need UI/login page styling\nTask is infrastructure-only without identity concerns\nCannot change auth policies"
      },
      {
        "title": "Authentication vs Authorization",
        "body": "AuthN (Authentication)AuthZ (Authorization)\"Who are you?\"\"What can you do?\"Verify identityCheck permissionsIssue credentialsEnforce policiesLogin/logoutAccess control"
      },
      {
        "title": "Authentication Strategies",
        "body": "StrategyProsConsBest ForSessionSimple, secureStateful, scalingTraditional web appsJWTStateless, scalableToken size, revocationAPIs, microservicesOAuth2/OIDCDelegated, SSOComplex setupSocial login, enterprise"
      },
      {
        "title": "Generate Tokens",
        "body": "import jwt from 'jsonwebtoken';\n\nfunction generateTokens(user: User) {\n  const accessToken = jwt.sign(\n    { userId: user.id, email: user.email, role: user.role },\n    process.env.JWT_SECRET!,\n    { expiresIn: '15m' }  // Short-lived\n  );\n\n  const refreshToken = jwt.sign(\n    { userId: user.id },\n    process.env.JWT_REFRESH_SECRET!,\n    { expiresIn: '7d' }  // Long-lived\n  );\n\n  return { accessToken, refreshToken };\n}"
      },
      {
        "title": "Verify Middleware",
        "body": "function authenticate(req: Request, res: Response, next: NextFunction) {\n  const authHeader = req.headers.authorization;\n  if (!authHeader?.startsWith('Bearer ')) {\n    return res.status(401).json({ error: 'No token provided' });\n  }\n\n  const token = authHeader.substring(7);\n  try {\n    const payload = jwt.verify(token, process.env.JWT_SECRET!);\n    req.user = payload;\n    next();\n  } catch (error) {\n    return res.status(401).json({ error: 'Invalid token' });\n  }\n}"
      },
      {
        "title": "Refresh Token Flow",
        "body": "app.post('/api/auth/refresh', async (req, res) => {\n  const { refreshToken } = req.body;\n  \n  try {\n    // Verify refresh token\n    const payload = jwt.verify(refreshToken, process.env.JWT_REFRESH_SECRET!);\n    \n    // Check if token exists in database (not revoked)\n    const storedToken = await db.refreshTokens.findOne({\n      token: await hash(refreshToken),\n      expiresAt: { $gt: new Date() }\n    });\n    \n    if (!storedToken) {\n      return res.status(403).json({ error: 'Token revoked' });\n    }\n    \n    // Generate new access token\n    const user = await db.users.findById(payload.userId);\n    const accessToken = jwt.sign(\n      { userId: user.id, email: user.email, role: user.role },\n      process.env.JWT_SECRET!,\n      { expiresIn: '15m' }\n    );\n    \n    res.json({ accessToken });\n  } catch {\n    res.status(401).json({ error: 'Invalid refresh token' });\n  }\n});"
      },
      {
        "title": "Session-Based Authentication",
        "body": "import session from 'express-session';\nimport RedisStore from 'connect-redis';\n\napp.use(session({\n  store: new RedisStore({ client: redisClient }),\n  secret: process.env.SESSION_SECRET!,\n  resave: false,\n  saveUninitialized: false,\n  cookie: {\n    secure: process.env.NODE_ENV === 'production',  // HTTPS only\n    httpOnly: true,   // No JavaScript access\n    maxAge: 24 * 60 * 60 * 1000,  // 24 hours\n    sameSite: 'strict'  // CSRF protection\n  }\n}));\n\n// Login\napp.post('/api/auth/login', async (req, res) => {\n  const { email, password } = req.body;\n  const user = await db.users.findOne({ email });\n  \n  if (!user || !(await verifyPassword(password, user.passwordHash))) {\n    return res.status(401).json({ error: 'Invalid credentials' });\n  }\n  \n  req.session.userId = user.id;\n  req.session.role = user.role;\n  res.json({ user: { id: user.id, email: user.email } });\n});\n\n// Logout\napp.post('/api/auth/logout', (req, res) => {\n  req.session.destroy(() => {\n    res.clearCookie('connect.sid');\n    res.json({ message: 'Logged out' });\n  });\n});"
      },
      {
        "title": "OAuth2 / Social Login",
        "body": "import passport from 'passport';\nimport { Strategy as GoogleStrategy } from 'passport-google-oauth20';\n\npassport.use(new GoogleStrategy({\n  clientID: process.env.GOOGLE_CLIENT_ID!,\n  clientSecret: process.env.GOOGLE_CLIENT_SECRET!,\n  callbackURL: '/api/auth/google/callback'\n}, async (accessToken, refreshToken, profile, done) => {\n  // Find or create user\n  let user = await db.users.findOne({ googleId: profile.id });\n  \n  if (!user) {\n    user = await db.users.create({\n      googleId: profile.id,\n      email: profile.emails?.[0]?.value,\n      name: profile.displayName\n    });\n  }\n  \n  return done(null, user);\n}));\n\n// Routes\napp.get('/api/auth/google', \n  passport.authenticate('google', { scope: ['profile', 'email'] }));\n\napp.get('/api/auth/google/callback',\n  passport.authenticate('google', { session: false }),\n  (req, res) => {\n    const tokens = generateTokens(req.user);\n    res.redirect(`${FRONTEND_URL}/auth/callback?token=${tokens.accessToken}`);\n  });"
      },
      {
        "title": "Authorization: RBAC",
        "body": "enum Role {\n  USER = 'user',\n  MODERATOR = 'moderator',\n  ADMIN = 'admin'\n}\n\nconst roleHierarchy: Record<Role, Role[]> = {\n  [Role.ADMIN]: [Role.ADMIN, Role.MODERATOR, Role.USER],\n  [Role.MODERATOR]: [Role.MODERATOR, Role.USER],\n  [Role.USER]: [Role.USER]\n};\n\nfunction hasRole(userRole: Role, requiredRole: Role): boolean {\n  return roleHierarchy[userRole].includes(requiredRole);\n}\n\nfunction requireRole(...roles: Role[]) {\n  return (req: Request, res: Response, next: NextFunction) => {\n    if (!req.user) {\n      return res.status(401).json({ error: 'Not authenticated' });\n    }\n    if (!roles.some(role => hasRole(req.user.role, role))) {\n      return res.status(403).json({ error: 'Insufficient permissions' });\n    }\n    next();\n  };\n}\n\n// Usage\napp.delete('/api/users/:id',\n  authenticate,\n  requireRole(Role.ADMIN),\n  async (req, res) => {\n    await db.users.delete(req.params.id);\n    res.json({ message: 'User deleted' });\n  }\n);"
      },
      {
        "title": "Permission-Based Access",
        "body": "enum Permission {\n  READ_USERS = 'read:users',\n  WRITE_USERS = 'write:users',\n  DELETE_USERS = 'delete:users',\n  READ_POSTS = 'read:posts',\n  WRITE_POSTS = 'write:posts'\n}\n\nconst rolePermissions: Record<Role, Permission[]> = {\n  [Role.USER]: [Permission.READ_POSTS, Permission.WRITE_POSTS],\n  [Role.MODERATOR]: [Permission.READ_POSTS, Permission.WRITE_POSTS, Permission.READ_USERS],\n  [Role.ADMIN]: Object.values(Permission)\n};\n\nfunction requirePermission(...permissions: Permission[]) {\n  return (req: Request, res: Response, next: NextFunction) => {\n    if (!req.user) return res.status(401).json({ error: 'Not authenticated' });\n    \n    const hasAll = permissions.every(p => \n      rolePermissions[req.user.role]?.includes(p)\n    );\n    \n    if (!hasAll) return res.status(403).json({ error: 'Insufficient permissions' });\n    next();\n  };\n}"
      },
      {
        "title": "Resource Ownership",
        "body": "function requireOwnership(resourceType: 'post' | 'comment') {\n  return async (req: Request, res: Response, next: NextFunction) => {\n    if (!req.user) return res.status(401).json({ error: 'Not authenticated' });\n    \n    // Admins can access anything\n    if (req.user.role === Role.ADMIN) return next();\n    \n    const resource = await db[resourceType].findById(req.params.id);\n    if (!resource) return res.status(404).json({ error: 'Not found' });\n    \n    if (resource.userId !== req.user.userId) {\n      return res.status(403).json({ error: 'Not authorized' });\n    }\n    \n    next();\n  };\n}\n\n// Usage: Users can only update their own posts\napp.put('/api/posts/:id', authenticate, requireOwnership('post'), updatePost);"
      },
      {
        "title": "Password Security",
        "body": "import bcrypt from 'bcrypt';\nimport { z } from 'zod';\n\nconst passwordSchema = z.string()\n  .min(12, 'Password must be at least 12 characters')\n  .regex(/[A-Z]/, 'Must contain uppercase')\n  .regex(/[a-z]/, 'Must contain lowercase')\n  .regex(/[0-9]/, 'Must contain number')\n  .regex(/[^A-Za-z0-9]/, 'Must contain special character');\n\nasync function hashPassword(password: string): Promise<string> {\n  return bcrypt.hash(password, 12);  // 12 rounds\n}\n\nasync function verifyPassword(password: string, hash: string): Promise<boolean> {\n  return bcrypt.compare(password, hash);\n}"
      },
      {
        "title": "✅ Do",
        "body": "Use HTTPS everywhere\nHash passwords with bcrypt (12+ rounds)\nUse short-lived access tokens (15-30 min)\nStore refresh tokens in database (revocable)\nValidate all input\nRate limit auth endpoints\nLog security events\nUse secure cookie flags (httpOnly, secure, sameSite)"
      },
      {
        "title": "❌ Don't",
        "body": "Store passwords in plain text\nStore JWT in localStorage (XSS vulnerable)\nUse weak JWT secrets\nTrust client-side auth checks only\nExpose stack traces in errors\nSkip server-side validation\nIgnore rate limiting"
      },
      {
        "title": "Common Pitfalls",
        "body": "IssueSolutionJWT in localStorageUse httpOnly cookiesNo token expirationSet short TTL + refresh tokensWeak passwordsEnforce strong policy with zodNo rate limitingUse express-rate-limit + RedisClient-only authAlways validate server-side"
      }
    ],
    "body": "Authentication & Authorization Patterns\n\nMaster authentication and authorization patterns including JWT, OAuth2, session management, and RBAC to build secure, scalable access control systems.\n\nDescription\n\nUSE WHEN:\n\nImplementing user authentication systems\nSecuring REST or GraphQL APIs\nAdding OAuth2/social login or SSO\nDesigning session management\nImplementing RBAC or permission systems\nDebugging authentication issues\n\nDON'T USE WHEN:\n\nOnly need UI/login page styling\nTask is infrastructure-only without identity concerns\nCannot change auth policies\nAuthentication vs Authorization\nAuthN (Authentication)\tAuthZ (Authorization)\n\"Who are you?\"\t\"What can you do?\"\nVerify identity\tCheck permissions\nIssue credentials\tEnforce policies\nLogin/logout\tAccess control\nAuthentication Strategies\nStrategy\tPros\tCons\tBest For\nSession\tSimple, secure\tStateful, scaling\tTraditional web apps\nJWT\tStateless, scalable\tToken size, revocation\tAPIs, microservices\nOAuth2/OIDC\tDelegated, SSO\tComplex setup\tSocial login, enterprise\nJWT Implementation\nGenerate Tokens\nimport jwt from 'jsonwebtoken';\n\nfunction generateTokens(user: User) {\n  const accessToken = jwt.sign(\n    { userId: user.id, email: user.email, role: user.role },\n    process.env.JWT_SECRET!,\n    { expiresIn: '15m' }  // Short-lived\n  );\n\n  const refreshToken = jwt.sign(\n    { userId: user.id },\n    process.env.JWT_REFRESH_SECRET!,\n    { expiresIn: '7d' }  // Long-lived\n  );\n\n  return { accessToken, refreshToken };\n}\n\nVerify Middleware\nfunction authenticate(req: Request, res: Response, next: NextFunction) {\n  const authHeader = req.headers.authorization;\n  if (!authHeader?.startsWith('Bearer ')) {\n    return res.status(401).json({ error: 'No token provided' });\n  }\n\n  const token = authHeader.substring(7);\n  try {\n    const payload = jwt.verify(token, process.env.JWT_SECRET!);\n    req.user = payload;\n    next();\n  } catch (error) {\n    return res.status(401).json({ error: 'Invalid token' });\n  }\n}\n\nRefresh Token Flow\napp.post('/api/auth/refresh', async (req, res) => {\n  const { refreshToken } = req.body;\n  \n  try {\n    // Verify refresh token\n    const payload = jwt.verify(refreshToken, process.env.JWT_REFRESH_SECRET!);\n    \n    // Check if token exists in database (not revoked)\n    const storedToken = await db.refreshTokens.findOne({\n      token: await hash(refreshToken),\n      expiresAt: { $gt: new Date() }\n    });\n    \n    if (!storedToken) {\n      return res.status(403).json({ error: 'Token revoked' });\n    }\n    \n    // Generate new access token\n    const user = await db.users.findById(payload.userId);\n    const accessToken = jwt.sign(\n      { userId: user.id, email: user.email, role: user.role },\n      process.env.JWT_SECRET!,\n      { expiresIn: '15m' }\n    );\n    \n    res.json({ accessToken });\n  } catch {\n    res.status(401).json({ error: 'Invalid refresh token' });\n  }\n});\n\nSession-Based Authentication\nimport session from 'express-session';\nimport RedisStore from 'connect-redis';\n\napp.use(session({\n  store: new RedisStore({ client: redisClient }),\n  secret: process.env.SESSION_SECRET!,\n  resave: false,\n  saveUninitialized: false,\n  cookie: {\n    secure: process.env.NODE_ENV === 'production',  // HTTPS only\n    httpOnly: true,   // No JavaScript access\n    maxAge: 24 * 60 * 60 * 1000,  // 24 hours\n    sameSite: 'strict'  // CSRF protection\n  }\n}));\n\n// Login\napp.post('/api/auth/login', async (req, res) => {\n  const { email, password } = req.body;\n  const user = await db.users.findOne({ email });\n  \n  if (!user || !(await verifyPassword(password, user.passwordHash))) {\n    return res.status(401).json({ error: 'Invalid credentials' });\n  }\n  \n  req.session.userId = user.id;\n  req.session.role = user.role;\n  res.json({ user: { id: user.id, email: user.email } });\n});\n\n// Logout\napp.post('/api/auth/logout', (req, res) => {\n  req.session.destroy(() => {\n    res.clearCookie('connect.sid');\n    res.json({ message: 'Logged out' });\n  });\n});\n\nOAuth2 / Social Login\nimport passport from 'passport';\nimport { Strategy as GoogleStrategy } from 'passport-google-oauth20';\n\npassport.use(new GoogleStrategy({\n  clientID: process.env.GOOGLE_CLIENT_ID!,\n  clientSecret: process.env.GOOGLE_CLIENT_SECRET!,\n  callbackURL: '/api/auth/google/callback'\n}, async (accessToken, refreshToken, profile, done) => {\n  // Find or create user\n  let user = await db.users.findOne({ googleId: profile.id });\n  \n  if (!user) {\n    user = await db.users.create({\n      googleId: profile.id,\n      email: profile.emails?.[0]?.value,\n      name: profile.displayName\n    });\n  }\n  \n  return done(null, user);\n}));\n\n// Routes\napp.get('/api/auth/google', \n  passport.authenticate('google', { scope: ['profile', 'email'] }));\n\napp.get('/api/auth/google/callback',\n  passport.authenticate('google', { session: false }),\n  (req, res) => {\n    const tokens = generateTokens(req.user);\n    res.redirect(`${FRONTEND_URL}/auth/callback?token=${tokens.accessToken}`);\n  });\n\nAuthorization: RBAC\nenum Role {\n  USER = 'user',\n  MODERATOR = 'moderator',\n  ADMIN = 'admin'\n}\n\nconst roleHierarchy: Record<Role, Role[]> = {\n  [Role.ADMIN]: [Role.ADMIN, Role.MODERATOR, Role.USER],\n  [Role.MODERATOR]: [Role.MODERATOR, Role.USER],\n  [Role.USER]: [Role.USER]\n};\n\nfunction hasRole(userRole: Role, requiredRole: Role): boolean {\n  return roleHierarchy[userRole].includes(requiredRole);\n}\n\nfunction requireRole(...roles: Role[]) {\n  return (req: Request, res: Response, next: NextFunction) => {\n    if (!req.user) {\n      return res.status(401).json({ error: 'Not authenticated' });\n    }\n    if (!roles.some(role => hasRole(req.user.role, role))) {\n      return res.status(403).json({ error: 'Insufficient permissions' });\n    }\n    next();\n  };\n}\n\n// Usage\napp.delete('/api/users/:id',\n  authenticate,\n  requireRole(Role.ADMIN),\n  async (req, res) => {\n    await db.users.delete(req.params.id);\n    res.json({ message: 'User deleted' });\n  }\n);\n\nPermission-Based Access\nenum Permission {\n  READ_USERS = 'read:users',\n  WRITE_USERS = 'write:users',\n  DELETE_USERS = 'delete:users',\n  READ_POSTS = 'read:posts',\n  WRITE_POSTS = 'write:posts'\n}\n\nconst rolePermissions: Record<Role, Permission[]> = {\n  [Role.USER]: [Permission.READ_POSTS, Permission.WRITE_POSTS],\n  [Role.MODERATOR]: [Permission.READ_POSTS, Permission.WRITE_POSTS, Permission.READ_USERS],\n  [Role.ADMIN]: Object.values(Permission)\n};\n\nfunction requirePermission(...permissions: Permission[]) {\n  return (req: Request, res: Response, next: NextFunction) => {\n    if (!req.user) return res.status(401).json({ error: 'Not authenticated' });\n    \n    const hasAll = permissions.every(p => \n      rolePermissions[req.user.role]?.includes(p)\n    );\n    \n    if (!hasAll) return res.status(403).json({ error: 'Insufficient permissions' });\n    next();\n  };\n}\n\nResource Ownership\nfunction requireOwnership(resourceType: 'post' | 'comment') {\n  return async (req: Request, res: Response, next: NextFunction) => {\n    if (!req.user) return res.status(401).json({ error: 'Not authenticated' });\n    \n    // Admins can access anything\n    if (req.user.role === Role.ADMIN) return next();\n    \n    const resource = await db[resourceType].findById(req.params.id);\n    if (!resource) return res.status(404).json({ error: 'Not found' });\n    \n    if (resource.userId !== req.user.userId) {\n      return res.status(403).json({ error: 'Not authorized' });\n    }\n    \n    next();\n  };\n}\n\n// Usage: Users can only update their own posts\napp.put('/api/posts/:id', authenticate, requireOwnership('post'), updatePost);\n\nPassword Security\nimport bcrypt from 'bcrypt';\nimport { z } from 'zod';\n\nconst passwordSchema = z.string()\n  .min(12, 'Password must be at least 12 characters')\n  .regex(/[A-Z]/, 'Must contain uppercase')\n  .regex(/[a-z]/, 'Must contain lowercase')\n  .regex(/[0-9]/, 'Must contain number')\n  .regex(/[^A-Za-z0-9]/, 'Must contain special character');\n\nasync function hashPassword(password: string): Promise<string> {\n  return bcrypt.hash(password, 12);  // 12 rounds\n}\n\nasync function verifyPassword(password: string, hash: string): Promise<boolean> {\n  return bcrypt.compare(password, hash);\n}\n\nBest Practices\n✅ Do\nUse HTTPS everywhere\nHash passwords with bcrypt (12+ rounds)\nUse short-lived access tokens (15-30 min)\nStore refresh tokens in database (revocable)\nValidate all input\nRate limit auth endpoints\nLog security events\nUse secure cookie flags (httpOnly, secure, sameSite)\n❌ Don't\nStore passwords in plain text\nStore JWT in localStorage (XSS vulnerable)\nUse weak JWT secrets\nTrust client-side auth checks only\nExpose stack traces in errors\nSkip server-side validation\nIgnore rate limiting\nCommon Pitfalls\nIssue\tSolution\nJWT in localStorage\tUse httpOnly cookies\nNo token expiration\tSet short TTL + refresh tokens\nWeak passwords\tEnforce strong policy with zod\nNo rate limiting\tUse express-rate-limit + Redis\nClient-only auth\tAlways validate server-side"
  },
  "trust": {
    "sourceLabel": "tencent",
    "provenanceUrl": "https://clawhub.ai/brandonwise/secure-auth-patterns",
    "publisherUrl": "https://clawhub.ai/brandonwise/secure-auth-patterns",
    "owner": "brandonwise",
    "version": "1.0.0",
    "license": null,
    "verificationStatus": "Indexed source record"
  },
  "links": {
    "detailUrl": "https://openagent3.xyz/skills/secure-auth-patterns",
    "downloadUrl": "https://openagent3.xyz/downloads/secure-auth-patterns",
    "agentUrl": "https://openagent3.xyz/skills/secure-auth-patterns/agent",
    "manifestUrl": "https://openagent3.xyz/skills/secure-auth-patterns/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/secure-auth-patterns/agent.md"
  }
}