Spaces:
Running on Zero
Running on Zero
Download codestar/server/auth.py from dongyanggg/codestar-backend: direct link, hf CLI and curl.
- Browser
- Download file 3 kB
-
https://huggingface.co/spaces/dongyanggg/codestar-backend/resolve/main/codestar/server/auth.py
- Command line
-
hf download hf://spaces/dongyanggg/codestar-backend/codestar/server/auth.py
-
curl -L -o auth.py https://huggingface.co/spaces/dongyanggg/codestar-backend/resolve/main/codestar/server/auth.py
3 kB
| """Authentication and JWT Token Utilities for CodeStar.""" | |
| from __future__ import annotations | |
| import os | |
| import hmac | |
| import hashlib | |
| import secrets | |
| from datetime import datetime, timezone, timedelta | |
| from typing import Optional, Dict, Any | |
| from fastapi import Header, HTTPException, status | |
| import jwt | |
| SECRET_KEY = os.environ.get("CODESTAR_JWT_SECRET", "codestar-super-secret-key-change-in-production-2026") | |
| ALGORITHM = "HS256" | |
| ACCESS_TOKEN_EXPIRE_DAYS = 30 | |
| def hash_password(password: str) -> tuple[str, str]: | |
| """Hashes a password with a random salt using PBKDF2-HMAC-SHA256.""" | |
| salt = secrets.token_hex(16) | |
| pwd_hash = hashlib.pbkdf2_hmac( | |
| "sha256", password.encode("utf-8"), salt.encode("utf-8"), 100_000 | |
| ).hex() | |
| return pwd_hash, salt | |
| def verify_password(plain_password: str, stored_hash: str, salt: str) -> bool: | |
| """Verifies a plain password against the stored hash and salt.""" | |
| calc_hash = hashlib.pbkdf2_hmac( | |
| "sha256", plain_password.encode("utf-8"), salt.encode("utf-8"), 100_000 | |
| ).hex() | |
| return hmac.compare_digest(calc_hash, stored_hash) | |
| def create_access_token(user_id: int, username: str, expires_delta: Optional[timedelta] = None) -> str: | |
| """Creates a signed JWT access token.""" | |
| expire = datetime.now(timezone.utc) + ( | |
| expires_delta if expires_delta else timedelta(days=ACCESS_TOKEN_EXPIRE_DAYS) | |
| ) | |
| payload = { | |
| "sub": str(user_id), | |
| "username": username, | |
| "exp": expire, | |
| } | |
| return jwt.encode(payload, SECRET_KEY, algorithm=ALGORITHM) | |
| def decode_access_token(token: str) -> Optional[Dict[str, Any]]: | |
| """Decodes and validates a JWT token.""" | |
| try: | |
| payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) | |
| return payload | |
| except (jwt.PyJWTError, Exception): | |
| return None | |
| def get_current_user(authorization: Optional[str] = Header(None)) -> Optional[Dict[str, Any]]: | |
| """Extracts the logged-in user from Authorization header if present, otherwise returns None.""" | |
| if not authorization: | |
| return None | |
| parts = authorization.split() | |
| if len(parts) != 2 or parts[0].lower() != "bearer": | |
| return None | |
| token = parts[1] | |
| payload = decode_access_token(token) | |
| if not payload or "sub" not in payload: | |
| return None | |
| try: | |
| user_id = int(payload["sub"]) | |
| username = payload.get("username", "") | |
| return {"id": user_id, "username": username} | |
| except (ValueError, TypeError): | |
| return None | |
| def require_current_user(authorization: Optional[str] = Header(None)) -> Dict[str, Any]: | |
| """Requires a valid logged-in user, raising 401 Unauthorized if missing or invalid.""" | |
| user = get_current_user(authorization) | |
| if not user: | |
| raise HTTPException( | |
| status_code=status.HTTP_401_UNAUTHORIZED, | |
| detail="Authentication required. Please login first.", | |
| headers={"WWW-Authenticate": "Bearer"}, | |
| ) | |
| return user | |