feat: add authentication and user management with Authentik integration
Build and Push / build (release) Successful in 1m16s
Build and Push / build (release) Successful in 1m16s
- Add PostgreSQL database with async SQLAlchemy - Add Alembic migrations for schema management - Add User, Role, UserPreferences, ApiKey models - Add auth endpoints: /auth/me, /auth/users, /auth/users/sync-from-authentik - Add token validation via Authentik userinfo endpoint - Add bulk user sync from Authentik admin API - Add database health check to diagnostics 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.5
parent
cdb6344013
commit
4f45f9bf37
@@ -0,0 +1,110 @@
|
||||
"""
|
||||
Authentication Schemas
|
||||
|
||||
Pydantic models for auth request/response payloads.
|
||||
"""
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
from pydantic import Field
|
||||
|
||||
from src.base_schema import BaseSchema
|
||||
|
||||
|
||||
class AuthSyncRequest(BaseSchema):
|
||||
"""
|
||||
Request payload for POST /auth/sync
|
||||
|
||||
The client sends this after obtaining an OIDC token from Authentik.
|
||||
The access_token is validated against Authentik's userinfo endpoint.
|
||||
"""
|
||||
|
||||
access_token: str = Field(
|
||||
...,
|
||||
description="OIDC access token from Authentik",
|
||||
)
|
||||
|
||||
|
||||
class RoleSchema(BaseSchema):
|
||||
"""Role information in domain:action format"""
|
||||
|
||||
name: str = Field(..., description="Role name (e.g., 'control-room:admin')")
|
||||
domain: str = Field(..., description="Permission domain (e.g., 'control-room')")
|
||||
action: str = Field(..., description="Permission action (e.g., 'admin')")
|
||||
|
||||
|
||||
class UserPreferencesSchema(BaseSchema):
|
||||
"""User preferences"""
|
||||
|
||||
theme: str = Field(default="system", description="Theme preference: system, light, dark")
|
||||
default_room: str = Field(default="front-hall", description="Default room for housekeeping")
|
||||
preferences_json: dict = Field(default_factory=dict, description="Extended preferences")
|
||||
|
||||
|
||||
class UserSchema(BaseSchema):
|
||||
"""User information returned from sync"""
|
||||
|
||||
id: uuid.UUID = Field(..., description="Internal user ID")
|
||||
authentik_id: uuid.UUID = Field(..., description="Authentik user ID")
|
||||
email: str = Field(..., description="User email")
|
||||
name: str = Field(..., description="Display name")
|
||||
avatar_url: Optional[str] = Field(None, description="Profile picture URL")
|
||||
created_at: datetime = Field(..., description="Account creation timestamp")
|
||||
last_login: Optional[datetime] = Field(None, description="Last login timestamp")
|
||||
|
||||
|
||||
class AuthSyncResponse(BaseSchema):
|
||||
"""
|
||||
Response from POST /auth/sync
|
||||
|
||||
Contains the synced user profile, roles, and preferences.
|
||||
"""
|
||||
|
||||
user: UserSchema = Field(..., description="User profile")
|
||||
roles: list[RoleSchema] = Field(..., description="User's permission roles")
|
||||
preferences: UserPreferencesSchema = Field(..., description="User preferences")
|
||||
is_new_user: bool = Field(..., description="True if user was just created")
|
||||
|
||||
|
||||
class TokenInfoSchema(BaseSchema):
|
||||
"""
|
||||
Token information from Authentik userinfo endpoint
|
||||
|
||||
This is what Authentik returns when validating an access token.
|
||||
"""
|
||||
|
||||
sub: str = Field(..., description="Subject (Authentik user ID)")
|
||||
email: str = Field(..., description="User email")
|
||||
name: Optional[str] = Field(None, description="Display name")
|
||||
preferred_username: Optional[str] = Field(None, description="Username")
|
||||
groups: list[str] = Field(default_factory=list, description="Group memberships")
|
||||
picture: Optional[str] = Field(None, description="Profile picture URL")
|
||||
|
||||
|
||||
class UserListItemSchema(BaseSchema):
|
||||
"""User item for list display"""
|
||||
|
||||
id: uuid.UUID = Field(..., description="Internal user ID")
|
||||
email: str = Field(..., description="User email")
|
||||
name: str = Field(..., description="Display name")
|
||||
avatar_url: Optional[str] = Field(None, description="Profile picture URL")
|
||||
created_at: datetime = Field(..., description="Account creation timestamp")
|
||||
last_login: Optional[datetime] = Field(None, description="Last login timestamp")
|
||||
roles: list[str] = Field(default_factory=list, description="Role names")
|
||||
|
||||
|
||||
class UsersListResponse(BaseSchema):
|
||||
"""Response from GET /auth/users"""
|
||||
|
||||
items: list[UserListItemSchema] = Field(..., description="List of users")
|
||||
total: int = Field(..., description="Total count of users")
|
||||
|
||||
|
||||
class BulkSyncResultSchema(BaseSchema):
|
||||
"""Result from bulk sync operation"""
|
||||
|
||||
created: int = Field(..., description="Number of users created")
|
||||
updated: int = Field(..., description="Number of users updated")
|
||||
failed: int = Field(..., description="Number of users that failed to sync")
|
||||
total_in_authentik: int = Field(..., description="Total users in Authentik")
|
||||
errors: list[str] = Field(default_factory=list, description="Error messages for failed syncs")
|
||||
Reference in New Issue
Block a user