Issue
What is the issue?
In callback(), oauthlib MismatchingStateError returns a redirect. The Flask session check does not:
if not session.get("state") or session.get("state") != request.args["state"]:
redirect(url_for("web.login")) # State does not match!
credentials = flow_instance.flow.credentials
# verify_oauth2_token + session["google_id"] still run
This is the cookie binding for “this browser started this login.” fetch_token still requires a valid Google code matching the Flow singleton. A forged state alone does not skip Google.
Expected Behaviour
Mismatched or missing Flask session["state"] redirects to login and does not set google_id / name / email.
Actual Behaviour
After a successful fetch_token, a mismatched Flask state still verifies the ID token and writes the session.
Steps to reproduce
Test client: mock CREFlow.fetch_token success and id_token.verify_oauth2_token. Set session["state"] = "victim-state", request /rest/v1/callback?state=attacker-state. Session still contains google_id and email.
Success criteria
Issue
What is the issue?
In
callback(), oauthlibMismatchingStateErrorreturns a redirect. The Flask session check does not:This is the cookie binding for “this browser started this login.”
fetch_tokenstill requires a valid Google code matching the Flow singleton. A forgedstatealone does not skip Google.Expected Behaviour
Mismatched or missing Flask
session["state"]redirects to login and does not setgoogle_id/name/email.Actual Behaviour
After a successful
fetch_token, a mismatched Flask state still verifies the ID token and writes the session.Steps to reproduce
Test client: mock
CREFlow.fetch_tokensuccess andid_token.verify_oauth2_token. Setsession["state"] = "victim-state", request/rest/v1/callback?state=attacker-state. Session still containsgoogle_idandemail.Success criteria
return redirect(...)on Flask state mismatch.google_id/emailin session;verify_oauth2_tokennot used to establish login.make lint/make mypy/make testgreen.