Authentication
Session-based auth with Clerk integration, magic links, and service-to-service tokens.
Services Authentication
The Services API uses session-based authentication backed by Clerk for user identity and shared tokens for service-to-service communication.
Auth Flow
User → Clerk (sign in) → JWT → POST /api/auth/session → Session Token (UUID)
│
All subsequent API calls use this tokenSession Creation
After Clerk authentication, the frontend exchanges the Clerk JWT for a Reeve session token:
POST /api/auth/session
Authorization: Bearer <clerk-jwt>
# Response:
{
"session_token": "550e8400-e29b-41d4-a716-446655440000",
"user_id": "user_abc123",
"email": "user@example.com"
}Session Validation
Every API request validates the session token:
async def get_current_user(
authorization: str = Header(None),
db: Session = Depends(get_db),
) -> AuthUser:
token = extract_bearer_token(authorization)
if token:
session = validate_session(db, token)
if session:
return AuthUser(
user_id=session.user_id,
email=session.email
)
raise HTTPException(status_code=401, detail="Not authenticated")Auth Context
Every authenticated endpoint receives an AuthUser:
@router.get("/api/credits/balance")
async def get_balance(user: AuthUser = Depends(get_current_user)):
return credit_service.get_balance(user.user_id)Service-to-Service Auth
The gateway authenticates with the Services API using a shared secret:
def verify_service_token(
x_reeve_services_token: str = Header(None),
) -> bool:
expected = settings.reeve_services_token
if not x_reeve_services_token or x_reeve_services_token != expected:
raise HTTPException(status_code=401, detail="Invalid service token")
return TrueUsed for:
- Credit deductions (gateway → services after LLM calls)
- Usage tracking
- Tenant provisioning
Guest Mode
For demo and unauthenticated access:
async def get_current_user_or_guest(
x_guest_id: str = Header(None),
authorization: str = Header(None),
db: Session = Depends(get_db),
) -> AuthUser:
# Try session token first
if authorization:
# ... validate session
pass
# Fall back to guest
if x_guest_id:
return AuthUser(user_id=x_guest_id, is_guest=True)
raise HTTPException(status_code=401)Guest users get read-only access to demo data. Write operations check user.is_guest and reject accordingly.
Session tokens are UUIDs stored in PostgreSQL, not JWTs. This means they can be revoked instantly by deleting the row — no waiting for token expiry.