Added cognito authentication - Fix 8
All checks were successful
AWS Deploy on Push / build (push) Successful in 1m13s

This commit is contained in:
2025-05-15 16:52:54 -05:00
parent ae040fc49e
commit 30ccf86c86
2 changed files with 56 additions and 45 deletions

View File

@@ -1,19 +1,23 @@
import os
import boto3
import requests
import jwt
from fastapi import Depends, HTTPException, status, Request from fastapi import Depends, HTTPException, status, Request
from fastapi.security import OAuth2AuthorizationCodeBearer from fastapi.security import OAuth2AuthorizationCodeBearer
from fastapi.security.utils import get_authorization_scheme_param from fastapi.security.utils import get_authorization_scheme_param
import os from fastapi.responses import RedirectResponse
import jwt
REGION = "us-east-2" REGION = "us-east-2"
USER_POOL_ID = os.getenv("COGNITO_USER_POOL_ID") USER_POOL_ID = os.getenv("COGNITO_USER_POOL_ID")
CLIENT_ID = os.getenv("COGNITO_CLIENT_ID") CLIENT_ID = os.getenv("COGNITO_CLIENT_ID")
DOMAIN = f"https://iptv-updater.auth.{REGION}.amazoncognito.com" DOMAIN = f"https://iptv-updater.auth.{REGION}.amazoncognito.com"
class CustomOAuth2(OAuth2AuthorizationCodeBearer): class BrowserAwareOAuth2(OAuth2AuthorizationCodeBearer):
async def __call__(self, request: Request) -> str: async def __call__(self, request: Request) -> str:
# Check if this is a browser request # Check if this is a browser request
is_browser = "text/html" in request.headers.get("accept", "") is_browser = "text/html" in request.headers.get("accept", "")
# Try to get token from cookie first, then header
authorization = request.cookies.get("token") authorization = request.cookies.get("token")
if not authorization: if not authorization:
authorization = request.headers.get("Authorization") authorization = request.headers.get("Authorization")
@@ -22,31 +26,55 @@ class CustomOAuth2(OAuth2AuthorizationCodeBearer):
if not authorization or scheme.lower() != "bearer": if not authorization or scheme.lower() != "bearer":
if is_browser: if is_browser:
redirect_uri = str(request.base_url)[:-1] + "/auth/callback" # Remove trailing slash redirect_uri = str(request.base_url) + "auth/callback"
raise HTTPException( # Return redirect for browser requests
status_code=302, return RedirectResponse(
headers={ f"{DOMAIN}/login?client_id={CLIENT_ID}"
"Location": f"{DOMAIN}/login?client_id={CLIENT_ID}" f"&response_type=code"
f"&response_type=code" f"&scope=openid+email+profile"
f"&scope=openid+email+profile" f"&redirect_uri={redirect_uri}",
f"&redirect_uri={redirect_uri}" status_code=302
}
)
else:
raise HTTPException(
status_code=401,
detail="Not authenticated",
headers={"WWW-Authenticate": "Bearer"},
) )
# Return 401 for API requests
raise HTTPException(
status_code=401,
detail="Not authenticated",
headers={"WWW-Authenticate": "Bearer"},
)
return param return param
oauth2_scheme = CustomOAuth2( # Update the oauth2_scheme to use our custom class
oauth2_scheme = BrowserAwareOAuth2(
authorizationUrl=f"{DOMAIN}/oauth2/authorize", authorizationUrl=f"{DOMAIN}/oauth2/authorize",
tokenUrl=f"{DOMAIN}/oauth2/token", tokenUrl=f"{DOMAIN}/oauth2/token"
) )
def exchange_code_for_token(code: str, redirect_uri: str):
token_url = f"{DOMAIN}/oauth2/token"
data = {
'grant_type': 'authorization_code',
'client_id': CLIENT_ID,
'code': code,
'redirect_uri': redirect_uri
}
response = requests.post(token_url, data=data)
if response.status_code == 200:
return response.json()
print(f"Token exchange failed: {response.text}")
raise HTTPException(status_code=400, detail="Failed to exchange code for token")
async def get_current_user(request: Request, token: str = Depends(oauth2_scheme)): async def get_current_user(request: Request, token: str = Depends(oauth2_scheme)):
if not token:
redirect_uri = str(request.base_url) + "auth/callback"
return RedirectResponse(
f"{DOMAIN}/login?client_id={CLIENT_ID}"
f"&response_type=code"
f"&scope=openid+email+profile"
f"&redirect_uri={redirect_uri}"
)
try: try:
decoded = jwt.decode( decoded = jwt.decode(
token, token,

View File

@@ -1,22 +1,12 @@
from fastapi import FastAPI, Depends, HTTPException, Request, Response from fastapi import FastAPI, Depends, HTTPException, Request
from fastapi.responses import RedirectResponse, JSONResponse from fastapi.responses import RedirectResponse
from app.cabletv.utils.auth import get_current_user, exchange_code_for_token from app.cabletv.utils.auth import get_current_user, exchange_code_for_token
from fastapi.middleware.cors import CORSMiddleware
from starlette.middleware.sessions import SessionMiddleware
app = FastAPI() app = FastAPI()
# Add CORS middleware @app.get("/")
app.add_middleware( async def root():
CORSMiddleware, return {"message": "IPTV Updater API"}
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Add session middleware
app.add_middleware(SessionMiddleware, secret_key="your-secret-key")
@app.get("/protected") @app.get("/protected")
async def protected_route(request: Request, user = Depends(get_current_user)): async def protected_route(request: Request, user = Depends(get_current_user)):
@@ -28,17 +18,10 @@ async def auth_callback(request: Request, code: str):
redirect_uri = str(request.base_url) redirect_uri = str(request.base_url)
tokens = exchange_code_for_token(code, redirect_uri) tokens = exchange_code_for_token(code, redirect_uri)
# For browser requests, redirect to protected page # Create redirect response to protected route
is_browser = "text/html" in request.headers.get("accept", "") response = RedirectResponse(url="/protected", status_code=302)
if is_browser:
response = RedirectResponse(url="/protected")
else:
response = JSONResponse(content={
"message": "Authentication successful",
"id_token": tokens["id_token"]
})
# Set the token cookie # Set token cookie
response.set_cookie( response.set_cookie(
key="token", key="token",
value=tokens["id_token"], value=tokens["id_token"],