Spaces:
Sleeping
Sleeping
| """ | |
| Auth utilities — bcrypt + JWT. Mirrored from NourishBot/auth/utils.py, adapted for CodyBuddy. | |
| """ | |
| import os | |
| import warnings | |
| from datetime import datetime, timedelta, timezone | |
| import bcrypt | |
| import jwt | |
| from fastapi import HTTPException, status | |
| _JWT_SECRET = os.environ.get("JWT_SECRET", "dev-secret-change-me") | |
| if _JWT_SECRET == "dev-secret-change-me": | |
| warnings.warn("JWT_SECRET not set — using insecure dev default", stacklevel=1) | |
| _ALGORITHM = "HS256" | |
| _EXPIRE_DAYS = 7 | |
| def hash_password(plain: str) -> str: | |
| return bcrypt.hashpw(plain.encode(), bcrypt.gensalt(rounds=12)).decode() | |
| def verify_password(plain: str, hashed: str) -> bool: | |
| return bcrypt.checkpw(plain.encode(), hashed.encode()) | |
| def create_access_token(user_id: str, email: str) -> str: | |
| # ponytail: re-read from env each call so tests can monkeypatch os.environ | |
| secret = os.environ.get("JWT_SECRET", "dev-secret-change-me") | |
| expire = datetime.now(timezone.utc) + timedelta(days=_EXPIRE_DAYS) | |
| payload = {"sub": user_id, "email": email, "exp": expire, "iat": datetime.now(timezone.utc)} | |
| return jwt.encode(payload, secret, algorithm=_ALGORITHM) | |
| def decode_access_token(token: str) -> dict: | |
| secret = os.environ.get("JWT_SECRET", "dev-secret-change-me") | |
| try: | |
| return jwt.decode(token, secret, algorithms=[_ALGORITHM]) | |
| except jwt.ExpiredSignatureError: | |
| raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Token has expired") | |
| except jwt.InvalidTokenError: | |
| raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token") | |