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

+13 -1
View File
@@ -129,6 +129,16 @@ def same_resource(left: str, right: str) -> bool:
return normalized[0] == normalized[1]
def flow_tag(state: str) -> str:
"""Eight hex characters derived from a Gitea ``state`` (names the browser-binding cookie).
Concurrent sign-ins in one browser each get their own cookie, so a second flow
cannot overwrite the binding of the first. The tag is public (it is a hash
prefix of a value that is in the callback URL anyway) and grants nothing.
"""
return token_hash(state)[:8]
def s256(verifier: str) -> str:
"""PKCE S256 challenge for ``verifier``."""
digest = hashlib.sha256(verifier.encode("ascii")).digest()
@@ -214,6 +224,8 @@ class ConsentDecision:
redirect: str = field(repr=False)
binding_token: str | None = field(default=None, repr=False)
#: Short public tag of the Gitea state, used to name this flow's binding cookie.
flow_tag: str | None = None
class GiteaOAuthProvider(
@@ -438,7 +450,7 @@ class GiteaOAuthProvider(
location = self._upstream.authorize_url(
state=state, code_challenge=s256(pending.code_verifier)
)
return ConsentDecision(redirect=location, binding_token=binding)
return ConsentDecision(redirect=location, binding_token=binding, flow_tag=flow_tag(state))
async def complete_callback(
self, state: str, binding: str, code: str, error: str