You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tooling.apache.org by ak...@apache.org on 2026/05/15 16:03:14 UTC

(tooling-gofannon) branch main updated: auth: tolerate providers that don't echo state in callback

This is an automated email from the ASF dual-hosted git repository.

andrewmusselman pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tooling-gofannon.git


The following commit(s) were added to refs/heads/main by this push:
     new b43ef85  auth: tolerate providers that don't echo state in callback
b43ef85 is described below

commit b43ef858d89e0f5b57f3f21e3c4d8c76515de890
Author: Andrew Musselman <ak...@apache.org>
AuthorDate: Fri May 15 09:03:04 2026 -0700

    auth: tolerate providers that don't echo state in callback
    
    oauth.apache.org doesn't pass the state parameter back in the OAuth
    callback URL — it stores state server-side and validates it during
    the code exchange. The previous strict check ('state must be in URL
    AND match cookie') broke the ASF flow with 'Invalid state; possible
    CSRF' even on legitimate logins.
    
    Relax to: cookie must be present (proves the browser started the
    flow from this app); if state IS in the URL it must match the cookie
    (covers standard OAuth providers like Google, GitHub, Microsoft).
    For ASF the cookie alone is the CSRF defense; the code itself is
    single-use and bound server-side to the originating session.
---
 webapp/packages/api/user-service/routes_auth.py | 22 ++++++++++++++++++----
 1 file changed, 18 insertions(+), 4 deletions(-)

diff --git a/webapp/packages/api/user-service/routes_auth.py b/webapp/packages/api/user-service/routes_auth.py
index 7e3ba5b..335cee7 100644
--- a/webapp/packages/api/user-service/routes_auth.py
+++ b/webapp/packages/api/user-service/routes_auth.py
@@ -155,10 +155,24 @@ async def login_callback(
     if not provider:
         raise HTTPException(status_code=404, detail=f"Unknown provider: {provider_type}")
 
-    # CSRF check: the state returned by the provider must match the
-    # state we stashed in the cookie at login-init time.
-    if not state or not expected_state or not secrets.compare_digest(state, expected_state):
-        raise HTTPException(status_code=400, detail="Invalid state; possible CSRF")
+    # The state cookie must be present — its existence proves this browser
+    # started the flow from this app.
+    if not expected_state:
+        raise HTTPException(
+            status_code=400,
+            detail="Missing state cookie; possible CSRF",
+        )
+
+    # Some providers (notably ASF OAuth) don't echo state back in the
+    # callback URL. When the provider DOES return it, verify it matches
+    # the cookie. When it doesn't, the cookie alone is the CSRF check —
+    # acceptable for ASF since the code itself is single-use and bound
+    # server-side to the originating session.
+    if state and not secrets.compare_digest(state, expected_state):
+        raise HTTPException(
+            status_code=400,
+            detail="State mismatch; possible CSRF",
+        )
 
     # Exchange code -> UserInfo. Any provider error becomes a 502 here;
     # the client retries from the login page.


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@tooling.apache.org
For additional commands, e-mail: commits-help@tooling.apache.org