Files
CIAgent/apps/api/app/repositories/refresh_token_repository.py
T
saksham 1a4c80958f Initial commit: CI Agent competitive-intelligence monitoring app
FastAPI + Celery + Next.js + Postgres/Redis app with company monitoring,
source collection, LLM-based change analysis, enrichment, and account
security (Turnstile, escalating lockout, email verification).
2026-08-05 10:48:20 -04:00

56 lines
1.9 KiB
Python

from __future__ import annotations
import uuid
from datetime import UTC, datetime
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app.db.base import ensure_aware_utc
from app.models.refresh_token import RefreshToken
class RefreshTokenRepository:
def __init__(self, db: AsyncSession) -> None:
self.db = db
async def create(
self, *, user_id: uuid.UUID, token_hash: str, expires_at: datetime
) -> RefreshToken:
record = RefreshToken(user_id=user_id, token_hash=token_hash, expires_at=expires_at)
self.db.add(record)
await self.db.flush()
return record
async def get_valid_by_hash(self, token_hash: str) -> RefreshToken | None:
result = await self.db.execute(
select(RefreshToken).where(RefreshToken.token_hash == token_hash)
)
record = result.scalar_one_or_none()
if record is None:
return None
if record.revoked_at is not None:
return None
if ensure_aware_utc(record.expires_at) < datetime.now(UTC):
return None
return record
async def revoke(self, record: RefreshToken) -> None:
record.revoked_at = datetime.now(UTC)
await self.db.flush()
async def revoke_all_for_user(self, user_id: uuid.UUID) -> None:
"""Invalidates every active session for this user - used after a
password reset, since an attacker who had a valid refresh token
shouldn't stay logged in past the password change that locked them
out going forward."""
result = await self.db.execute(
select(RefreshToken).where(
RefreshToken.user_id == user_id, RefreshToken.revoked_at.is_(None)
)
)
now = datetime.now(UTC)
for record in result.scalars().all():
record.revoked_at = now
await self.db.flush()