#main.py from fastapi import FastAPI, Form, Depends, HTTPException, status from fastapi.requests import Request from fastapi.responses import HTMLResponse, RedirectResponse, JSONResponse from fastapi.templating import Jinja2Templates from sqlalchemy.orm import Session from auth import verify_token, oauth2_scheme, auth_views, register, UserCreate, authenticate_user, get_user_by_verification_token, resetpassword from database import get_db, get_user_by_email from datetime import timedelta from typing import Optional import httpx #import auth #import tts import os import asyncio my_secret_key = os.environ['my_secret_key'] from fastapi.staticfiles import StaticFiles app = FastAPI() app.mount("/static", StaticFiles(directory="static"), name="static") #router = APIRouter() templates = Jinja2Templates(directory="templates") #from google.cloud import recaptchaenterprise_v1 #from google.cloud.recaptchaenterprise_v1 import Assessment """ def create_assessment( project_id: str, recaptcha_key: str, token: str, recaptcha_action: str ) -> Assessment: client = recaptchaenterprise_v1.RecaptchaEnterpriseServiceClient() # Set the properties of the event to be tracked. event = recaptchaenterprise_v1.Event() event.site_key = recaptcha_key event.token = token assessment = recaptchaenterprise_v1.Assessment() assessment.event = event project_name = f"projects/{project_id}" # Build the assessment request. request = recaptchaenterprise_v1.CreateAssessmentRequest() request.assessment = assessment request.parent = project_name response = client.create_assessment(request) # Check if the token is valid. if not response.token_properties.valid: print( "The CreateAssessment call failed because the token was " + "invalid for the following reasons: " + str(response.token_properties.invalid_reason) ) return # Check if the expected action was executed. if response.token_properties.action != recaptcha_action: print( "The action attribute in your reCAPTCHA tag does" + "not match the action you are expecting to score" ) return else: # Get the risk score and the reason(s). # For more information on interpreting the assessment, see: # https://cloud.google.com/recaptcha-enterprise/docs/interpret-assessment for reason in response.risk_analysis.reasons: print(reason) print( "The reCAPTCHA score for this token is: " + str(response.risk_analysis.score) ) # Get the assessment name (ID). Use this to annotate the assessment. assessment_name = client.parse_assessment_path(response.name).get("assessment") print(f"Assessment name: {assessment_name}") return response """ # 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 # Route for the landing page @app.get("/", response_class=HTMLResponse) async def landing(request: Request): return templates.TemplateResponse("landing.html", {"request": request}) # Your other routes and app configuration go here @app.get("/login", response_class=HTMLResponse) async def login(request: Request): return templates.TemplateResponse("login.html", {"request": request}) @app.post("/login") async def login_post( request: Request, email: str = Form(...), password: str = Form(...), db: Session = Depends(get_db) ): if not email or not password: raise HTTPException(status_code=400, detail="Invalid email or password") user = authenticate_user(db, email, password) if user and user.is_verified: # Check if user is verified access_token = auth_views.create_access_token( data={"sub": user.email}, expires_delta=timedelta(minutes=auth_views.ACCESS_TOKEN_EXPIRE_MINUTES) ) # Redirect the user to the protected route with the token in the URL url = app.url_path_for("get_protected") # Ensure you have a name="get_protected" in your app.get("/protected") decorator #return RedirectResponse(url=f"/protected?token={access_token}", status_code=status.HTTP_303_SEE_OTHER) #return RedirectResponse(f"{url}?token={access_token}") response = RedirectResponse(f"{url}?token={access_token}", status_code=status.HTTP_303_SEE_OTHER) response.set_cookie(key="access_token", value=f"Bearer {access_token}", httponly=True) # response.set_cookie(key="access_token", value=access_token, httponly=True) return response elif user and not user.is_verified: # User is not verified raise HTTPException( status_code=400, detail="You must verify your email before accessing this resource." ) else: # If authentication fails, return to the login page with an error message return templates.TemplateResponse( "login.html", {"request": request, "error_message": "Invalid email or password"} ) @app.get("/register", response_class=HTMLResponse) async def register_get(request: Request): return templates.TemplateResponse("register.html", {"request": request}) @app.post("/register", response_class=HTMLResponse) async def register_post( request: Request, username: str = Form(...), email: str = Form(...), password: str = Form(...), confirm_password: str = Form(...), recaptcha_token: str = Form(...), # Add this line to accept the reCAPTCHA token db: Session = Depends(get_db) ): # Perform reCAPTCHA verification first recaptcha_secret = '6LdMjQcpAAAAAF8wutZt2Jcwvur-qtxsnoiGoHdG' # Replace with your reCAPTCHA secret key recaptcha_url = 'https://www.google.com/recaptcha/api/siteverify' recaptcha_data = { 'secret': recaptcha_secret, 'response': recaptcha_token } async with httpx.AsyncClient() as client: recaptcha_response = await client.post(recaptcha_url, data=recaptcha_data) recaptcha_result = recaptcha_response.json() print(recaptcha_result) # or use proper logging if not recaptcha_result.get('success', False): raise HTTPException(status_code=400, detail="reCAPTCHA validation failed.") # Call the create_assessment function to validate the token # assessment = await create_assessment( project_id, recaptcha_key, token, recaptcha_action ) # loop = asyncio.get_running_loop() # assessment = await loop.run_in_executor( # None, create_assessment, project_id, recaptcha_key, token, recaptcha_action # ) # Check the assessment result # if not assessment or assessment.risk_analysis.score < 0.5: # Use an appropriate risk score threshold # return templates.TemplateResponse("register.html", { # "request": request, # "error_message": "Captcha validation failed." # }) if password != confirm_password: # Return to the registration page with an error return templates.TemplateResponse("register.html", { "request": request, "error_message": "Passwords do not match." }) try: user = UserCreate(username=username, email=email, password=password, confirm_password=confirm_password) register(user, db) # If this function raises an HTTPException, it should be handled except HTTPException as e: # Return to the registration page with the error detail return templates.TemplateResponse("register.html", { "request": request, "error_message": e.detail }) # Redirect to the successful registration page response = RedirectResponse("/registration_successful", status_code=status.HTTP_302_FOUND) return response @app.get("/registration_successful", response_class=HTMLResponse) async def registration_successful(request: Request): # Render the successful registration page return templates.TemplateResponse("registration_successful.html", {"request": request}) @app.get("/verify", response_class=HTMLResponse) async def verify_email(token: str, db: Session = Depends(get_db)): user = get_user_by_verification_token(db, token) if not user: raise HTTPException(status_code=400, detail="Invalid verification token") if user.is_verified: raise HTTPException(status_code=400, detail="Email already verified") user.is_verified = True user.email_verification_token = None # Clear the verification token db.commit() # Create access token for the user after successful verification access_token = auth_views.create_access_token(data={"sub": user.email}, expires_delta=timedelta(minutes=auth_views.ACCESS_TOKEN_EXPIRE_MINUTES)) # Redirect to the protected route with the token as a query parameter (or as required by your front-end/client) return RedirectResponse(url=f"/protected?token={access_token}") #from jwt import decode, PyJWTError # make sure jwt is imported @app.get("/protected", response_class=HTMLResponse) async def get_protected( request: Request, db: Session = Depends(get_db), token: Optional[str] = None # token is Optional because it may come from the cookie ): # Try to get the token from the query parameter first, then fall back to the cookie token = token or request.cookies.get("access_token") if not token: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Not authenticated") # Here verify_token is used directly in the endpoint # If the token is invalid, verify_token will raise an HTTPException and the following lines will not be executed user_email = verify_token(token) # Assuming that verify_token returns the user's email if the token is valid # Get the user from the database db_user = get_user_by_email(db, user_email) if db_user is None or not db_user.is_verified: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="User not found or not verified in the database") # Render a template response return templates.TemplateResponse("protected.html", {"request": request, "user": db_user.username}) @app.get("/reset-password") async def reset_password_get(request: Request, token: str): return templates.TemplateResponse("reset-password.html", {"request": request, "token": token}) @app.post("/password-reset-request") async def password_reset_request(email: str = Form(...), db: Session = Depends(get_db)): user = get_user_by_email(db, email) if user: resetpassword(user,db) # Always return the same message to avoid revealing whether an email is registered return {"message": "Password reset link sent if the email is registered with us."} @app.get("/password-reset-request", response_class=HTMLResponse) async def password_reset_form(request: Request): return templates.TemplateResponse("password_reset_request.html", {"request": request}) from fastapi import Form @app.post("/reset-password") async def reset_password(token: str = Form(...), new_password: str = Form(...), db: Session = Depends(get_db)): user = get_user_by_verification_token(db, token) if not user: raise HTTPException(status_code=400, detail="Invalid or expired token") # Hash the new password hashed_password = auth_views.pwd_context.hash(new_password) # Update the user's password user.hashed_password = hashed_password user.email_verification_token = None # Clear the token db.commit() return {"message": "Password successfully reset."} #@app.get("/protected", response_class=HTMLResponse) #async def get_protected(request: Request, token: Optional[str] = None, db: Session = Depends(get_db)): # Try to get the token from the query parameter first, then fall back to the cookie # token = token or request.cookies.get("access_token") # if not token: # raise HTTPException(status_code=401, detail="Not authenticated") # try: # payload = decode(token, auth_views.SECRET_KEY, algorithms=[auth_views.ALGORITHM]) # user_email = payload.get("sub") # if user_email is None: # raise HTTPException(status_code=401, detail="Not authenticated") # except PyJWTError: # raise HTTPException(status_code=401, detail="Could not validate credentials") # db_user = get_user_by_email(db, user_email) # if db_user is None or not db_user.is_verified: # raise HTTPException(status_code=401, detail="User not found or not verified in the database") # return templates.TemplateResponse("protected.html", {"request": request, "user": db_user.username}) #async def get_protected( # request: Request, # token: str = Query(None), # Accept token from query parameters # db: Session = Depends(get_db) #): # Now pass both the request and token to the protected_route function # return await protected_route(request, token, db)