Files
boc/iom/quixzoom-auth-service/models/web_session.py
T
Bernt 6989a98d75 feat: Passwordless cross-device authentication
- Arkitektur: docs/auth/passwordless-architecture.md
- Backend: iom/quixzoom-auth-service/ (FastAPI + Redis)
- Webb: quixzoom-market-pages/se/login/ (QR-kod + polling)
- App: iom/quixzoom-app/src/features/auth/ (push + deep links)

Flöde: QR-kod → app-godkännande → webb-inloggad
2026-07-07 07:11:50 +00:00

120 lines
3.9 KiB
Python

"""
Web Session model for passwordless authentication
Represents an authenticated web session after device approval
"""
from datetime import datetime
from typing import Optional, Dict, Any
from enum import Enum
from pydantic import BaseModel, Field
import uuid
class WebSessionStatus(str, Enum):
"""Web session status"""
ACTIVE = "active"
EXPIRED = "expired"
LOGGED_OUT = "logged_out"
REVOKED = "revoked"
class WebSession(BaseModel):
"""Web session database model"""
id: str = Field(default_factory=lambda: str(uuid.uuid4()))
# Session identifiers
session_token: str = Field(..., description="JWT session token")
refresh_token: str = Field(..., description="Refresh token for session renewal")
# User info
user_id: str = Field(..., description="Authenticated user ID")
auth_request_id: str = Field(..., description="Reference to auth request")
device_id: str = Field(..., description="Device that approved the login")
# Status
status: WebSessionStatus = Field(default=WebSessionStatus.ACTIVE)
# Security context
ip_address: Optional[str] = Field(None)
user_agent: Optional[str] = Field(None)
browser_fingerprint: Optional[str] = Field(None)
device_fingerprint: Optional[str] = Field(None)
# Session metadata
metadata: Dict[str, Any] = Field(default_factory=dict)
# Timestamps
created_at: datetime = Field(default_factory=datetime.utcnow)
expires_at: datetime = Field(..., description="Session expiration")
last_activity_at: datetime = Field(default_factory=datetime.utcnow)
logged_out_at: Optional[datetime] = Field(None)
class Config:
json_schema_extra = {
"example": {
"id": "ws_123456",
"session_token": "eyJhbGciOiJIUzI1NiIs...",
"refresh_token": "rt_abc123...",
"user_id": "user_abc123",
"auth_request_id": "auth_req_123",
"device_id": "dev_123456789",
"status": "active",
"created_at": "2024-01-15T10:30:00Z",
"expires_at": "2024-01-15T18:30:00Z",
"last_activity_at": "2024-01-15T10:35:00Z"
}
}
class WebSessionCreate(BaseModel):
"""Request model for creating web session"""
user_id: str
auth_request_id: str
device_id: str
ip_address: Optional[str] = None
user_agent: Optional[str] = None
browser_fingerprint: Optional[str] = None
device_fingerprint: Optional[str] = None
metadata: Dict[str, Any] = Field(default_factory=dict)
class WebSessionResponse(BaseModel):
"""Response model for web session"""
id: str
user_id: str
device_id: str
status: WebSessionStatus
created_at: datetime
expires_at: datetime
last_activity_at: datetime
class Config:
from_attributes = True
class WebSessionTokenResponse(BaseModel):
"""Response model for session tokens"""
access_token: str = Field(..., description="JWT access token")
refresh_token: str = Field(..., description="Refresh token")
token_type: str = Field(default="Bearer")
expires_in: int = Field(..., description="Token expiration in seconds")
session_id: str
class WebSessionCompleteRequest(BaseModel):
"""Request model for completing web session"""
request_token: str = Field(..., description="Auth request token")
browser_fingerprint: Optional[str] = Field(None)
user_agent: Optional[str] = Field(None)
ip_address: Optional[str] = Field(None)
class WebSessionRefreshRequest(BaseModel):
"""Request model for refreshing session"""
refresh_token: str = Field(..., description="Valid refresh token")
class WebSessionLogoutRequest(BaseModel):
"""Request model for logging out"""
session_token: str = Field(..., description="Session token to invalidate")
all_sessions: bool = Field(default=False, description="Logout all user sessions")