diff --git a/src/core/settings.py b/src/core/settings.py index cd41b11..5425743 100644 --- a/src/core/settings.py +++ b/src/core/settings.py @@ -30,20 +30,23 @@ class DbSettings(BaseModel): url: str = f"sqlite+aiosqlite:///{DB_PATH}" -class AccessToken(BaseSettings): - model_config = SettingsConfigDict( - env_file=".env", env_file_encoding="utf-8", env_prefix="ACCESS_TOKEN_" - ) +class AccessToken(BaseModel): expire_minutes: int secret_key: str algorithm: str = "HS256" token_type: str = "bearer" # noqa: S105 +class RefreshToken(BaseModel): + expire_days: int + + class Settings(BaseSettings): api: ApiPrefix = ApiPrefix() db: DbSettings = DbSettings() - access_token: AccessToken = AccessToken() # type: ignore + access_token: AccessToken + refresh_token: RefreshToken + model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8", env_nested_delimiter='__') -settings = Settings() +settings = Settings() # type: ignore diff --git a/src/migrations/versions/2025_09_21_1952-f015c2db2185_expire_and_fingerprintjs_token.py b/src/migrations/versions/2025_09_21_1952-f015c2db2185_expire_and_fingerprintjs_token.py new file mode 100644 index 0000000..db11918 --- /dev/null +++ b/src/migrations/versions/2025_09_21_1952-f015c2db2185_expire_and_fingerprintjs_token.py @@ -0,0 +1,28 @@ +"""expire and fingerprintjs token + +Revision ID: f015c2db2185 +Revises: b879d3502c37 +Create Date: 2025-09-21 19:52:28.292685 + +""" +from typing import Sequence, Union + +import sqlalchemy as sa +from alembic import op + +revision: str = 'f015c2db2185' +down_revision: Union[str, None] = 'b879d3502c37' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + op.add_column('refresh_tokens', sa.Column('fingerprint', sa.String(length=255), nullable=False)) + op.add_column('refresh_tokens', sa.Column('expired_at', sa.TIMESTAMP(timezone=True), server_default=sa.text("(datetime('now', '+7 days'))"), nullable=False)) + + +def downgrade() -> None: + """Downgrade schema.""" + op.drop_column('refresh_tokens', 'expired_at') + op.drop_column('refresh_tokens', 'fingerprint') diff --git a/src/models/tokens.py b/src/models/tokens.py index 99bcf3f..0c52fa2 100644 --- a/src/models/tokens.py +++ b/src/models/tokens.py @@ -1,7 +1,10 @@ -from sqlalchemy import ForeignKey, Integer, String +from datetime import datetime + +from sqlalchemy import TIMESTAMP, ForeignKey, Integer, String, text from sqlalchemy.orm import Mapped, mapped_column from src.core.database import Base +from src.core.settings import settings class RefreshTokensORM(Base): @@ -9,3 +12,8 @@ class RefreshTokensORM(Base): id: Mapped[int] = mapped_column(Integer, primary_key=True) token: Mapped[str] = mapped_column(String(255), nullable=False) user_id: Mapped[int] = mapped_column(Integer, ForeignKey("users.id")) + fingerprint: Mapped[str] = mapped_column(String(255), nullable=False) + expired_at: Mapped[datetime] = mapped_column( + TIMESTAMP(timezone=True), + server_default=text(f"datetime('now', '+{settings.refresh_token.expire_days} days')"), + ) diff --git a/src/services/auth.py b/src/services/auth.py index a332f78..1ff4eff 100644 --- a/src/services/auth.py +++ b/src/services/auth.py @@ -38,7 +38,11 @@ class AuthService(BaseService): ) access_token = AuthManager.create_access_token(user_token.model_dump()) refresh_token = AuthManager.create_refresh_token() - await self.session.auth.create_one({"token": refresh_token, "user_id": user.id}) + await self.session.auth.create_one({ + "token": refresh_token, + "user_id": user.id, + "fingerprint": "default" # TODO: Implement proper fingerprint generation + }) await self.session.commit() return { "access_token": access_token, @@ -60,6 +64,7 @@ class AuthService(BaseService): await self.session.auth.create_one({ "token": new_refresh_token, "user_id": user_data.id, + "fingerprint": "default" # TODO: Implement proper fingerprint generation }) await self.session.commit() return {