skillby yonatangross

auth-patterns

Authentication and authorization patterns. Use when implementing login flows, JWT tokens, session management, password security, or role-based access control.

Installs: 0
Used in: 1 repos
Updated: 8h ago
$npx ai-builder add skill yonatangross/auth-patterns

Installs to .claude/skills/auth-patterns/

# Authentication Patterns

Implement secure authentication and authorization.

## When to Use

- Login/signup flows
- JWT token management
- Session security
- Role-based access control

## Password Hashing

```python
from argon2 import PasswordHasher

ph = PasswordHasher()

# Hash password
password_hash = ph.hash(password)

# Verify password
try:
    ph.verify(password_hash, password)
    # Password correct
except:
    # Password incorrect
    pass
```

**Requirements:**
- Minimum 12 characters
- Mixed case + numbers + symbols
- Use bcrypt, argon2, or scrypt
- Check against common password lists

## Session Management

```python
# Secure session cookies
app.config['SESSION_COOKIE_SECURE'] = True      # HTTPS only
app.config['SESSION_COOKIE_HTTPONLY'] = True    # No JS access
app.config['SESSION_COOKIE_SAMESITE'] = 'Strict'
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(hours=1)
```

## JWT Tokens (Access Token)

```python
import jwt
from datetime import datetime, timedelta

def create_access_token(user_id: str) -> str:
    """Short-lived access token (15 min - 1 hour)."""
    payload = {
        'user_id': user_id,
        'type': 'access',
        'exp': datetime.utcnow() + timedelta(minutes=15),  # Short-lived
        'iat': datetime.utcnow(),
    }
    return jwt.encode(payload, SECRET_KEY, algorithm='HS256')

def verify_token(token: str) -> str | None:
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])
        return payload['user_id']
    except jwt.ExpiredSignatureError:
        return None
    except jwt.InvalidTokenError:
        return None
```

## Refresh Token Rotation (2026 Best Practice)

```python
import secrets
from datetime import datetime, timedelta

def create_refresh_token(user_id: str, db) -> str:
    """Long-lived refresh token with rotation."""
    token = secrets.token_urlsafe(32)
    token_hash = hashlib.sha256(token.encode()).hexdigest()

    # Store hashed token in DB (never store plain tokens)
    db.execute("""
        INSERT INTO refresh_tokens (user_id, token_hash, expires_at, version)
        VALUES (?, ?, ?, ?)
    """, [user_id, token_hash, datetime.utcnow() + timedelta(days=7), 1])

    return token

def rotate_refresh_token(old_token: str, db) -> tuple[str, str]:
    """Rotate refresh token on use (security best practice).

    Returns: (new_access_token, new_refresh_token)
    """
    old_hash = hashlib.sha256(old_token.encode()).hexdigest()

    # Find and invalidate old token
    row = db.execute("""
        SELECT user_id, version FROM refresh_tokens
        WHERE token_hash = ? AND expires_at > NOW() AND revoked = FALSE
    """, [old_hash]).fetchone()

    if not row:
        raise InvalidTokenError("Refresh token invalid or expired")

    user_id, version = row

    # Revoke old token
    db.execute("UPDATE refresh_tokens SET revoked = TRUE WHERE token_hash = ?", [old_hash])

    # Create new tokens (rotation)
    new_access = create_access_token(user_id)
    new_refresh = create_refresh_token(user_id, db)

    return new_access, new_refresh

# API endpoint for token refresh
@app.route('/auth/refresh', methods=['POST'])
def refresh_tokens():
    refresh_token = request.json.get('refresh_token')
    try:
        access, refresh = rotate_refresh_token(refresh_token, db)
        return {"access_token": access, "refresh_token": refresh}
    except InvalidTokenError:
        abort(401)
```

**Token Expiry Guidelines (2026):**
| Token Type | Expiry | Storage |
|------------|--------|---------|
| Access | 15 min - 1 hour | Memory only (no persistence) |
| Refresh | 7-30 days | HTTPOnly cookie or secure storage |

## Rate Limiting

```python
from flask_limiter import Limiter

limiter = Limiter(app, key_func=get_remote_address)

@app.route('/login', methods=['POST'])
@limiter.limit("5 per minute")  # 5 attempts per minute
def login():
    # Login logic
    pass
```

## Role-Based Access Control

```python
from functools import wraps

def require_role(role):
    def decorator(f):
        @wraps(f)
        def wrapper(*args, **kwargs):
            if current_user.role != role:
                abort(403)
            return f(*args, **kwargs)
        return wrapper
    return decorator

@app.route('/admin/users')
@login_required
@require_role('admin')
def admin_users():
    return get_all_users()
```

## Multi-Factor Authentication

```python
import pyotp

def generate_totp_secret() -> str:
    return pyotp.random_base32()

def verify_totp(secret: str, code: str) -> bool:
    totp = pyotp.TOTP(secret)
    return totp.verify(code)
```

## Key Decisions

| Decision | Recommendation |
|----------|----------------|
| Password hash | **Argon2id** (preferred) > Argon2 > bcrypt (legacy) |
| Access token expiry | 15 min - 1 hour |
| Refresh token expiry | 7-30 days with rotation |
| Session cookie | HTTPOnly, Secure, SameSite=Strict |
| Rate limit | 5 attempts per 15 min |
| JWT algorithm | HS256 (symmetric) or RS256 (asymmetric for microservices) |

## Common Mistakes

- Storing passwords in plaintext
- No rate limiting on login
- Long-lived tokens
- Revealing if email exists
- No MFA option

## Related Skills

- `owasp-top-10` - Security fundamentals
- `input-validation` - Data validation
- `api-design-framework` - API security

Quick Install

$npx ai-builder add skill yonatangross/auth-patterns

Details

Type
skill
Slug
yonatangross/auth-patterns
Created
3d ago