"""Authentication state: opaque backend-issued sessions and single-use email tokens. Only token *hashes* are stored (see app.core.security). """ import uuid from datetime import datetime from sqlalchemy import DateTime, ForeignKey, String, func from sqlalchemy import Enum as SAEnum from sqlalchemy.orm import Mapped, mapped_column from app.models.base import Base from app.models.enums import TokenPurpose from app.models.mixins import UUIDPrimaryKey class Session(Base, UUIDPrimaryKey): __tablename__ = "sessions" user_id: Mapped[uuid.UUID] = mapped_column( ForeignKey("users.id", ondelete="CASCADE"), index=True ) token_hash: Mapped[str] = mapped_column(String(64), unique=True, index=True) created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), server_default=func.now(), nullable=False ) expires_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False) revoked_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True)) class UserToken(Base, UUIDPrimaryKey): __tablename__ = "user_tokens" user_id: Mapped[uuid.UUID] = mapped_column( ForeignKey("users.id", ondelete="CASCADE"), index=True ) purpose: Mapped[TokenPurpose] = mapped_column(SAEnum(TokenPurpose, name="token_purpose")) token_hash: Mapped[str] = mapped_column(String(64), unique=True, index=True) created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), server_default=func.now(), nullable=False ) expires_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False) used_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))