Token导航 LogoToken导航TokenDH.com
研究检索敏感数据github未标认证来源可访问clear审计通过

auth-security授权安全

Agent Skill

用于辅助安全审计、权限检查、凭据风险、认证流程和常见漏洞排查。它适合让 Agent 梳理敏感配置、检查依赖风险、分析鉴权逻辑或生成安全复核清单。使用时不能把工具输出直接当最终结论,涉及密钥、令牌、用户数据或生产系统时,应先确认最小权限、脱敏方式和操作边界。

总安装

1,812

周安装

74

GitHub Stars

28

下载量

580
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

3

许可证

MIT

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:auth-security(授权安全)
来源仓库:https://github.com/majiayu000/claude-arsenal
仓库路径:skills/auth-security
安装命令:
npx skills add https://github.com/majiayu000/claude-arsenal --skill auth-security
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。不同来源提供的安装方式可能略有差异;本站展示可直接复制的安装命令,安装前请核对来源页面。

skills.shnpx skills
npx skills add https://github.com/majiayu000/claude-arsenal --skill auth-security

简介

用于辅助安全审计、权限检查、凭据风险和认证流程排查。

  • 适合梳理敏感配置、检查依赖风险、分析鉴权逻辑或生成安全复核清单。
  • 通过系统步骤发现项目结构、识别认证文件和中间件配置,提供路由保护建议。
  • 使用时不能把工具输出直接当最终结论,涉及密钥、令牌或生产系统时应确认最小权限和操作边界。
  • auth-security 属于研究检索类 Skill,可作为该场景下的辅助能力补充。

SKILL.md

Auth Security

Core Principles

  • OAuth 2.1 — Follow RFC 9700 (January 2025)
  • PKCE Required — All clients must use PKCE
  • Short-lived Tokens — Access tokens expire in 5-15 minutes
  • Token Rotation — Refresh tokens are single-use
  • HttpOnly Storage — Browser tokens in HttpOnly cookies
  • Explicit Algorithm — Never trust JWT header algorithm
  • No backwards compatibility — Delete deprecated auth flows

OAuth 2.1 Key Changes

Deprecated Flows (DO NOT USE)

FlowStatusReplacement
Implicit GrantRemovedAuthorization Code + PKCE
Password GrantRemovedAuthorization Code + PKCE
Auth Code without PKCERemovedMust use PKCE

Required: Authorization Code + PKCE

import crypto from 'crypto';

// 1. Generate code verifier (43-128 chars)
function generateCodeVerifier(): string {
  return crypto.randomBytes(32).toString('base64url');
}

// 2. Generate code challenge
function generateCodeChallenge(verifier: string): string {
  return crypto
    .createHash('sha256')
    .update(verifier)
    .digest('base64url');
}

// 3. Authorization request
const verifier = generateCodeVerifier();
const challenge = generateCodeChallenge(verifier);

const authUrl = new URL('https://auth.example.com/authorize');
authUrl.searchParams.set('response_type', 'code');
authUrl.searchParams.set('client_id', CLIENT_ID);
authUrl.searchParams.set('redirect_uri', REDIRECT_URI);
authUrl.searchParams.set('code_challenge', challenge);
authUrl.searchParams.set('code_challenge_method', 'S256');
authUrl.searchParams.set('scope', 'openid profile email');
authUrl.searchParams.set('state', generateState());

// 4. Token exchange (after redirect)
const tokenResponse = await fetch('https://auth.example.com/token', {
  method: 'POST',
  headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  body: new URLSearchParams({
    grant_type: 'authorization_code',
    code: authorizationCode,
    redirect_uri: REDIRECT_URI,
    client_id: CLIENT_ID,
    code_verifier: verifier, // Prove we initiated the request
  }),
});

JWT Best Practices

Algorithm Selection (2025)

PriorityAlgorithmNotes
1EdDSA (Ed25519)Most secure, quantum-resistant properties
2ES256 (ECDSA P-256)Widely supported, compact signatures
3PS256 (RSA-PSS)More secure than RS256
4RS256 (RSA PKCS#1)Best compatibility
// Recommended: ES256
import { SignJWT, jwtVerify } from 'jose';

const privateKey = await importPKCS8(PRIVATE_KEY_PEM, 'ES256');
const publicKey = await importSPKI(PUBLIC_KEY_PEM, 'ES256');

// Sign
const token = await new SignJWT({ sub: userId, scope: 'read write' })
  .setProtectedHeader({ alg: 'ES256', typ: 'JWT', kid: keyId })
  .setIssuer('https://auth.example.com')
  .setAudience('https://api.example.com')
  .setExpirationTime('15m')
  .setIssuedAt()
  .setJti(crypto.randomUUID())
  .sign(privateKey);

Token Structure

interface AccessTokenPayload {
  // Standard claims
  iss: string;  // Issuer
  sub: string;  // Subject (user ID)
  aud: string;  // Audience
  exp: number;  // Expiration (Unix timestamp)
  iat: number;  // Issued at
  jti: string;  // JWT ID (unique identifier)

  // Custom claims
  scope: string;      // Permissions
  email?: string;     // User email
  roles?: string[];   // User roles
}

Verification (Critical)

import { jwtVerify, errors } from 'jose';

async function verifyAccessToken(token: string): Promise<AccessTokenPayload> {
  try {
    const { payload } = await jwtVerify(token, publicKey, {
      // CRITICAL: Explicitly specify allowed algorithms
      algorithms: ['ES256'],

      // Validate standard claims
      issuer: 'https://auth.example.com',
      audience: 'https://api.example.com',

      // Clock tolerance for sync issues
      clockTolerance: 30,
    });

    // Additional validation
    if (!payload.scope?.includes('read')) {
      throw new Error('Insufficient scope');
    }

    return payload as AccessTokenPayload;
  } catch (err) {
    if (err instanceof errors.JWTExpired) {
      throw new AuthError('Token expired', 'TOKEN_EXPIRED');
    }
    if (err instanceof errors.JWTClaimValidationFailed) {
      throw new AuthError('Invalid token claims', 'INVALID_CLAIMS');
    }
    throw new AuthError('Invalid token', 'INVALID_TOKEN');
  }
}

Token Storage

Web Applications

// Set token in HttpOnly cookie (server-side)
function setAuthCookie(res: Response, token: string) {
  res.cookie('access_token', token, {
    httpOnly: true,     // Not accessible via JavaScript
    secure: true,       // HTTPS only
    sameSite: 'strict', // CSRF protection
    maxAge: 15 * 60 * 1000, // 15 minutes
    path: '/api',       // Only sent to API routes
  });
}

// Refresh token (longer-lived)
function setRefreshCookie(res: Response, token: string) {
  res.cookie('refresh_token', token, {
    httpOnly: true,
    secure: true,
    sameSite: 'strict',
    maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days
    path: '/api/auth/refresh',  // Only for refresh endpoint
  });
}

Single Page Applications (SPA)

// Store in memory (NOT localStorage/sessionStorage)
class TokenManager {
  private accessToken: string | null = null;

  setToken(token: string) {
    this.accessToken = token;
  }

  getToken(): string | null {
    return this.accessToken;
  }

  clearToken() {
    this.accessToken = null;
  }
}

// Use with Refresh Token Rotation
// Refresh token in HttpOnly cookie
// Access token in memory

Storage Comparison

StorageXSS SafeCSRF SafePersistence
HttpOnly CookieYesNeeds SameSiteYes
MemoryYesYesNo (lost on reload)
localStorageNoYesYes
sessionStorageNoYesTab only

Refresh Token Rotation

Flow

1. Client sends refresh_token
2. Server validates refresh_token
3. Server generates NEW access_token + NEW refresh_token
4. Server INVALIDATES old refresh_token
5. Server returns new tokens
6. Client stores new tokens

Implementation

async function refreshTokens(refreshToken: string) {
  // Find token in database
  const stored = await db.refreshToken.findUnique({
    where: { token: hashToken(refreshToken) },
    include: { user: true },
  });

  if (!stored) {
    throw new AuthError('Invalid refresh token', 'INVALID_TOKEN');
  }

  // Check if already used (reuse detection)
  if (stored.usedAt) {
    // Potential token theft - revoke ALL user tokens
    await db.refreshToken.deleteMany({
      where: { userId: stored.userId },
    });

    // Alert security team
    await alertSecurityTeam({
      event: 'REFRESH_TOKEN_REUSE',
      userId: stored.userId,
      tokenId: stored.id,
    });

    throw new AuthError('Token reuse detected', 'TOKEN_REUSE');
  }

  // Check expiration
  if (stored.expiresAt < new Date()) {
    throw new AuthError('Refresh token expired', 'TOKEN_EXPIRED');
  }

  // Mark as used (but keep for reuse detection)
  await db.refreshToken.update({
    where: { id: stored.id },
    data: { usedAt: new Date() },
  });

  // Generate new tokens
  const newAccessToken = await generateAccessToken(stored.user);
  const newRefreshToken = await generateRefreshToken(stored.user);

  // Store new refresh token
  await db.refreshToken.create({
    data: {
      token: hashToken(newRefreshToken),
      userId: stored.userId,
      expiresAt: addDays(new Date(), 7),
      previousTokenId: stored.id, // Chain for audit
    },
  });

  return {
    accessToken: newAccessToken,
    refreshToken: newRefreshToken,
  };
}

Attack Prevention

Algorithm Confusion

// WRONG: Trusts header algorithm
jwt.verify(token, key); // Uses alg from header

// CORRECT: Explicit algorithm
jwt.verify(token, key, { algorithms: ['ES256'] });

CSRF Protection

// Use SameSite cookies
res.cookie('session', token, {
  sameSite: 'strict', // or 'lax' for cross-site links
});

// Or double-submit cookie pattern
const csrfToken = crypto.randomBytes(32).toString('hex');
res.cookie('csrf', csrfToken, { httpOnly: false });
// Client sends csrf token in header

XSS Protection

// Content Security Policy
res.setHeader('Content-Security-Policy', [
  "default-src 'self'",
  "script-src 'self'",
  "style-src 'self' 'unsafe-inline'",
].join('; '));

// Use HttpOnly cookies for tokens
// Never store tokens in localStorage

Token Binding (DPoP)

// Demonstration of Proof of Possession
// Bind token to client's key pair

const dpopProof = await new SignJWT({
  htm: 'POST',
  htu: 'https://api.example.com/resource',
  ath: await hashAccessToken(accessToken), // Access token hash
})
  .setProtectedHeader({ alg: 'ES256', typ: 'dpop+jwt', jwk: publicKey })
  .setJti(crypto.randomUUID())
  .setIssuedAt()
  .sign(privateKey);

// Send with request
fetch('https://api.example.com/resource', {
  headers: {
    Authorization: `DPoP ${accessToken}`,
    DPoP: dpopProof,
  },
});

Token Revocation

// Revoke all user tokens (e.g., password change, logout all)
async function revokeAllUserTokens(userId: string) {
  await db.refreshToken.deleteMany({
    where: { userId },
  });

  // If using token blacklist for access tokens
  await redis.sadd(`revoked:${userId}`, Date.now());
  await redis.expire(`revoked:${userId}`, 15 * 60); // 15 min (access token lifetime)
}

// Check blacklist during verification
async function isTokenRevoked(userId: string, iat: number): Promise<boolean> {
  const revokedAt = await redis.get(`revoked:${userId}`);
  return revokedAt && parseInt(revokedAt) > iat * 1000;
}

Checklist

## OAuth 2.1
- [ ] Using Authorization Code flow
- [ ] PKCE enabled for all clients
- [ ] No implicit or password grants
- [ ] Redirect URI exact matching

## JWT
- [ ] Using ES256 or EdDSA algorithm
- [ ] Explicit algorithm verification
- [ ] Short expiration (≤15 min)
- [ ] Unique jti for each token
- [ ] Issuer and audience validation

## Tokens
- [ ] HttpOnly cookies for web apps
- [ ] Refresh token rotation enabled
- [ ] Reuse detection implemented
- [ ] Token revocation mechanism

## Security
- [ ] HTTPS everywhere
- [ ] SameSite cookies
- [ ] CSP headers configured
- [ ] Rate limiting on auth endpoints
- [ ] Brute force protection

See Also

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

04

需要参考平台分布和安装热度时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

补充不同宿主或平台的使用分布数据

能力 5

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

Claude Code

30.95%
按下载量换算180

Codex

23.38%
按下载量换算136

Antigravity

17.14%
按下载量换算99

windsurf

14.02%
按下载量换算81

trae

7.73%
按下载量换算45

OpenCode

3.73%
按下载量换算22

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

敏感数据

该 Skill 可能接触密钥、Token、环境变量或敏感配置,应进入高风险复核队列,默认不自动发布。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。

来源信息

继续浏览同类 Skills