c-stone-invoice-check/backend/auth.py
Markus Kruse 937267b507 feat: Backend complete with IMAP, PDF processing, Alembic migrations
- FastAPI Backend mit Router (emails, invoices, auth)
- PostgreSQL DB Models (Invoice, EmailLog, InvoiceDetail, User)
- PyMuPDF PDF-Verarbeitung mit Steuer-Erkennung
- openpyxl Excel-Export
- Alembic Migration initialisiert
- IMAP-Verbindung zu Strato funktioniert
- Docker-Setup mit Traefik-Labels

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-24 23:45:51 +02:00

98 lines
2.9 KiB
Python

"""
C-Stone Invoice Check — Authentifizierung
JWT-basierte Auth mit PyJWT
"""
import os
from datetime import datetime, timedelta
from typing import Optional
from jose import JWTError, jwt
from passlib.context import CryptContext
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from sqlalchemy.orm import Session, declarative_base
from sqlalchemy import Column, Integer, String, Boolean, DateTime
from backend.database import get_db
Base = declarative_base()
# Konfiguration
SECRET_KEY = os.getenv("SECRET_KEY", "your-secret-key-change-in-production")
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
# Hash-Kontext
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
# OAuth2
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/token")
# Router
from fastapi import APIRouter
router = APIRouter()
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
username = Column(String, unique=True, index=True)
hashed_password = Column(String)
is_active = Column(Boolean, default=True)
created_at = Column(DateTime, default=datetime.utcnow)
def verify_password(plain_password: str, hashed_password: str) -> bool:
"""Prüft Passwort gegen Hash"""
return pwd_context.verify(plain_password, hashed_password)
def get_password_hash(password: str) -> str:
"""Erstellt Hash aus Passwort"""
return pwd_context.hash(password)
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None) -> str:
"""Erstellt JWT Token"""
to_encode = data.copy()
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(minutes=15)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
def authenticate_user(db: Session, username: str, password: str) -> Optional[User]:
"""Authentifiziert Benutzer"""
user = db.query(User).filter(User.username == username).first()
if not user:
return None
if not verify_password(password, user.hashed_password):
return None
return user
async def get_current_user(
token: str = Depends(oauth2_scheme),
db: Session = Depends(get_db)
) -> User:
"""Holt aktuellen Benutzer aus Token"""
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise credentials_exception
except JWTError:
raise credentials_exception
user = db.query(User).filter(User.username == username).first()
if user is None:
raise credentials_exception
return user