first commit
This commit is contained in:
159
app/schemas/auth.py
Normal file
159
app/schemas/auth.py
Normal file
@ -0,0 +1,159 @@
|
||||
"""
|
||||
Authentication Schemas
|
||||
|
||||
Pydantic schemas for authentication and OIDC-related requests/responses.
|
||||
"""
|
||||
|
||||
from pydantic import BaseModel, Field, field_validator
|
||||
from typing import Optional, List
|
||||
|
||||
|
||||
# ==========================================
|
||||
# OIDC Authorization Schemas
|
||||
# ==========================================
|
||||
|
||||
class AuthorizationRequest(BaseModel):
|
||||
"""Schema for OIDC authorization request."""
|
||||
client_id: str = Field(..., description="OAuth client ID")
|
||||
redirect_uri: str = Field(..., description="Callback URL")
|
||||
response_type: str = Field("code", description="Response type (only 'code' supported)")
|
||||
scope: str = Field("openid", description="Requested scopes (space-separated)")
|
||||
state: Optional[str] = Field(None, description="CSRF protection state")
|
||||
|
||||
@field_validator('response_type')
|
||||
@classmethod
|
||||
def validate_response_type(cls, v: str) -> str:
|
||||
"""Validate that response_type is 'code'."""
|
||||
if v != "code":
|
||||
raise ValueError("Only 'code' response_type is supported (Authorization Code Flow)")
|
||||
return v
|
||||
|
||||
@field_validator('scope')
|
||||
@classmethod
|
||||
def validate_scope(cls, v: str) -> str:
|
||||
"""Validate that scope includes 'openid'."""
|
||||
scopes = v.split()
|
||||
if 'openid' not in scopes:
|
||||
raise ValueError("Scope must include 'openid'")
|
||||
return v
|
||||
|
||||
|
||||
class AuthorizationResponse(BaseModel):
|
||||
"""Schema for OIDC authorization response."""
|
||||
code: str = Field(..., description="Authorization code")
|
||||
state: Optional[str] = Field(None, description="State from request")
|
||||
|
||||
|
||||
# ==========================================
|
||||
# OIDC Token Schemas
|
||||
# ==========================================
|
||||
|
||||
class TokenRequest(BaseModel):
|
||||
"""Schema for OIDC token request."""
|
||||
grant_type: str = Field(..., description="Grant type (authorization_code)")
|
||||
code: str = Field(..., description="Authorization code")
|
||||
redirect_uri: str = Field(..., description="Redirect URI (must match authorization request)")
|
||||
client_id: str = Field(..., description="OAuth client ID")
|
||||
client_secret: str = Field(..., description="OAuth client secret")
|
||||
|
||||
@field_validator('grant_type')
|
||||
@classmethod
|
||||
def validate_grant_type(cls, v: str) -> str:
|
||||
"""Validate grant_type."""
|
||||
if v != "authorization_code":
|
||||
raise ValueError("Only 'authorization_code' grant_type is supported")
|
||||
return v
|
||||
|
||||
|
||||
class TokenResponse(BaseModel):
|
||||
"""Schema for OIDC token response."""
|
||||
access_token: str = Field(..., description="Access token")
|
||||
token_type: str = Field("Bearer", description="Token type")
|
||||
expires_in: int = Field(..., description="Token expiration time in seconds")
|
||||
id_token: str = Field(..., description="OpenID Connect ID token")
|
||||
scope: str = Field(..., description="Granted scopes")
|
||||
|
||||
|
||||
# ==========================================
|
||||
# UserInfo Schemas
|
||||
# ==========================================
|
||||
|
||||
class UserInfoResponse(BaseModel):
|
||||
"""Schema for OIDC UserInfo response."""
|
||||
sub: str = Field(..., description="Subject identifier (user ID)")
|
||||
username: str
|
||||
email: str
|
||||
name: str
|
||||
preferred_username: str
|
||||
role: str
|
||||
permissions: List[str]
|
||||
|
||||
|
||||
# ==========================================
|
||||
# Login/Registration Schemas
|
||||
# ==========================================
|
||||
|
||||
class LoginRequest(BaseModel):
|
||||
"""Schema for user login."""
|
||||
username: str = Field(..., description="Username")
|
||||
password: str = Field(..., description="Password")
|
||||
|
||||
|
||||
class LoginResponse(BaseModel):
|
||||
"""Schema for login response."""
|
||||
success: bool
|
||||
message: str
|
||||
user: Optional[dict] = None
|
||||
redirect_url: Optional[str] = None
|
||||
|
||||
|
||||
class RegisterRequest(BaseModel):
|
||||
"""Schema for user registration."""
|
||||
username: str = Field(..., min_length=3, max_length=80)
|
||||
email: str = Field(..., description="Email address")
|
||||
name: str = Field(..., min_length=1, max_length=120)
|
||||
password: str = Field(..., min_length=8)
|
||||
password_confirm: str = Field(..., description="Password confirmation")
|
||||
preferred_username: Optional[str] = Field(None, max_length=80)
|
||||
|
||||
@field_validator('password')
|
||||
@classmethod
|
||||
def validate_password(cls, v: str) -> str:
|
||||
"""Validate password strength."""
|
||||
if len(v) < 8:
|
||||
raise ValueError('Password must be at least 8 characters long')
|
||||
return v
|
||||
|
||||
@field_validator('password_confirm')
|
||||
@classmethod
|
||||
def passwords_match(cls, v: str, info) -> str:
|
||||
"""Validate that passwords match."""
|
||||
if 'password' in info.data and v != info.data['password']:
|
||||
raise ValueError('Passwords do not match')
|
||||
return v
|
||||
|
||||
|
||||
class RegisterResponse(BaseModel):
|
||||
"""Schema for registration response."""
|
||||
success: bool
|
||||
message: str
|
||||
user_id: Optional[int] = None
|
||||
|
||||
|
||||
# ==========================================
|
||||
# OIDC Discovery Schemas
|
||||
# ==========================================
|
||||
|
||||
class OIDCDiscoveryResponse(BaseModel):
|
||||
"""Schema for OIDC discovery document."""
|
||||
issuer: str
|
||||
authorization_endpoint: str
|
||||
token_endpoint: str
|
||||
userinfo_endpoint: str
|
||||
jwks_uri: str
|
||||
response_types_supported: List[str]
|
||||
subject_types_supported: List[str]
|
||||
id_token_signing_alg_values_supported: List[str]
|
||||
scopes_supported: List[str]
|
||||
token_endpoint_auth_methods_supported: List[str]
|
||||
claims_supported: List[str]
|
||||
Reference in New Issue
Block a user