Token导航 LogoToken导航

Agent Skill · 前端设计

jwt-auth(JWT auth 安全)

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

jwt-auth 是前端设计类 Agent Skill,可用宿主为 Codex、Claude、Cursor、Gemini CLI,通过GitHub 仓库获取,权限为接触敏感数据;许可证信息还不明确,第三方扫描已通过,按安装量在前端设计类的 10,216 个 Skill 里排第 5,036。

总安装 751周安装 31GitHub Stars 777下载 246核验 2026-05-01

安装说明

本站只整理中文说明,不托管安装包,也不代用户安装。
GitHub
许可证
unknown

安装方式

通过对话安装

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

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

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/dadbodgeoff/drift --skill jwt-auth

这个 Skill 是做什么的

前端设计类 Skill 关注页面结构、视觉规范和组件写法,输出通常是可运行的界面代码。

jwt-auth 用于辅助安全审计、权限检查和认证流程分析。适合梳理敏感配置、检查依赖风险或生成安全复核清单。使用时不能把工具输出直接当最终结论。

安装命令:npx skills add https://github.com/dadbodgeoff/drift --skill jwt-auth 涉及密钥或用户数据时应确认最小权限和操作边界。

来源仓库内路径:skills/jwt-auth

上手步骤

4 步
  1. 1

    确认自己的宿主工具

    公开资料标注支持 Codex、Claude、Cursor、Gemini CLI;先确认自己用的是其中之一,再按对应工具的 Skills 目录放置。

  2. 2

    拿到安装命令

    可以直接复制的命令是 npx skills add https://github.com/dadbodgeoff/drift --skill jwt-auth;命令里的占位符换成自己的路径或密钥再执行。

  3. 3

    安装后重启宿主

    命令执行完要完全退出宿主工具再打开,让新装的 Skill 被重新扫描到,否则列表里可能看不到。

  4. 4

    按最小权限试用

    它需要接触敏感数据,先在测试目录或测试账号里跑,确认没问题再放到正式项目。

常见问题

6 条

jwt-auth 怎么安装?

在终端执行 npx skills add https://github.com/dadbodgeoff/drift --skill jwt-auth。装完完全退出宿主工具再打开,让它重新扫描本地 Skill。

jwt-auth 支持哪些 AI 工具?

公开资料标注支持 Codex、Claude、Cursor、Gemini CLI。其他工具只要能读取本地 Skill 目录,一般也能手动放进去,但要自己确认触发方式是否一致。

jwt-auth 需要什么权限?

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

jwt-auth 是免费的吗?

公开资料没有标注明确许可证,收费和商用限制都无法确认。正式使用前请到来源仓库核对授权说明。

jwt-auth 用的人多吗?

公开数据是累计安装 751 次,近一周安装 31 次,下载 246 次,GitHub 777 星。这些数字来自第三方目录统计,只能当作热度参考,不代表质量或安全性。

jwt-auth 安全吗?

第三方扫描结果为通过,但这只覆盖已知的静态检查项,不能替代自己读一遍源码和安装命令。

值得选的地方和要先想清楚的地方

值得选的地方

  • 同时适配 4 种工具,换客户端不用重新找替代品。
  • 第三方扫描已通过,常见的高风险行为没有命中。

先想清楚的地方

  • 需要接触敏感数据,装到常用环境前要先评估影响面。
  • 许可证没有明确标注,能不能商用、能不能再分发都不确定。
  • 第三方目录整理,版本更新和行为细节以来源仓库为准。

适不适合你

更适合

  • 已经在用 Codex,想直接扩展它能力的人。
  • 需要处理前端设计类任务、又不想每次都手写提示词的人。

不太适合

  • 环境里有大量密钥、凭证,不方便给第三方 Skill 读取的人。
  • 需要明确授权才能用到公司项目里的场景。
  • 需要厂商级技术支持和 SLA 的生产系统。

前端设计类 Skill 对比

6 个同类
Skill总安装GitHub 星权限许可证扫描
frontend-design

前端设计 · Codex、Claude

8,823,360126,072只读MIT通过
vercel-composition-patterns

Vercel 组合模式 · Codex、Claude

3,858,81625,893需要联网MIT通过
vercel-react-native-skills

Vercel React native skills 前端 · Codex、Claude

2,468,66425,878只读MIT通过
adapt

adapt 搜索 · Codex、Claude

2,027,04023,125操作浏览器未明确通过
bolder

bolder 分析 · Codex、Claude

2,004,28823,130只读未明确通过
jwt-auth

JWT auth 安全 · 本页

751777接触敏感数据未明确通过

按安装量取同分类前几名;数字来自第三方目录统计,只作热度参考,不代表质量结论。

SKILL.md 原文

JWT Authentication with Refresh Token Rotation

Secure, stateless authentication with automatic token refresh.

When to Use This Skill

  • Building authentication for SPAs or mobile apps
  • Need stateless authentication for APIs
  • Want automatic token refresh without re-login
  • Implementing OAuth-style token flows

Token Architecture

┌─────────────────────────────────────────────────────┐
│                   Access Token                       │
│  - Short-lived (15 min)                             │
│  - Contains user claims                             │
│  - Sent with every request                          │
│  - Stored in memory (not localStorage)              │
└─────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────┐
│                  Refresh Token                       │
│  - Long-lived (7 days)                              │
│  - Used only to get new access tokens               │
│  - Stored in httpOnly cookie                        │
│  - Rotated on each use (one-time use)              │
└─────────────────────────────────────────────────────┘

Token Rotation Flow

1. Login
   Client ──────────────────────────────────▶ Server
          credentials
   Client ◀────────────────────────────────── Server
          access_token + refresh_token (cookie)

2. API Request
   Client ──────────────────────────────────▶ Server
          Authorization: Bearer {access_token}
   Client ◀────────────────────────────────── Server
          response

3. Token Refresh (when access token expires)
   Client ──────────────────────────────────▶ Server
          POST /auth/refresh (cookie sent)
   Client ◀────────────────────────────────── Server
          new_access_token + new_refresh_token
          (old refresh token invalidated)

TypeScript Implementation

Token Service

// token-service.ts
import jwt from 'jsonwebtoken';
import crypto from 'crypto';
import { Redis } from 'ioredis';

interface TokenConfig {
  accessTokenSecret: string;
  refreshTokenSecret: string;
  accessTokenExpiry: string;  // e.g., '15m'
  refreshTokenExpiry: string; // e.g., '7d'
}

interface TokenPayload {
  userId: string;
  email: string;
  role: string;
}

interface TokenPair {
  accessToken: string;
  refreshToken: string;
  expiresIn: number;
}

class TokenService {
  constructor(
    private config: TokenConfig,
    private redis: Redis
  ) {}

  async generateTokenPair(payload: TokenPayload): Promise<TokenPair> {
    // Generate access token
    const accessToken = jwt.sign(payload, this.config.accessTokenSecret, {
      expiresIn: this.config.accessTokenExpiry,
    });

    // Generate refresh token (random + signed)
    const refreshTokenId = crypto.randomUUID();
    const refreshToken = jwt.sign(
      { ...payload, tokenId: refreshTokenId },
      this.config.refreshTokenSecret,
      { expiresIn: this.config.refreshTokenExpiry }
    );

    // Store refresh token in Redis for rotation tracking
    await this.storeRefreshToken(payload.userId, refreshTokenId);

    // Calculate expiry in seconds
    const decoded = jwt.decode(accessToken) as { exp: number };
    const expiresIn = decoded.exp - Math.floor(Date.now() / 1000);

    return { accessToken, refreshToken, expiresIn };
  }

  async refreshTokens(refreshToken: string): Promise<TokenPair | null> {
    try {
      // Verify refresh token
      const decoded = jwt.verify(
        refreshToken,
        this.config.refreshTokenSecret
      ) as TokenPayload & { tokenId: string };

      // Check if token is still valid (not rotated)
      const isValid = await this.validateRefreshToken(
        decoded.userId,
        decoded.tokenId
      );

      if (!isValid) {
        // Token reuse detected - potential theft
        // Invalidate all tokens for this user
        await this.revokeAllUserTokens(decoded.userId);
        return null;
      }

      // Invalidate old refresh token
      await this.invalidateRefreshToken(decoded.userId, decoded.tokenId);

      // Generate new token pair
      return this.generateTokenPair({
        userId: decoded.userId,
        email: decoded.email,
        role: decoded.role,
      });
    } catch (error) {
      return null;
    }
  }

  verifyAccessToken(token: string): TokenPayload | null {
    try {
      return jwt.verify(token, this.config.accessTokenSecret) as TokenPayload;
    } catch {
      return null;
    }
  }

  private async storeRefreshToken(userId: string, tokenId: string): Promise<void> {
    const key = `refresh_tokens:${userId}`;
    // Store with expiry matching refresh token
    await this.redis.sadd(key, tokenId);
    await this.redis.expire(key, 7 * 24 * 60 * 60); // 7 days
  }

  private async validateRefreshToken(userId: string, tokenId: string): Promise<boolean> {
    const key = `refresh_tokens:${userId}`;
    return (await this.redis.sismember(key, tokenId)) === 1;
  }

  private async invalidateRefreshToken(userId: string, tokenId: string): Promise<void> {
    const key = `refresh_tokens:${userId}`;
    await this.redis.srem(key, tokenId);
  }

  async revokeAllUserTokens(userId: string): Promise<void> {
    const key = `refresh_tokens:${userId}`;
    await this.redis.del(key);
  }
}

export { TokenService, TokenConfig, TokenPayload, TokenPair };

Auth Routes

// auth-routes.ts
import { Router, Request, Response } from 'express';
import { TokenService } from './token-service';

const router = Router();

// Cookie options for refresh token
const REFRESH_COOKIE_OPTIONS = {
  httpOnly: true,
  secure: process.env.NODE_ENV === 'production',
  sameSite: 'strict' as const,
  path: '/auth', // Only sent to auth routes
  maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days
};

router.post('/login', async (req: Request, res: Response) => {
  const { email, password } = req.body;

  // Validate credentials (implement your own)
  const user = await validateCredentials(email, password);
  if (!user) {
    return res.status(401).json({ error: 'Invalid credentials' });
  }

  // Generate tokens
  const tokens = await tokenService.generateTokenPair({
    userId: user.id,
    email: user.email,
    role: user.role,
  });

  // Set refresh token as httpOnly cookie
  res.cookie('refresh_token', tokens.refreshToken, REFRESH_COOKIE_OPTIONS);

  // Return access token in response body
  res.json({
    accessToken: tokens.accessToken,
    expiresIn: tokens.expiresIn,
    user: {
      id: user.id,
      email: user.email,
      role: user.role,
    },
  });
});

router.post('/refresh', async (req: Request, res: Response) => {
  const refreshToken = req.cookies.refresh_token;

  if (!refreshToken) {
    return res.status(401).json({ error: 'No refresh token' });
  }

  const tokens = await tokenService.refreshTokens(refreshToken);

  if (!tokens) {
    // Clear invalid cookie
    res.clearCookie('refresh_token', { path: '/auth' });
    return res.status(401).json({ error: 'Invalid refresh token' });
  }

  // Set new refresh token
  res.cookie('refresh_token', tokens.refreshToken, REFRESH_COOKIE_OPTIONS);

  res.json({
    accessToken: tokens.accessToken,
    expiresIn: tokens.expiresIn,
  });
});

router.post('/logout', async (req: Request, res: Response) => {
  const refreshToken = req.cookies.refresh_token;

  if (refreshToken) {
    // Invalidate the refresh token
    try {
      const decoded = jwt.decode(refreshToken) as { userId: string };
      if (decoded?.userId) {
        await tokenService.revokeAllUserTokens(decoded.userId);
      }
    } catch {
      // Ignore decode errors
    }
  }

  res.clearCookie('refresh_token', { path: '/auth' });
  res.json({ success: true });
});

export { router as authRouter };

Auth Middleware

// auth-middleware.ts
import { Request, Response, NextFunction } from 'express';
import { TokenService, TokenPayload } from './token-service';

declare global {
  namespace Express {
    interface Request {
      user?: TokenPayload;
    }
  }
}

function authMiddleware(tokenService: TokenService) {
  return (req: Request, res: Response, next: NextFunction) => {
    const authHeader = req.headers.authorization;

    if (!authHeader?.startsWith('Bearer ')) {
      return res.status(401).json({ error: 'Missing authorization header' });
    }

    const token = authHeader.slice(7);
    const payload = tokenService.verifyAccessToken(token);

    if (!payload) {
      return res.status(401).json({ error: 'Invalid or expired token' });
    }

    req.user = payload;
    next();
  };
}

export { authMiddleware };

Python Implementation

# token_service.py
import jwt
import uuid
from datetime import datetime, timedelta
from typing import Optional, Dict, Any
from dataclasses import dataclass
import redis

@dataclass
class TokenPayload:
    user_id: str
    email: str
    role: str

@dataclass
class TokenPair:
    access_token: str
    refresh_token: str
    expires_in: int

class TokenService:
    def __init__(
        self,
        access_secret: str,
        refresh_secret: str,
        redis_client: redis.Redis,
        access_expiry_minutes: int = 15,
        refresh_expiry_days: int = 7,
    ):
        self.access_secret = access_secret
        self.refresh_secret = refresh_secret
        self.redis = redis_client
        self.access_expiry = timedelta(minutes=access_expiry_minutes)
        self.refresh_expiry = timedelta(days=refresh_expiry_days)

    def generate_token_pair(self, payload: TokenPayload) -> TokenPair:
        now = datetime.utcnow()

        # Access token
        access_payload = {
            "user_id": payload.user_id,
            "email": payload.email,
            "role": payload.role,
            "exp": now + self.access_expiry,
            "iat": now,
        }
        access_token = jwt.encode(access_payload, self.access_secret, algorithm="HS256")

        # Refresh token with unique ID
        token_id = str(uuid.uuid4())
        refresh_payload = {
            **access_payload,
            "token_id": token_id,
            "exp": now + self.refresh_expiry,
        }
        refresh_token = jwt.encode(refresh_payload, self.refresh_secret, algorithm="HS256")

        # Store refresh token ID
        self._store_refresh_token(payload.user_id, token_id)

        return TokenPair(
            access_token=access_token,
            refresh_token=refresh_token,
            expires_in=int(self.access_expiry.total_seconds()),
        )

    def refresh_tokens(self, refresh_token: str) -> Optional[TokenPair]:
        try:
            decoded = jwt.decode(refresh_token, self.refresh_secret, algorithms=["HS256"])

            # Validate token is still valid
            if not self._validate_refresh_token(decoded["user_id"], decoded["token_id"]):
                # Token reuse detected - revoke all
                self.revoke_all_user_tokens(decoded["user_id"])
                return None

            # Invalidate old token
            self._invalidate_refresh_token(decoded["user_id"], decoded["token_id"])

            # Generate new pair
            return self.generate_token_pair(TokenPayload(
                user_id=decoded["user_id"],
                email=decoded["email"],
                role=decoded["role"],
            ))
        except jwt.InvalidTokenError:
            return None

    def verify_access_token(self, token: str) -> Optional[TokenPayload]:
        try:
            decoded = jwt.decode(token, self.access_secret, algorithms=["HS256"])
            return TokenPayload(
                user_id=decoded["user_id"],
                email=decoded["email"],
                role=decoded["role"],
            )
        except jwt.InvalidTokenError:
            return None

    def _store_refresh_token(self, user_id: str, token_id: str) -> None:
        key = f"refresh_tokens:{user_id}"
        self.redis.sadd(key, token_id)
        self.redis.expire(key, int(self.refresh_expiry.total_seconds()))

    def _validate_refresh_token(self, user_id: str, token_id: str) -> bool:
        key = f"refresh_tokens:{user_id}"
        return self.redis.sismember(key, token_id)

    def _invalidate_refresh_token(self, user_id: str, token_id: str) -> None:
        key = f"refresh_tokens:{user_id}"
        self.redis.srem(key, token_id)

    def revoke_all_user_tokens(self, user_id: str) -> None:
        key = f"refresh_tokens:{user_id}"
        self.redis.delete(key)

Frontend Integration

// auth-client.ts
class AuthClient {
  private accessToken: string | null = null;
  private refreshPromise: Promise<boolean> | null = null;

  async login(email: string, password: string): Promise<boolean> {
    const response = await fetch('/auth/login', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      credentials: 'include', // Important for cookies
      body: JSON.stringify({ email, password }),
    });

    if (!response.ok) return false;

    const data = await response.json();
    this.accessToken = data.accessToken;

    // Schedule refresh before expiry
    this.scheduleRefresh(data.expiresIn);

    return true;
  }

  async fetch(url: string, options: RequestInit = {}): Promise<Response> {
    // Ensure we have a valid token
    if (!this.accessToken) {
      await this.refresh();
    }

    const response = await fetch(url, {
      ...options,
      headers: {
        ...options.headers,
        Authorization: `Bearer ${this.accessToken}`,
      },
    });

    // If 401, try refresh and retry
    if (response.status === 401) {
      const refreshed = await this.refresh();
      if (refreshed) {
        return fetch(url, {
          ...options,
          headers: {
            ...options.headers,
            Authorization: `Bearer ${this.accessToken}`,
          },
        });
      }
    }

    return response;
  }

  private async refresh(): Promise<boolean> {
    // Deduplicate concurrent refresh calls
    if (this.refreshPromise) {
      return this.refreshPromise;
    }

    this.refreshPromise = this.doRefresh();
    const result = await this.refreshPromise;
    this.refreshPromise = null;
    return result;
  }

  private async doRefresh(): Promise<boolean> {
    const response = await fetch('/auth/refresh', {
      method: 'POST',
      credentials: 'include',
    });

    if (!response.ok) {
      this.accessToken = null;
      return false;
    }

    const data = await response.json();
    this.accessToken = data.accessToken;
    this.scheduleRefresh(data.expiresIn);
    return true;
  }

  private scheduleRefresh(expiresIn: number): void {
    // Refresh 1 minute before expiry
    const refreshIn = (expiresIn - 60) * 1000;
    setTimeout(() => this.refresh(), refreshIn);
  }

  async logout(): Promise<void> {
    await fetch('/auth/logout', {
      method: 'POST',
      credentials: 'include',
    });
    this.accessToken = null;
  }
}

export const authClient = new AuthClient();

Best Practices

  1. Short access token expiry: 15 minutes max
  2. Rotate refresh tokens: One-time use prevents theft
  3. Store refresh token in httpOnly cookie: Not accessible to JS
  4. Store access token in memory: Not localStorage
  5. Detect token reuse: Revoke all tokens if detected

Common Mistakes

  • Storing tokens in localStorage (XSS vulnerable)
  • Long access token expiry (increases attack window)
  • Not rotating refresh tokens (stolen token = permanent access)
  • Not detecting refresh token reuse
  • Sending refresh token with every request

Security Checklist

  • Access token expiry ≤ 15 minutes
  • Refresh token in httpOnly cookie
  • Access token in memory only
  • Refresh token rotation on each use
  • Token reuse detection
  • Secure cookie flags (httpOnly, secure, sameSite)
  • HTTPS only in production

平台分布

Codex

36.83%
按下载量换算91

Claude

27.8%
按下载量换算68

Cursor

16.17%
按下载量换算40

Gemini CLI

9.76%
按下载量换算24

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

接触敏感数据

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

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。目前只有一个公开来源,建议同时核对源仓库。

继续浏览同类 Skills