---
paths:
  - "**/*.ts"
  - "**/*.tsx"
  - "**/*.js"
  - "**/*.jsx"
---
# TypeScript/JavaScript Security

> This file extends [common/security.md](../common/security.md) with TypeScript/JavaScript specific content.

## Secret Management

```typescript
// NEVER: Hardcoded secrets
const apiKey = "sk-proj-xxxxx"

// ALWAYS: Environment variables
const apiKey = process.env.OPENAI_API_KEY

if (!apiKey) {
  throw new Error('OPENAI_API_KEY not configured')
}
```

## JWT Token Validation

```typescript
import jwt from 'jsonwebtoken'

interface JWTPayload {
  userId: string
  email: string
  role: 'admin' | 'user'
}

export function verifyToken(token: string): JWTPayload {
  try {
    return jwt.verify(token, process.env.JWT_SECRET!) as JWTPayload
  } catch {
    throw new ApiError(401, 'Invalid token')
  }
}

export async function requireAuth(request: Request): Promise<JWTPayload> {
  const token = request.headers.get('authorization')?.replace('Bearer ', '')
  if (!token) throw new ApiError(401, 'Missing authorization token')
  return verifyToken(token)
}
```

Always verify `JWT_SECRET` is set at boot. Never log tokens. Use short TTL (15min) + refresh tokens.

## Role-Based Access Control (RBAC)

```typescript
type Permission = 'read' | 'write' | 'delete' | 'admin'

const rolePermissions: Record<User['role'], Permission[]> = {
  admin: ['read', 'write', 'delete', 'admin'],
  moderator: ['read', 'write', 'delete'],
  user: ['read', 'write'],
}

export function hasPermission(user: User, permission: Permission): boolean {
  return rolePermissions[user.role].includes(permission)
}

export function requirePermission(permission: Permission) {
  return (handler: (req: Request, user: User) => Promise<Response>) =>
    async (req: Request) => {
      const user = await requireAuth(req)
      if (!hasPermission(user, permission)) throw new ApiError(403, 'Insufficient permissions')
      return handler(req, user)
    }
}

// Usage
export const DELETE = requirePermission('delete')(async (req, user) => {
  // handler with verified permission
})
```

Check authorization at the route level (not inside business logic). Resource-level ownership checks (e.g., `if (resource.userId !== user.id)`) belong in service layer.

## Agent Support

- Use **security-reviewer** skill for comprehensive security audits
