Agent skill

jwt-authentication

JWT authentication implementation patterns. Use when implementing login, registration, token refresh, password reset, or any authentication/authorization system.

Stars 163
Forks 31

Install this agent skill to your Project

npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/other/jwt-authentication-cohen-liel-hivemind

SKILL.md

JWT Authentication Patterns

Token Strategy

  • Access token: short-lived (15 min), stateless JWT
  • Refresh token: long-lived (7 days), stored in DB for revocation
  • Storage: access in memory (JS var), refresh in httpOnly cookie

Implementation (FastAPI + Python-Jose)

python
# auth/tokens.py
from datetime import datetime, timedelta
from jose import JWTError, jwt
from passlib.context import CryptContext

SECRET_KEY = settings.SECRET_KEY  # 32+ char random string from env
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE = timedelta(minutes=15)
REFRESH_TOKEN_EXPIRE = timedelta(days=7)

pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

def hash_password(password: str) -> str:
    return pwd_context.hash(password)

def verify_password(plain: str, hashed: str) -> bool:
    return pwd_context.verify(plain, hashed)

def create_access_token(user_id: int) -> str:
    return jwt.encode(
        {"sub": str(user_id), "exp": datetime.utcnow() + ACCESS_TOKEN_EXPIRE, "type": "access"},
        SECRET_KEY, algorithm=ALGORITHM
    )

def create_refresh_token(user_id: int) -> str:
    return jwt.encode(
        {"sub": str(user_id), "exp": datetime.utcnow() + REFRESH_TOKEN_EXPIRE, "type": "refresh"},
        SECRET_KEY, algorithm=ALGORITHM
    )

def decode_token(token: str) -> dict:
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        return payload
    except JWTError:
        raise HTTPException(status_code=401, detail="Invalid or expired token")

Login Endpoint

python
@router.post("/login", response_model=TokenResponse)
async def login(form: OAuth2PasswordRequestForm = Depends(), db: AsyncSession = Depends(get_db)):
    user = await get_user_by_email(db, form.username)
    if not user or not verify_password(form.password, user.hashed_password):
        # Same error for both cases — don't reveal which field was wrong
        raise HTTPException(status_code=401, detail="Invalid credentials")

    # Rate limit check (use Redis counter)
    await check_login_rate_limit(user.id)

    access_token = create_access_token(user.id)
    refresh_token = create_refresh_token(user.id)

    # Store refresh token hash in DB for revocation
    await store_refresh_token(db, user.id, refresh_token)

    response = JSONResponse({"access_token": access_token, "token_type": "bearer"})
    response.set_cookie("refresh_token", refresh_token, httponly=True, secure=True, samesite="lax")
    return response

Auth Dependency

python
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/login")

async def get_current_user(
    token: str = Depends(oauth2_scheme),
    db: AsyncSession = Depends(get_db)
) -> User:
    payload = decode_token(token)
    if payload.get("type") != "access":
        raise HTTPException(status_code=401, detail="Invalid token type")
    user = await db.get(User, int(payload["sub"]))
    if not user or user.is_disabled:
        raise HTTPException(status_code=401, detail="User not found or disabled")
    return user

Refresh Token Rotation

python
@router.post("/refresh")
async def refresh(request: Request, db: AsyncSession = Depends(get_db)):
    refresh_token = request.cookies.get("refresh_token")
    if not refresh_token:
        raise HTTPException(status_code=401, detail="No refresh token")
    payload = decode_token(refresh_token)
    # Verify token exists in DB (revocation check)
    stored = await get_refresh_token(db, refresh_token)
    if not stored:
        raise HTTPException(status_code=401, detail="Token revoked")
    # Rotate: delete old, issue new
    await delete_refresh_token(db, refresh_token)
    new_access = create_access_token(int(payload["sub"]))
    new_refresh = create_refresh_token(int(payload["sub"]))
    await store_refresh_token(db, int(payload["sub"]), new_refresh)
    response = JSONResponse({"access_token": new_access})
    response.set_cookie("refresh_token", new_refresh, httponly=True, secure=True, samesite="lax")
    return response

Rules

  • httpOnly cookies for refresh tokens (XSS can't steal them)
  • Never store access tokens in localStorage (XSS risk)
  • Refresh token rotation: old token invalidated immediately on use
  • Rate limit login: 5 attempts per IP per 15 minutes
  • Same error message for wrong email AND wrong password
  • Logout must invalidate refresh token in DB

Expand your agent's capabilities with these related and highly-rated skills.

Didn't find tool you were looking for?

Be as detailed as possible for better results