Skip to main content

JWT Authentication

JWT Authentication

Separation of Duties: See Separation of Duties - API documentation is responsible for documenting APIs. It does NOT own agent manifests, execution, or infrastructure configuration.

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

ClaimDescriptionRequired
subSubject (user ID)
issIssuer (auth service)
audAudience (target service)
expExpiration time (Unix timestamp)
iatIssued at (Unix timestamp)
nbfNot before (Unix timestamp)
jtiJWT ID (unique identifier)
scopeOAuth 2.0 scopes
rolesUser roles
permissionsGranular permissions
userUser metadata
client_idClient 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:

OperationLimitWindow
Login10 attempts15 minutes
Token refresh100 requests1 hour
Token validation1000 requests1 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