MAL_Agent / src /auth.py
Kunal
update auth.py
8f64006
# auth.py
import streamlit as st
import requests
import webbrowser
import secrets
import threading
import time
from http.server import HTTPServer, BaseHTTPRequestHandler
from urllib.parse import urlparse, parse_qs
from dotenv import load_dotenv
import os
load_dotenv()
# Configuration (move these to secrets.toml in production)
CLIENT_ID = os.getenv("MAL_CLIENT_ID")
CLIENT_SECRET = os.getenv("MAL_CLIENT_SECRET")
REDIRECT_URI = os.getenv("MAL_REDIRECT_URI", "http://localhost:8000/callback")
PORT = int(os.getenv("MAL_PORT", 8000))
class MALAuth:
def __init__(self):
self.code_verifier = secrets.token_urlsafe(64)
self.code_challenge = self.code_verifier # Using 'plain' method
self.auth_code = None
self.error = None
class OAuthHandler(BaseHTTPRequestHandler):
auth_code = None
error = None
def do_GET(self):
parsed = urlparse(self.path)
params = parse_qs(parsed.query)
if "code" in params:
MALAuth.OAuthHandler.auth_code = params["code"][0]
self.send_response(200)
self.end_headers()
self.wfile.write(b"Authorization successful! Return to the app.")
elif "error" in params:
MALAuth.OAuthHandler.error = params["error"][0]
self.send_response(400)
self.end_headers()
self.wfile.write(b"Authorization failed. Check your settings.")
else:
self.send_response(400)
self.end_headers()
self.wfile.write(b"Invalid request")
def run_server(self):
server = HTTPServer(('localhost', PORT), self.OAuthHandler)
server.timeout = 120
server.handle_request()
def start_oauth_flow(self):
server_thread = threading.Thread(target=self.run_server)
server_thread.daemon = True
server_thread.start()
auth_url = (
"https://myanimelist.net/v1/oauth2/authorize?"
f"response_type=code&"
f"client_id={CLIENT_ID}&"
f"code_challenge={self.code_challenge}&"
f"redirect_uri={REDIRECT_URI}"
)
webbrowser.open(auth_url)
start_time = time.time()
while not self.OAuthHandler.auth_code and not self.OAuthHandler.error:
if time.time() - start_time > 120:
return None, "Authorization timed out"
time.sleep(0.5)
return self.OAuthHandler.auth_code, self.OAuthHandler.error
def get_access_token(self, auth_code):
token_url = "https://myanimelist.net/v1/oauth2/token"
data = {
"client_id": CLIENT_ID,
"client_secret": CLIENT_SECRET,
"code": auth_code,
"code_verifier": self.code_verifier,
"grant_type": "authorization_code",
"redirect_uri": REDIRECT_URI
}
try:
response = requests.post(token_url, data=data)
response.raise_for_status()
return response.json()["access_token"], None
except requests.exceptions.HTTPError as e:
return None, f"Token exchange failed: {e.response.status_code} {e.response.text}"
def login_button():
"""Streamlit component for handling MAL login"""
if st.button("Login with MyAnimeList"):
with st.spinner("Authenticating..."):
auth = MALAuth()
auth_code, error = auth.start_oauth_flow()
if error:
st.error(error)
return False
access_token, error = auth.get_access_token(auth_code)
if error:
st.error(error)
return False
st.session_state.access_token = access_token
st.rerun()
return True