""" 認證相關 Schemas """ from typing import Optional, Dict, Any from pydantic import BaseModel, Field, ConfigDict class TokenResponse(BaseModel): """Token 響應""" access_token: str = Field(..., description="Access Token") token_type: str = Field(default="bearer", description="Token 類型") expires_in: int = Field(..., description="過期時間 (秒)") refresh_token: Optional[str] = Field(None, description="Refresh Token") model_config = ConfigDict( json_schema_extra={ "example": { "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...", "token_type": "bearer", "expires_in": 1800, "refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..." } } ) class UserInfo(BaseModel): """用戶資訊""" sub: str = Field(..., description="用戶 ID (Keycloak UUID)") username: str = Field(..., description="用戶名稱") email: str = Field(..., description="郵件地址") first_name: Optional[str] = Field(None, description="名字") last_name: Optional[str] = Field(None, description="姓氏") email_verified: bool = Field(False, description="郵件是否已驗證") tenant: Optional[Dict[str, Any]] = Field(None, description="租戶資訊") model_config = ConfigDict(from_attributes=True) class LoginRequest(BaseModel): """登入請求""" username: str = Field(..., min_length=3, description="用戶名稱") password: str = Field(..., min_length=6, description="密碼") model_config = ConfigDict( json_schema_extra={ "example": { "username": "porsche.chen@lab.taipei", "password": "your-password" } } )