Overview
Every Anar product uses JWT-based authentication with role-based access control (RBAC). The shared library (anar_shared) provides a common auth module so all 13 Python backends enforce the same token format, signing algorithm, and role hierarchy.
Token Format
Anar uses HS256-signed JWTs with the following payload structure:
{
"sub": "user@example.com",
"role": "admin",
"exp": 1739750400
}
| Field | Type | Description |
|---|---|---|
sub | string | Subject identifier (username or email) |
role | string | One of admin, manager, user, or viewer |
exp | integer | Unix timestamp for token expiry |
Roles
The platform defines four roles with descending privilege levels:
| Role | Permissions |
|---|---|
admin | Full access to all resources, user management, configuration |
manager | Create, update, and delete resources within their scope |
user | Read access and limited write operations (submit requests, upload files) |
viewer | Read-only access to dashboards and reports |
Setting Up Auth in a Product
1. Initialize Auth Functions
Use make_auth to generate a token creator and a FastAPI dependency for extracting the current user from the Authorization header:
from anar_shared import make_auth, make_require_role
create_token, get_current_user = make_auth(
jwt_secret="your-secret-key",
jwt_algorithm="HS256",
jwt_expire_minutes=60,
)
require_role = make_require_role(get_current_user)
2. Protect Endpoints
Apply get_current_user as a dependency to require authentication, or use require_role to enforce specific roles:
from fastapi import APIRouter, Depends
router = APIRouter()
@router.get("/api/v1/resources")
async def list_resources(user=Depends(get_current_user)):
return {"user": user.sub, "role": user.role}
@router.delete("/api/v1/resources/{id}")
async def delete_resource(id: str, user=Depends(require_role("admin", "manager"))):
# Only admin and manager can delete
...
3. Generate Tokens
Use the create_token function to issue tokens during login or from a management endpoint:
@router.post("/api/v1/auth/token")
async def login(username: str, password: str):
# Validate credentials against your user store
user = await authenticate(username, password)
token = create_token(subject=user.email, role=user.role)
return {"access_token": token, "token_type": "bearer"}
Making Authenticated Requests
Include the JWT in the Authorization header as a Bearer token:
curl -X GET http://localhost:8001/api/v1/conversations \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."
For service-to-service calls, the AnarServiceClient from the shared library accepts an auth_token parameter:
from anar_shared import AnarServiceClient, ServiceConfig
client = AnarServiceClient(ServiceConfig(
guard_url="http://localhost:8002",
auth_token="your-service-token",
))
result = await client.guard_scan("Check this text for PII")
JWT Secret Configuration
Each product reads its JWT secret from an environment variable. The naming convention uses a product-specific prefix:
| Product | Environment Variable |
|---|---|
| Chat | CHAT_JWT_SECRET or JWT_SECRET |
| Guard | GUARD_JWT_SECRET or JWT_SECRET |
| Voice | VOICE_JWT_SECRET or JWT_SECRET |
| Agents | AGENTS_JWT_SECRET |
| Comply | COMPLY_JWT_SECRET |
| Eval | EVAL_JWT_SECRET or JWT_SECRET |
| Docs | DOCS_JWT_SECRET or JWT_SECRET |
| Flow | FLOW_JWT_SECRET |
| Insights | INSIGHTS_JWT_SECRET or JWT_SECRET |
| Translate | TRANSLATE_JWT_SECRET |
| Minutes | MINUTES_JWT_SECRET |
| Procure | PROCURE_JWT_SECRET |
Production Secrets
Never use the default development secrets in production. Generate a strong random secret with openssl rand -hex 32 and store it in your secrets manager.
Token Data Model
The TokenData model returned by get_current_user has two fields:
class TokenData(BaseModel):
sub: str # Subject (user identifier)
role: str # Role (defaults to "viewer" if not present in token)
If a token is missing the role claim, it defaults to viewer — the lowest privilege level. If the token is invalid or expired, the middleware returns a 401 Unauthorized response:
{
"detail": "Invalid authentication token"
}
Role Enforcement Errors
When a user has a valid token but lacks the required role, the middleware returns 403 Forbidden:
{
"detail": "Role 'user' not authorized. Requires: admin, manager"
}