← Documentation Home

JWT Authentication

JSON Web Token authentication for API access across the Bluefly platform.

Overview

All platform APIs use JWT (JSON Web Token) for authentication: - Token-based auth: Stateless authentication via JWT - Unified auth service: Centralized authentication (Compliance Engine) - Role-based access: RBAC with granular permissions - Token rotation: Automatic token refresh - Audit logging: Complete authentication audit trails

Architecture

graph LR
    A[Client] -->|1. Login| B[Auth Service]
    B -->|2. JWT| A
    A -->|3. API Request + JWT| C[API Gateway]
    C -->|4. Verify JWT| D[Token Validator]
    D -->|5. Allow/Deny| C
    C -->|6. Response| A

Getting a JWT Token

1. Username/Password Login

POST /api/v1/auth/login
Content-Type: application/json

Request:

{
  "username": "developer@bluefly.io",
  "password": "secure-password-123",
  "client_id": "llm-platform-web",
  "grant_type": "password"
}

Response:

{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "read write admin"
}

2. API Key Authentication

For service-to-service authentication:

POST /api/v1/auth/api-key
Content-Type: application/json

Request:

{
  "api_key": "sk_live_abc123...",
  "client_id": "agent-brain-service"
}

Response:

{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "service": "agent-brain",
  "permissions": ["agent:execute", "mesh:communicate"]
}

3. OAuth 2.0 (GitLab)

For user authentication via GitLab OAuth:

See OAuth 2.0 Guide for complete flow.

Using JWT Tokens

Authorization Header

Include JWT in the Authorization header:

curl -X GET https://gateway.local.bluefly.io/api/v1/chat/completions \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "model": "auto-route",
    "messages": [{"role": "user", "content": "Hello"}]
  }'

For WebSocket or legacy clients only:

ws://tracer.local.bluefly.io/api/v1/traces/stream?access_token=eyJhbGci...

⚠️ Warning: Query parameters appear in logs. Use Authorization header whenever possible.

JWT Token Structure

JWT tokens are signed with RS256 (RSA SHA-256):

{
  "alg": "RS256",
  "typ": "JWT",
  "kid": "auth-key-2025-01"
}

Payload

{
  "sub": "user-123",
  "iss": "https://auth.bluefly.io",
  "aud": ["https://gateway.local.bluefly.io"],
  "exp": 1705323600,
  "iat": 1705320000,
  "nbf": 1705320000,
  "jti": "token-abc123",
  "scope": "read write",
  "roles": ["developer", "agent-admin"],
  "permissions": [
    "agent:execute",
    "mesh:communicate",
    "workflow:create"
  ],
  "user": {
    "id": "user-123",
    "email": "developer@bluefly.io",
    "name": "John Developer"
  },
  "client_id": "llm-platform-web"
}

Signature

RSASHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload),
  privateKey
)

Token Claims

Claim Description Required
sub Subject (user ID)
iss Issuer (auth service)
aud Audience (target service)
exp Expiration time (Unix timestamp)
iat Issued at (Unix timestamp)
nbf Not before (Unix timestamp)
jti JWT ID (unique identifier)
scope OAuth 2.0 scopes
roles User roles
permissions Granular permissions
user User metadata
client_id Client identifier

Token Refresh

Refresh expired access tokens using refresh token:

POST /api/v1/auth/refresh
Content-Type: application/json

Request:

{
  "refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "grant_type": "refresh_token"
}

Response:

{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 3600
}

Token Revocation

Revoke a token before expiration:

POST /api/v1/auth/revoke
Content-Type: application/json
Authorization: Bearer eyJhbGci...

Request:

{
  "token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type_hint": "access_token"
}

Response:

{
  "revoked": true,
  "jti": "token-abc123",
  "revokedAt": "2025-01-15T10:35:00Z"
}

Token Validation

Client-Side Validation

Validate JWT locally without API call:

TypeScript:

import * as jose from 'jose';

const JWKS_URL = 'https://auth.bluefly.io/.well-known/jwks.json';

async function validateToken(token: string) {
  const JWKS = jose.createRemoteJWKSet(new URL(JWKS_URL));

  const { payload } = await jose.jwtVerify(token, JWKS, {
    issuer: 'https://auth.bluefly.io',
    audience: 'https://gateway.local.bluefly.io'
  });

  return payload;
}

Server-Side Validation

Validate via auth service:

POST /api/v1/auth/validate
Content-Type: application/json

Request:

{
  "token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Response:

{
  "valid": true,
  "payload": {
    "sub": "user-123",
    "exp": 1705323600,
    "roles": ["developer"],
    "permissions": ["agent:execute"]
  }
}

Invalid Token:

{
  "valid": false,
  "error": "token_expired",
  "message": "Token expired at 2025-01-15T10:00:00Z"
}

Public Keys (JWKS)

Get public keys for JWT verification:

GET /.well-known/jwks.json

Response:

{
  "keys": [
    {
      "kty": "RSA",
      "kid": "auth-key-2025-01",
      "use": "sig",
      "alg": "RS256",
      "n": "xGOr-H7A...",
      "e": "AQAB"
    },
    {
      "kty": "RSA",
      "kid": "auth-key-2024-12",
      "use": "sig",
      "alg": "RS256",
      "n": "yH1s-K8B...",
      "e": "AQAB"
    }
  ]
}

Error Responses

401 Unauthorized

{
  "error": {
    "type": "authentication_error",
    "code": "invalid_token",
    "message": "Invalid or expired JWT token",
    "details": {
      "reason": "token_expired",
      "expiredAt": "2025-01-15T10:00:00Z"
    }
  }
}

403 Forbidden

{
  "error": {
    "type": "authorization_error",
    "code": "insufficient_permissions",
    "message": "User lacks required permission",
    "details": {
      "required": "agent:execute",
      "userPermissions": ["agent:read"]
    }
  }
}

Security Best Practices

1. Token Storage

✅ Recommended: - Store in memory (React state, etc.) - Use secure HTTP-only cookies - Use secure session storage

❌ Avoid: - LocalStorage (XSS vulnerable) - URL parameters (logged everywhere) - Unencrypted cookies

2. Token Lifetime

Access Tokens: Short-lived (1 hour) Refresh Tokens: Long-lived (30 days)

3. Token Rotation

Automatically rotate tokens: - Before expiration (proactive refresh) - After security events (force re-auth) - During key rotation (seamless transition)

4. Scope Limitation

Request only required scopes:

{
  "scope": "agent:read workflow:execute"
}

Don't request admin unless absolutely necessary.

Rate Limiting

JWT authentication is rate-limited:

Operation Limit Window
Login 10 attempts 15 minutes
Token refresh 100 requests 1 hour
Token validation 1000 requests 1 minute

Rate Limit Headers:

X-RateLimit-Limit: 10
X-RateLimit-Remaining: 7
X-RateLimit-Reset: 1705320900

Client Libraries

TypeScript

import { AuthClient } from '@bluefly/auth-client';

const auth = new AuthClient({
  endpoint: 'https://auth.bluefly.io',
  clientId: 'llm-platform-web'
});

// Login
const { access_token, refresh_token } = await auth.login({
  username: 'developer@bluefly.io',
  password: 'secure-password'
});

// Automatic token refresh
const apiClient = auth.createClient({
  baseURL: 'https://gateway.local.bluefly.io',
  autoRefresh: true
});

// Use authenticated client
const response = await apiClient.post('/api/v1/chat/completions', {
  model: 'auto-route',
  messages: [{ role: 'user', content: 'Hello' }]
});

Python

from bluefly import AuthClient

auth = AuthClient(
    endpoint="https://auth.bluefly.io",
    client_id="llm-platform-cli"
)

# Login
tokens = auth.login(
    username="developer@bluefly.io",
    password="secure-password"
)

# Use access token
api = auth.create_client(
    base_url="https://gateway.local.bluefly.io",
    auto_refresh=True
)

response = api.post("/api/v1/chat/completions", json={
    "model": "auto-route",
    "messages": [{"role": "user", "content": "Hello"}]
})

Audit Logging

All authentication events are logged:

{
  "eventType": "login_success",
  "userId": "user-123",
  "timestamp": "2025-01-15T10:00:00Z",
  "ip": "192.168.1.100",
  "userAgent": "Mozilla/5.0...",
  "clientId": "llm-platform-web",
  "metadata": {
    "mfa": false,
    "provider": "local"
  }
}

See Security Architecture for audit details.

Next Steps