# app/auth.py from fastapi import APIRouter, Depends, HTTPException, Form, Response, status, FastAPI from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm from fastapi.templating import Jinja2Templates from fastapi.responses import HTMLResponse from fastapi.requests import Request from pydantic import BaseModel from sqlalchemy.orm import Session from models import User from database import get_db import jwt from passlib.context import CryptContext from datetime import datetime, timedelta templates = Jinja2Templates(directory="templates") oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") class AuthViews: def __init__(self): self.pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") self.SECRET_KEY = "your-secret-key" # Replace with your actual secret key self.ALGORITHM = "HS256" self.ACCESS_TOKEN_EXPIRE_MINUTES = 30 def verify_token(token: str = Depends(oauth2_scheme)): try: payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) return payload.get("sub") except JWTError: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Could not validate credentials", headers={"WWW-Authenticate": "Bearer"}, ) # User model class UserCreate(BaseModel): username: str password: str email: str def register(self, user: UserCreate, db: Session = Depends(get_db)): # Validate email format and check for existing users db_user = database.get_user_by_email(db, user.email) if db_user: raise HTTPException(status_code=400, detail="Email already registered") # Hash the password hashed_password = self.pwd_context.hash(user.password) # Generate a verification token verification_token = email.generate_verification_token(user.email) # Send a verification email (implement email.send_verification_email) # Create the user in the database user_in_db = models.User(email=user.email, hashed_password=hashed_password) db.add(user_in_db) db.commit() db.refresh(user_in_db) return user_in_db def verify_email(self, verification_token: str, db: Session = Depends(get_db)): # Verify the email using the token (implement email.verify_token) email = email.verify_token(verification_token) if not email: raise HTTPException(status_code=400, detail="Invalid verification token") # Get the user by email user = database.get_user_by_email(db, email) if not user: raise HTTPException(status_code=400, detail="User not found") if user.is_verified: raise HTTPException(status_code=400, detail="Email already verified") # Mark the email as verified user.is_verified = True db.commit() return {"message": "Email verification successful"} # Dependency for verifying the user's token def get_current_user(token: str = Depends(verify_token)): if not token: raise HTTPException(status_code=401, detail="Token not valid") return token # Function to generate JWT tokens def create_access_token(self, data: dict, expires_delta: timedelta): to_encode = data.copy() expire = datetime.utcnow() + expires_delta to_encode.update({"exp": expire}) encoded_jwt = jwt.encode(to_encode, self.SECRET_KEY, algorithm=self.ALGORITHM) return encoded_jwt # Your login route @app.post("/auth/login", response_model=dict) def login(self, form_data: OAuth2PasswordRequestForm = Depends()): # Check email verification db_user = database.get_user_by_email(db, form_data.username) if not db_user or not self.pwd_context.verify(form_data.password, db_user.hashed_password): raise HTTPException(status_code=400, detail="Incorrect email or password") if not db_user.is_verified: raise HTTPException(status_code=400, detail="Email not verified") # Generate an access token access_token_expires = timedelta(minutes=self.ACCESS_TOKEN_EXPIRE_MINUTES) access_token = create_access_token({"sub": db_user.email}, access_token_expires) return {"access_token": access_token, "token_type": "bearer"} auth_views = AuthViews() @app.get("/login", response_class=HTMLResponse) async def login(request: Request): return templates.TemplateResponse("login.html", {"request": request}) @app.get("/register", response_class=HTMLResponse) async def register(request: Request): return templates.TemplateResponse("register.html", {"request": request}) @app.get("/verify/{verification_token}", response_class=HTMLResponse) async def verify_email(verification_token: str, request: Request): # Perform verification and return an appropriate template return templates.TemplateResponse("verify.html", {"request": request}) # User authentication (protected route) @app.get("/protected", response_model=str) async def protected_route(request: Request, token: str = Depends(oauth2_scheme), db: Session = Depends(get_db)): # Verify the access token user = verify_token(token, SECRET_KEY, ALGORITHM) if user is None: raise HTTPException(status_code=401, detail="Invalid or expired token") # Check if the user exists in the database db_user = get_user_by_email(db, user) # Modify this to match your database query if db_user is None: raise HTTPException(status_code=401, detail="User not found in the database") # The user exists in the database, and you can render the protected route template return templates.TemplateResponse("protected.html", {"request": request, "user": db_user.username})