fix: connexion OAuth Gitea — redirect URI invalide (erreur 'Unregistered Redirect URI')
Le /auth/login construisait le redirect_uri avec le schéma http:// en dur et dupliquait l'expression dans login/callback : toute URL d'accès non enregistrée (https, reverse proxy, hostname, port différent) était rejetée par Gitea avec 'Unregistered Redirect URI' — les 2 liens 'Connect Gitea' et 'Register with Gitea' de Workspaces → Gitea Projects étaient touchés. - nouveau helper get_redirect_uri(request) : override explicite OAUTH_REDIRECT_URI (si défini), sinon schéma depuis X-Forwarded-Proto (fallback request scheme) + hôte depuis X-Forwarded-Host (fallback Host) - redirect_uri stocké en session à l'authorize et réutilisé tel quel dans l'échange de code (plus de dérive entre les deux étapes) - même correctif dans GitHubProvider.exchange_code (ignorait le paramètre) - config : oauth_redirect_uri par défaut vide (dynamique) au lieu de localhost:8080 en dur - .env.example documente OAUTH_REDIRECT_URI - 4 tests de régression (host header, X-Forwarded-Proto/Host, override env, URL d'authorize) — 178/179 OK, l'échec restant (test_views_calendar) est pré-existant et dépend de la date
This commit is contained in:
+27
-8
@@ -13,6 +13,24 @@ from app.config import settings
|
||||
logger = logging.getLogger(__name__)
|
||||
router = APIRouter(tags=["auth"], prefix="/auth")
|
||||
|
||||
|
||||
def get_redirect_uri(request: Request) -> str:
|
||||
"""OAuth redirect URI for this request.
|
||||
|
||||
Explicit `OAUTH_REDIRECT_URI` env override wins (must be registered in the
|
||||
provider's OAuth app). Otherwise it is derived from the request so it always
|
||||
matches the URL the user actually used: scheme from `X-Forwarded-Proto`
|
||||
(reverse proxies) falling back to the request scheme, host from
|
||||
`X-Forwarded-Host` falling back to the `Host` header.
|
||||
"""
|
||||
if settings.oauth_redirect_uri:
|
||||
return settings.oauth_redirect_uri
|
||||
proto = request.headers.get("x-forwarded-proto", "")
|
||||
scheme = proto.split(",")[0].strip() or request.url.scheme or "http"
|
||||
fwd_host = request.headers.get("x-forwarded-host", "")
|
||||
host = fwd_host.split(",")[0].strip() or request.headers.get("host", "localhost:8080")
|
||||
return f"{scheme}://{host}/auth/callback"
|
||||
|
||||
LOCAL_LOGIN_HTML = """<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -144,10 +162,11 @@ async def login(request: Request, provider: str = Query("gitea")):
|
||||
# Encode auth mode in state to survive session loss during OAuth redirect
|
||||
signed_state = f"{state}:{mode}" if mode else state
|
||||
request.session["oauth_mode"] = mode
|
||||
# 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(signed_state, redirect_uri=dynamic_redirect_uri, force_login=(mode == "link"))
|
||||
# Redirect URI derived from the incoming request (scheme-aware); stored in
|
||||
# session so the callback reuses the EXACT same URI for token exchange
|
||||
redirect_uri = get_redirect_uri(request)
|
||||
request.session["oauth_redirect_uri"] = redirect_uri
|
||||
auth_url = oauth_provider.get_authorize_url(signed_state, redirect_uri=redirect_uri, force_login=(mode == "link"))
|
||||
return RedirectResponse(url=auth_url, status_code=302)
|
||||
|
||||
|
||||
@@ -282,10 +301,10 @@ async def callback(
|
||||
if not oauth_provider:
|
||||
return HTMLResponse(f"<h1>Unknown provider: {provider_name}</h1>", status_code=400)
|
||||
|
||||
# Exchange code for token — use dynamic redirect URI matching the authorize step
|
||||
host = request.headers.get("host", "localhost:8080")
|
||||
dynamic_redirect_uri = f"http://{host}/auth/callback"
|
||||
token_data = await oauth_provider.exchange_code(code, redirect_uri=dynamic_redirect_uri)
|
||||
# Exchange code for token — reuse the redirect URI from the authorize step
|
||||
# (stored in session), falling back to deriving it from this request
|
||||
redirect_uri = request.session.get("oauth_redirect_uri") or get_redirect_uri(request)
|
||||
token_data = await oauth_provider.exchange_code(code, redirect_uri=redirect_uri)
|
||||
if not token_data:
|
||||
return HTMLResponse("<h1>Token exchange failed</h1>", status_code=400)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user