fix: require a matching Origin on consent and name the binding cookie per flow (SEC-17, SEC-19)

This commit is contained in:
Latte committed 2026-10-14 17:26:52 +02:00
1 parent 8be859243d
commit 782d416e46
5 files changed
+306 -19

No files matched your search

+11 -7
View File
@@ -393,6 +393,7 @@ def test_callback_in_a_browser_that_did_not_start_the_flow_is_refused(
"csrf": match.group(1),
"decision": "approve",
},
headers={"Origin": BASE},
)
state = query_of(approved.headers["location"])["state"]
@@ -440,20 +441,21 @@ def test_consent_post_needs_the_csrf_cookie_token_and_origin() -> None:
request_id, csrf = consent_form(client)
form = {"request": request_id, "csrf": csrf, "decision": "approve"}
cross_site = browser(oauth.app).post("/oauth/consent", data=form) # no Strict cookie
wrong_token = client.post("/oauth/consent", data={**form, "csrf": "nope"})
origin = {"Origin": BASE}
cross_site = browser(oauth.app).post("/oauth/consent", data=form, headers=origin) # no cookie
wrong_token = client.post("/oauth/consent", data={**form, "csrf": "nope"}, headers=origin)
bad_origin = client.post(
"/oauth/consent", data=form, headers={"Origin": "https://evil.example"}
)
unknown = client.post("/oauth/consent", data={**form, "request": "unknown"})
good = client.post("/oauth/consent", data=form, headers={"Origin": BASE})
again = client.post("/oauth/consent", data=form)
unknown = client.post("/oauth/consent", data={**form, "request": "unknown"}, headers=origin)
good = client.post("/oauth/consent", data=form, headers=origin)
again = client.post("/oauth/consent", data=form, headers=origin)
assert [r.status_code for r in (cross_site, wrong_token, bad_origin, unknown)] == [400] * 4
assert good.status_code == 303
assert again.status_code == 400 # single use
binding = good.headers["set-cookie"].lower()
assert "__host-aegis-oauth-flow=" in binding and "samesite=lax" in binding
assert "__host-aegis-oauth-flow-" in binding and "samesite=lax" in binding
def test_unknown_consent_request_page_is_a_400() -> None:
@@ -468,7 +470,9 @@ def test_denying_consent_returns_access_denied_to_the_client() -> None:
request_id, csrf = consent_form(client)
denied = client.post(
"/oauth/consent", data={"request": request_id, "csrf": csrf, "decision": "deny"}
"/oauth/consent",
data={"request": request_id, "csrf": csrf, "decision": "deny"},
headers={"Origin": BASE},
)
assert denied.status_code == 303