Working on: The Twitch authentication
This commit is contained in:
9
auth.py
9
auth.py
@@ -1,5 +1,6 @@
|
|||||||
import httpx
|
import httpx
|
||||||
import secrets
|
import secrets
|
||||||
|
import logging
|
||||||
from fastapi import APIRouter, Depends, HTTPException, Request, Response
|
from fastapi import APIRouter, Depends, HTTPException, Request, Response
|
||||||
from fastapi.responses import RedirectResponse
|
from fastapi.responses import RedirectResponse
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
@@ -11,6 +12,9 @@ import security
|
|||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
# Dependency to get a DB session
|
# Dependency to get a DB session
|
||||||
def get_db():
|
def get_db():
|
||||||
db = SessionLocal()
|
db = SessionLocal()
|
||||||
@@ -27,6 +31,7 @@ async def login_with_twitch(request: Request):
|
|||||||
# Generate a random state token for CSRF protection
|
# Generate a random state token for CSRF protection
|
||||||
state = secrets.token_urlsafe(16)
|
state = secrets.token_urlsafe(16)
|
||||||
request.session['oauth_state'] = state
|
request.session['oauth_state'] = state
|
||||||
|
logger.info(f"Generated OAuth state: {state} for session.")
|
||||||
|
|
||||||
# As per RESEARCH_REPORT.md, these are the minimum required scopes
|
# As per RESEARCH_REPORT.md, these are the minimum required scopes
|
||||||
scopes = "chat:read"
|
scopes = "chat:read"
|
||||||
@@ -48,7 +53,9 @@ async def auth_twitch_callback(code: str, state: str, request: Request, db: Sess
|
|||||||
Step 2 of OAuth flow: Handle the callback from Twitch after user authorization.
|
Step 2 of OAuth flow: Handle the callback from Twitch after user authorization.
|
||||||
"""
|
"""
|
||||||
# CSRF Protection: Validate the state
|
# CSRF Protection: Validate the state
|
||||||
if state != request.session.pop('oauth_state', None):
|
session_state = request.session.pop('oauth_state', None)
|
||||||
|
if state != session_state:
|
||||||
|
logger.error(f"OAuth state mismatch! Received state: '{state}', Session state: '{session_state}'")
|
||||||
raise HTTPException(status_code=403, detail="Invalid state parameter. CSRF attack suspected.")
|
raise HTTPException(status_code=403, detail="Invalid state parameter. CSRF attack suspected.")
|
||||||
|
|
||||||
# Step 4: Exchange the authorization code for an access token
|
# Step 4: Exchange the authorization code for an access token
|
||||||
|
|||||||
8
main.py
8
main.py
@@ -65,13 +65,13 @@ async def lifespan(app: FastAPI):
|
|||||||
|
|
||||||
app = FastAPI(lifespan=lifespan)
|
app = FastAPI(lifespan=lifespan)
|
||||||
|
|
||||||
# Add middleware to trust proxy headers (X-Forwarded-For, X-Forwarded-Proto)
|
|
||||||
# This is crucial for running behind a reverse proxy like Nginx or Caddy.
|
|
||||||
app.add_middleware(ProxyHeadersMiddleware, trusted_hosts="*")
|
|
||||||
|
|
||||||
# Add session middleware. A secret key is required for signing the session cookie.
|
# Add session middleware. A secret key is required for signing the session cookie.
|
||||||
# We can reuse our encryption key for this, but in production you might want a separate key.
|
# We can reuse our encryption key for this, but in production you might want a separate key.
|
||||||
|
# Note: Middleware is applied in reverse order (last added is first executed).
|
||||||
|
# We want ProxyHeaders to run FIRST (outermost) to fix the scheme/host,
|
||||||
|
# then SessionMiddleware to run SECOND (inner) so it sees the correct scheme.
|
||||||
app.add_middleware(SessionMiddleware, secret_key=settings.ENCRYPTION_KEY)
|
app.add_middleware(SessionMiddleware, secret_key=settings.ENCRYPTION_KEY)
|
||||||
|
app.add_middleware(ProxyHeadersMiddleware, trusted_hosts="*")
|
||||||
|
|
||||||
# Mount the 'static' directory using an absolute path for reliability
|
# Mount the 'static' directory using an absolute path for reliability
|
||||||
# This MUST be done before the routes that depend on it are defined.
|
# This MUST be done before the routes that depend on it are defined.
|
||||||
|
|||||||
Reference in New Issue
Block a user