diff --git a/app/auth/providers.py b/app/auth/providers.py index 9cd457d..609f4d0 100644 --- a/app/auth/providers.py +++ b/app/auth/providers.py @@ -22,11 +22,11 @@ class OAuthProvider(ABC): """Whether this provider is configured.""" @abstractmethod - def get_authorize_url(self, state: str) -> str: + def get_authorize_url(self, state: str, redirect_uri: str | None = None) -> str: """Build the authorization URL.""" @abstractmethod - async def exchange_code(self, code: str) -> dict | None: + async def exchange_code(self, code: str, redirect_uri: str | None = None) -> dict | None: """Exchange authorization code for access token.""" @abstractmethod @@ -53,25 +53,25 @@ class GiteaProvider(OAuthProvider): def is_enabled(self) -> bool: return bool(self.client_id and self.client_secret) - def get_authorize_url(self, state: str) -> str: + def get_authorize_url(self, state: str, redirect_uri: str | None = None) -> str: return ( f"{self.base}/login/oauth/authorize?" + urlencode({ "client_id": self.client_id, - "redirect_uri": self.redirect_uri, + "redirect_uri": redirect_uri or self.redirect_uri, "response_type": "code", "state": state, }) ) - async def exchange_code(self, code: str) -> dict | None: + async def exchange_code(self, code: str, redirect_uri: str | None = None) -> dict | None: url = f"{self.base}/login/oauth/access_token" data = { "client_id": self.client_id, "client_secret": self.client_secret, "code": code, "grant_type": "authorization_code", - "redirect_uri": self.redirect_uri, + "redirect_uri": redirect_uri or self.redirect_uri, } async with httpx.AsyncClient(timeout=15) as client: r = await client.post(url, json=data, headers={"Accept": "application/json"}) @@ -143,18 +143,18 @@ class GitHubProvider(OAuthProvider): def is_enabled(self) -> bool: return bool(self.client_id and self.client_secret) - def get_authorize_url(self, state: str) -> str: + def get_authorize_url(self, state: str, redirect_uri: str | None = None) -> str: return ( f"{self.authorize_url}?" + urlencode({ "client_id": self.client_id, - "redirect_uri": self.redirect_uri, + "redirect_uri": redirect_uri or self.redirect_uri, "scope": "repo,user", "state": state, }) ) - async def exchange_code(self, code: str) -> dict | None: + async def exchange_code(self, code: str, redirect_uri: str | None = None) -> dict | None: async with httpx.AsyncClient(timeout=15) as client: r = await client.post( self.token_url, diff --git a/app/routers/auth.py b/app/routers/auth.py index 2ca238a..67e70f8 100644 --- a/app/routers/auth.py +++ b/app/routers/auth.py @@ -115,7 +115,10 @@ async def login(request: Request, provider: str = Query("gitea")): state = secrets.token_hex(32) request.session["oauth_state"] = state request.session["oauth_provider"] = provider - auth_url = oauth_provider.get_authorize_url(state) + # Dynamic redirect URI based on incoming Host header + host = request.headers.get("host", "localhost:8080") + dynamic_redirect_uri = f"http://{host}/auth/callback" + auth_url = oauth_provider.get_authorize_url(state, redirect_uri=dynamic_redirect_uri) return RedirectResponse(url=auth_url, status_code=302) @@ -237,8 +240,10 @@ async def callback( if not oauth_provider: return HTMLResponse(f"