feat: Add Auth Service with JWT authentication
This commit is contained in:
129
services/auth-service/actor_context.py
Normal file
129
services/auth-service/actor_context.py
Normal file
@@ -0,0 +1,129 @@
|
||||
"""
|
||||
Actor Context Builder
|
||||
|
||||
Extracts ActorIdentity from request (session token or API key)
|
||||
"""
|
||||
import asyncpg
|
||||
from fastapi import Header, HTTPException, Cookie
|
||||
from typing import Optional
|
||||
from models import ActorIdentity, ActorType
|
||||
import json
|
||||
|
||||
async def build_actor_context(
|
||||
db_pool: asyncpg.Pool,
|
||||
authorization: Optional[str] = Header(None),
|
||||
session_token: Optional[str] = Cookie(None),
|
||||
x_api_key: Optional[str] = Header(None, alias="X-API-Key")
|
||||
) -> ActorIdentity:
|
||||
"""
|
||||
Build ActorIdentity from request
|
||||
|
||||
Priority:
|
||||
1. X-API-Key header
|
||||
2. Authorization header (Bearer token)
|
||||
3. session_token cookie
|
||||
|
||||
Raises HTTPException(401) if no valid credentials
|
||||
"""
|
||||
|
||||
# Try API Key first
|
||||
if x_api_key:
|
||||
actor = await get_actor_from_api_key(db_pool, x_api_key)
|
||||
if actor:
|
||||
return actor
|
||||
|
||||
# Try Authorization header
|
||||
if authorization and authorization.startswith("Bearer "):
|
||||
token = authorization.replace("Bearer ", "")
|
||||
actor = await get_actor_from_session(db_pool, token)
|
||||
if actor:
|
||||
return actor
|
||||
|
||||
# Try session cookie
|
||||
if session_token:
|
||||
actor = await get_actor_from_session(db_pool, session_token)
|
||||
if actor:
|
||||
return actor
|
||||
|
||||
raise HTTPException(
|
||||
status_code=401,
|
||||
detail="Unauthorized: No valid session token or API key"
|
||||
)
|
||||
|
||||
async def get_actor_from_session(db_pool: asyncpg.Pool, token: str) -> Optional[ActorIdentity]:
|
||||
"""Get ActorIdentity from session token"""
|
||||
async with db_pool.acquire() as conn:
|
||||
row = await conn.fetchrow(
|
||||
"""
|
||||
SELECT actor_id, actor_data, expires_at
|
||||
FROM sessions
|
||||
WHERE token = $1 AND is_valid = true
|
||||
""",
|
||||
token
|
||||
)
|
||||
|
||||
if not row:
|
||||
return None
|
||||
|
||||
# Check expiration
|
||||
from datetime import datetime, timezone
|
||||
if row['expires_at'] < datetime.now(timezone.utc):
|
||||
# Expired
|
||||
await conn.execute("UPDATE sessions SET is_valid = false WHERE token = $1", token)
|
||||
return None
|
||||
|
||||
# Parse actor data
|
||||
actor_data = row['actor_data']
|
||||
return ActorIdentity(**actor_data)
|
||||
|
||||
async def get_actor_from_api_key(db_pool: asyncpg.Pool, key: str) -> Optional[ActorIdentity]:
|
||||
"""Get ActorIdentity from API key"""
|
||||
async with db_pool.acquire() as conn:
|
||||
row = await conn.fetchrow(
|
||||
"""
|
||||
SELECT actor_id, actor_data, expires_at, last_used
|
||||
FROM api_keys
|
||||
WHERE key = $1 AND is_active = true
|
||||
""",
|
||||
key
|
||||
)
|
||||
|
||||
if not row:
|
||||
return None
|
||||
|
||||
# Check expiration
|
||||
from datetime import datetime, timezone
|
||||
if row['expires_at'] and row['expires_at'] < datetime.now(timezone.utc):
|
||||
# Expired
|
||||
await conn.execute("UPDATE api_keys SET is_active = false WHERE key = $1", key)
|
||||
return None
|
||||
|
||||
# Update last_used
|
||||
await conn.execute(
|
||||
"UPDATE api_keys SET last_used = NOW() WHERE key = $1",
|
||||
key
|
||||
)
|
||||
|
||||
# Parse actor data
|
||||
actor_data = row['actor_data']
|
||||
return ActorIdentity(**actor_data)
|
||||
|
||||
async def require_actor(
|
||||
db_pool: asyncpg.Pool,
|
||||
authorization: Optional[str] = Header(None),
|
||||
session_token: Optional[str] = Cookie(None),
|
||||
x_api_key: Optional[str] = Header(None, alias="X-API-Key")
|
||||
) -> ActorIdentity:
|
||||
"""
|
||||
Dependency for routes that require authentication
|
||||
|
||||
Usage:
|
||||
@app.get("/protected")
|
||||
async def protected_route(actor: ActorIdentity = Depends(require_actor)):
|
||||
...
|
||||
"""
|
||||
return await build_actor_context(db_pool, authorization, session_token, x_api_key)
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user