🔑 OAuth 2.0

OAuth 2.0 is the most widely used authorization framework for API access. Selecting the appropriate grant type is critical.

Choosing a Grant Type

Grant Type Use Case Security Level
Authorization Code + PKCE SPAs and mobile apps (recommended) Highest
Authorization Code Server-side web apps High
Client Credentials Server-to-server communication (M2M) Medium
Implicit (deprecated) Legacy SPAs Low
Resource Owner Password (deprecated) Highly trusted first-party only Low
Do Not Use Implicit Grant

Implicit Grant is now deprecated because the token is exposed in the URL fragment. Use Authorization Code + PKCE for SPAs.

Authorization Code + PKCE Flow

JavaScript PKCE Challenge Generation
// 1. Randomly generate code_verifier
function generateCodeVerifier() {
  const array = new Uint8Array(32);
  crypto.getRandomValues(array);
  return btoa(String.fromCharCode(...array))
    .replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
}

// 2. Generate code_challenge with SHA-256
async function generateCodeChallenge(verifier) {
  const encoder = new TextEncoder();
  const data = encoder.encode(verifier);
  const digest = await crypto.subtle.digest('SHA-256', data);
  return btoa(String.fromCharCode(...new Uint8Array(digest)))
    .replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
}

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

const authUrl = `https://auth.example.com/authorize?` +
  `response_type=code&` +
  `client_id=${clientId}&` +
  `redirect_uri=${redirectUri}&` +
  `code_challenge=${challenge}&` +
  `code_challenge_method=S256&` +
  `scope=read write`;

🎫 JWT (JSON Web Token)

JWT is widely used as a stateless authentication token, but implementation mistakes directly lead to security vulnerabilities.

JWT Security Considerations

✅ Do

  • Explicitly specify the algorithm (HS256 / RS256)
  • Set a short expiration (exp) time (15 minutes or less)
  • Validate issuer (iss) and audience (aud)
  • Generate secret keys with sufficient length (256 bits or more)
  • Implement rotation for refresh tokens

❌ Don't

  • Store sensitive information in the JWT payload (Base64 is not encryption)
  • Allow alg: "none"
  • Store tokens in localStorage (vulnerable to XSS)
  • Neglect token revocation management
  • Use an RS256 public key as an HS256 secret key

Secure JWT Implementation

JavaScript (Node.js) Token Issuance and Verification
const jwt = require('jsonwebtoken');

// --- Token Issuance ---
function issueTokens(user) {
  const accessToken = jwt.sign(
    { sub: user.id, role: user.role },
    process.env.JWT_SECRET,
    {
      algorithm: 'HS256',
      expiresIn: '15m',
      issuer: 'api.example.com',
      audience: 'app.example.com',
    }
  );

  const refreshToken = jwt.sign(
    { sub: user.id, type: 'refresh' },
    process.env.REFRESH_SECRET,
    { algorithm: 'HS256', expiresIn: '7d' }
  );

  return { accessToken, refreshToken };
}

// --- Token Verification ---
function verifyAccessToken(token) {
  return jwt.verify(token, process.env.JWT_SECRET, {
    algorithms: ['HS256'],   // ← Always specify!
    issuer: 'api.example.com',
    audience: 'app.example.com',
  });
}

Token Storage Location Comparison

Storage Location XSS Resistance CSRF Resistance Recommendation
HttpOnly Cookie △ (requires SameSite setting) Recommended
Memory (variable) Lost on page navigation
localStorage ✕ (can be stolen via XSS) Not recommended
sessionStorage ✕ (can be stolen via XSS) Limited use
Recommended: HttpOnly + Secure + SameSite Cookie

Storing the access token in a cookie with HttpOnly, Secure, and SameSite=Strict attributes is the safest approach. This prevents token theft through XSS attacks.

🗝 API Key Management

API keys are a simple authentication mechanism, but improper management can lead to serious risks.

API Key Best Practices

Generation and Storage

Generate with sufficient entropy (256 bits or more). Store hashed; never keep plain text in the database.

Scope Restriction

Assign minimum scopes per key (read-only, specific resources only, etc.). Avoid wildcard permissions.

Rotation

Rotate keys regularly (within 90 days). Set a grace period for old keys. Revoke immediately upon leakage.

Transmission Method

Send via header (X-API-Key or Authorization). Do not include in URL query parameters (they appear in logs).

Environment Variable Management

Do not hard-code keys in source code. Manage them with environment variables or a secret manager.

Monitoring and Logging

Monitor API key usage. Detect anomalous patterns (mass requests, access from unknown IPs).

API Key Validation Implementation Example

JavaScript (Express) Hashed API Key Validation
const crypto = require('crypto');

// Hash the API key with SHA-256 and compare
async function validateApiKey(req, res, next) {
  const apiKey = req.headers['x-api-key'];
  if (!apiKey) {
    return res.status(401).json({ error: 'API key required' });
  }

  // Hash and look up in DB (timingSafeEqual not needed since we compare hashes)
  const hashedKey = crypto
    .createHash('sha256')
    .update(apiKey)
    .digest('hex');

  const keyRecord = await db.findApiKey(hashedKey);

  if (!keyRecord || keyRecord.revokedAt) {
    return res.status(403).json({ error: 'Invalid API key' });
  }

  // Check expiration
  if (keyRecord.expiresAt && new Date() > keyRecord.expiresAt) {
    return res.status(403).json({ error: 'API key expired' });
  }

  req.apiClient = keyRecord;
  next();
}

🤖 Securing LLM & AI Agent API Access

AI agents and LLM-powered applications require additional authentication and authorization patterns beyond traditional API security.

Model-Specific Scopes

Assign different access levels per model tier (e.g., GPT-4 requires elevated scope). Prevent unauthorized use of expensive or sensitive models.

Token Budget Enforcement

Embed token budgets in access tokens or API key metadata. Enforce per-request and per-day limits to prevent denial-of-wallet attacks.

Prompt-Level Authorization

Check user permissions before executing certain prompt types (e.g., code generation, data analysis). Map prompt categories to role-based permissions.

Multi-Tenant Isolation

Ensure conversation history, fine-tuned models, and RAG data are isolated per tenant. Use tenant-scoped API keys with namespace enforcement.

Agent Identity & Attestation

Assign cryptographic identities to AI agents. Use signed JWTs for inter-agent communication. Verify agent identity before granting tool access.

Credential Rotation for AI Services

Automate API key rotation for LLM providers. Use short-lived tokens for agent sessions. Revoke credentials immediately when agents are decommissioned.

Agent Tool Access Authorization

Scenario Auth Method Required Scope Approval
Agent reads public data API Key data:read Automatic
Agent modifies user data OAuth 2.0 (delegated) data:write User consent required
Agent calls external API OAuth 2.0 + mTLS external:invoke Human-in-the-loop
Agent executes code / shell Scoped JWT + Sandbox exec:sandbox Admin approval + audit log

Scoped OAuth Token for AI Agents (Python)

Python Agent Token Issuance with Scope Enforcement
import jwt
import time
from datetime import datetime, timedelta

class AgentTokenIssuer:
    """Issues scoped, short-lived tokens for AI agents."""

    ALLOWED_SCOPES = {
        "reader": ["data:read"],
        "writer": ["data:read", "data:write"],
        "executor": ["data:read", "data:write", "exec:sandbox"],
    }

    def __init__(self, secret_key: str):
        self.secret_key = secret_key

    def issue_agent_token(
        self,
        agent_id: str,
        role: str,
        tenant_id: str,
        token_budget: int = 10000,
        ttl_minutes: int = 15,
    ) -> str:
        # Validate role and resolve scopes
        if role not in self.ALLOWED_SCOPES:
            raise ValueError(f"Invalid role: {role}")

        payload = {
            "sub": agent_id,
            "tenant": tenant_id,
            "scopes": self.ALLOWED_SCOPES[role],
            "token_budget": token_budget,
            "iat": datetime.utcnow(),
            "exp": datetime.utcnow() + timedelta(minutes=ttl_minutes),
            "type": "agent",
        }
        return jwt.encode(payload, self.secret_key, algorithm="HS256")

    def verify_tool_access(self, token: str, required_scope: str) -> dict:
        # Decode and verify agent token
        payload = jwt.decode(
            token, self.secret_key, algorithms=["HS256"]
        )
        if required_scope not in payload["scopes"]:
            raise PermissionError(
                f"Agent {payload['sub']} lacks scope: {required_scope}"
            )
        return payload
OWASP References

Related: LLM06: Excessive Agency, ASI01: Excessive Agency, ASI03: Insecure Tool/Function Calling

Authentication Method Comparison

Method Stateless Use Case Security Considerations
OAuth 2.0 + PKCE APIs requiring user authorization state/PKCE required, token expiration management
JWT Bearer Inter-microservice communication Algorithm specification required, no sensitive data in payload
API Key Server-to-server, external integrations Rotation and scope restriction required
mTLS High-security M2M communication High operational cost for certificate management
Session Cookie Traditional web applications CSRF protection required, scalability challenges