Files
microdao-daarion/services/auth-service/routes_sessions.py
Apple 6bd769ef40 feat(city-map): Add 2D City Map with coordinates and agent presence
- Add migration 013_city_map_coordinates.sql with map coordinates, zones, and agents table
- Add /city/map API endpoint in city-service
- Add /city/agents and /city/agents/online endpoints
- Extend presence aggregator to include agents[] in snapshot
- Add AgentsSource for fetching agent data from DB
- Create CityMap component with interactive room tiles
- Add useCityMap hook for fetching map data
- Update useGlobalPresence to include agents
- Add map/list view toggle on /city page
- Add agent badges to room cards and map tiles
2025-11-27 07:00:47 -08:00

131 lines
3.4 KiB
Python

"""
Session management routes
"""
from fastapi import APIRouter, HTTPException, Depends, Response
import asyncpg
from datetime import datetime, timedelta, timezone
import secrets
from models import LoginRequest, LoginResponse, ActorIdentity, ActorType
from actor_context import require_actor
import json
router = APIRouter(prefix="/auth", tags=["sessions"])
# Mock users for Phase 4
# In production, this would be in database with proper password hashing
MOCK_USERS = {
"admin@daarion.city": {
"actor_id": "user:1",
"actor_type": "human",
"microdao_ids": ["microdao:daarion"],
"roles": ["system_admin", "microdao_owner"]
},
"user@daarion.city": {
"actor_id": "user:93",
"actor_type": "human",
"microdao_ids": ["microdao:daarion", "microdao:7"],
"roles": ["member", "microdao_owner"]
},
"sofia@agents.daarion.city": {
"actor_id": "agent:sofia",
"actor_type": "agent",
"microdao_ids": ["microdao:daarion"],
"roles": ["agent"]
}
}
def get_db_pool(request) -> asyncpg.Pool:
"""Get database pool from app state"""
return request.app.state.db_pool
@router.post("/login", response_model=LoginResponse)
async def login(
request: LoginRequest,
response: Response,
db_pool: asyncpg.Pool = Depends(get_db_pool)
):
"""
Login and get session token
Phase 4: Mock implementation with predefined users
Phase 5: Real Passkey integration
"""
# Check mock users
if request.email not in MOCK_USERS:
raise HTTPException(401, "Invalid credentials")
user_data = MOCK_USERS[request.email]
# Build ActorIdentity
actor = ActorIdentity(
actor_id=user_data["actor_id"],
actor_type=ActorType(user_data["actor_type"]),
microdao_ids=user_data["microdao_ids"],
roles=user_data["roles"]
)
# Generate session token
token = secrets.token_urlsafe(32)
expires_at = datetime.now(timezone.utc) + timedelta(days=7)
# Store in database
async with db_pool.acquire() as conn:
await conn.execute(
"""
INSERT INTO sessions (token, actor_id, actor_data, expires_at)
VALUES ($1, $2, $3, $4)
""",
token,
actor.actor_id,
json.dumps(actor.model_dump()),
expires_at
)
# Set cookie
response.set_cookie(
key="session_token",
value=token,
httponly=True,
max_age=7 * 24 * 60 * 60, # 7 days
samesite="lax"
)
return LoginResponse(
session_token=token,
actor=actor,
expires_at=expires_at
)
@router.get("/me", response_model=ActorIdentity)
async def get_me(
actor: ActorIdentity = Depends(require_actor)
):
"""Get current actor identity"""
return actor
@router.post("/logout")
async def logout(
response: Response,
actor: ActorIdentity = Depends(require_actor),
db_pool: asyncpg.Pool = Depends(get_db_pool)
):
"""Logout and invalidate session"""
# Invalidate all sessions for this actor
async with db_pool.acquire() as conn:
await conn.execute(
"UPDATE sessions SET is_valid = false WHERE actor_id = $1",
actor.actor_id
)
# Clear cookie
response.delete_cookie("session_token")
return {"status": "logged_out"}