Commit 024c042
Merge commit from fork
* Bind OAuth callback to the initiating browser
The embedded authorization server loaded a pending authorization at
/oauth/callback purely by the upstream state parameter. Nothing tied the
record to the browser that called /oauth/authorize, so an attacker could
start a flow for their own client, hand the upstream IdP URL to a victim,
and receive an authorization code minted for the victim's identity
(GHSA-2gjv-f568-6cxp).
/oauth/authorize now sets a per-flow, HttpOnly, SameSite=Lax cookie whose
SHA-256 is stored on the pending record, and the callback completes a leg
only when the calling browser presents a matching cookie. The cookie uses
the __Host- prefix whenever the browser-facing authorize URL is https so
a sibling subdomain cannot plant a matching value. Each multi-upstream
chain leg mints its own binding. A callback without a valid binding is
answered with a plain 400 and consumes the record; it is never redirected
to the client's redirect URI. Records without a binding hash are refused
by storage and rejected at the callback. Config validation warns at
startup when an upstream's redirect_uri host differs from the authorize
host, since a host-only cookie cannot bridge them.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* Bind device-flow login to the submitting browser
The device flow's verification page shares /oauth/callback with the
authorization-code flow and had the same gap: POST /oauth/device stored a
PendingDeviceLogin keyed only by the upstream state, so whoever reached the
callback with that state completed the login. A user_code holder could hand
the upstream URL to someone else and have that person's identity land on the
confirmation page for the holder's device (RFC 8628 Section 5.4).
The submit handler now mints the same browser-binding cookie and stores its
hash on the PendingDeviceLogin. Both the completion path and the
upstream-error path require the cookie; a foreign browser gets 400, the
login is consumed, and the DeviceRequest is left pending rather than
authorized or denied by a browser that did not start the login. Storage
refuses a device login without a binding hash.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* Simplify browser binding helpers
Collapse the per-record verify wrappers into one function that takes the
stored hash, consume the pending record before the binding check so a
single rejection helper serves both flows, and drop the cookie-name method
that only forwarded to the package function. Remove the cookie-clearing
step and the storage-level empty-hash refusal: the record is deleted on
first use so a leftover cookie is inert, and the callback already rejects a
record without a hash, which is where that guarantee is enforced and
tested.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* Require an anti-forgery token on the device form
POST /oauth/device accepted any form submission, so a cross-site page
could auto-submit an attacker's user_code from a victim's browser. That
browser would then be issued the device-login binding cookie and could
complete the upstream callback, which left the binding proving only that
the browser posted the form, not that the user chose to.
GET /oauth/device now sets a random cookie and embeds the same value in a
hidden form_token field, and the submit handler accepts the form only when
the two match in constant time. The cookie has the same shape as the
binding cookie, so a cross-site POST neither carries it nor can plant it.
A rejected submission re-renders the form with a fresh token and an error;
nothing is minted. Drivers that post a user_code without loading the form
must now load it first.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* Advertise device page on the browser-facing host
The device authorization response advertised the verification page from
the issuer while the callback lives on the browser-facing authorize base
URL, so a deployment that sets authorizationEndpointBaseUrl to another
host stranded the binding cookie on the issuer host and every device login
failed at the callback with no diagnostic. The page is now advertised from
the same base URL as /oauth/authorize.
The device form's anti-forgery cookie was rotated on every render, so a
second open form, or a second local server sharing the browser's cookie
jar, invalidated the first. The cookie the browser already holds is now
reused and only minted when absent; a cross-site page can neither read it
nor make the browser send it, so the double-submit check keeps its
strength.
Config validation also warns when an upstream callback is plain http on a
non-loopback host while the authorize URL is https: the binding cookie is
Secure there and browsers will not send it to the callback.
Two tests drive both flows through a real HTTP server with a cookie jar,
issuer on 127.0.0.1 and browser-facing base on localhost, so host-only
cookie delivery is exercised rather than simulated; the device one fails
without the first change above.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>1 parent dfb0713 commit 024c042
34 files changed
Lines changed: 1569 additions & 183 deletions
File tree
- cmd/thv-operator/api/v1beta1
- deploy/charts/operator-crds
- files/crds
- templates
- docs
- arch
- operator
- pkg/authserver
- server
- handlers
- storage
- test
- e2e/thv-operator/virtualmcp
- integration/authserver/helpers
Lines changed: 10 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
944 | 944 | | |
945 | 945 | | |
946 | 946 | | |
| 947 | + | |
| 948 | + | |
| 949 | + | |
| 950 | + | |
947 | 951 | | |
948 | 952 | | |
949 | 953 | | |
| |||
1525 | 1529 | | |
1526 | 1530 | | |
1527 | 1531 | | |
| 1532 | + | |
| 1533 | + | |
| 1534 | + | |
1528 | 1535 | | |
1529 | 1536 | | |
1530 | 1537 | | |
| |||
1689 | 1696 | | |
1690 | 1697 | | |
1691 | 1698 | | |
| 1699 | + | |
| 1700 | + | |
| 1701 | + | |
1692 | 1702 | | |
1693 | 1703 | | |
1694 | 1704 | | |
| |||
Lines changed: 20 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
242 | 242 | | |
243 | 243 | | |
244 | 244 | | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
245 | 249 | | |
246 | 250 | | |
247 | 251 | | |
| |||
1774 | 1778 | | |
1775 | 1779 | | |
1776 | 1780 | | |
| 1781 | + | |
| 1782 | + | |
| 1783 | + | |
1777 | 1784 | | |
1778 | 1785 | | |
1779 | 1786 | | |
| |||
2111 | 2118 | | |
2112 | 2119 | | |
2113 | 2120 | | |
| 2121 | + | |
| 2122 | + | |
| 2123 | + | |
2114 | 2124 | | |
2115 | 2125 | | |
2116 | 2126 | | |
| |||
3077 | 3087 | | |
3078 | 3088 | | |
3079 | 3089 | | |
| 3090 | + | |
| 3091 | + | |
| 3092 | + | |
| 3093 | + | |
3080 | 3094 | | |
3081 | 3095 | | |
3082 | 3096 | | |
| |||
4609 | 4623 | | |
4610 | 4624 | | |
4611 | 4625 | | |
| 4626 | + | |
| 4627 | + | |
| 4628 | + | |
4612 | 4629 | | |
4613 | 4630 | | |
4614 | 4631 | | |
| |||
4946 | 4963 | | |
4947 | 4964 | | |
4948 | 4965 | | |
| 4966 | + | |
| 4967 | + | |
| 4968 | + | |
4949 | 4969 | | |
4950 | 4970 | | |
4951 | 4971 | | |
| |||
Lines changed: 20 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
118 | 118 | | |
119 | 119 | | |
120 | 120 | | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
121 | 125 | | |
122 | 126 | | |
123 | 127 | | |
| |||
1650 | 1654 | | |
1651 | 1655 | | |
1652 | 1656 | | |
| 1657 | + | |
| 1658 | + | |
| 1659 | + | |
1653 | 1660 | | |
1654 | 1661 | | |
1655 | 1662 | | |
| |||
1987 | 1994 | | |
1988 | 1995 | | |
1989 | 1996 | | |
| 1997 | + | |
| 1998 | + | |
| 1999 | + | |
1990 | 2000 | | |
1991 | 2001 | | |
1992 | 2002 | | |
| |||
5150 | 5160 | | |
5151 | 5161 | | |
5152 | 5162 | | |
| 5163 | + | |
| 5164 | + | |
| 5165 | + | |
| 5166 | + | |
5153 | 5167 | | |
5154 | 5168 | | |
5155 | 5169 | | |
| |||
6682 | 6696 | | |
6683 | 6697 | | |
6684 | 6698 | | |
| 6699 | + | |
| 6700 | + | |
| 6701 | + | |
6685 | 6702 | | |
6686 | 6703 | | |
6687 | 6704 | | |
| |||
7019 | 7036 | | |
7020 | 7037 | | |
7021 | 7038 | | |
| 7039 | + | |
| 7040 | + | |
| 7041 | + | |
7022 | 7042 | | |
7023 | 7043 | | |
7024 | 7044 | | |
| |||
Lines changed: 20 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
245 | 245 | | |
246 | 246 | | |
247 | 247 | | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
248 | 252 | | |
249 | 253 | | |
250 | 254 | | |
| |||
1777 | 1781 | | |
1778 | 1782 | | |
1779 | 1783 | | |
| 1784 | + | |
| 1785 | + | |
| 1786 | + | |
1780 | 1787 | | |
1781 | 1788 | | |
1782 | 1789 | | |
| |||
2114 | 2121 | | |
2115 | 2122 | | |
2116 | 2123 | | |
| 2124 | + | |
| 2125 | + | |
| 2126 | + | |
2117 | 2127 | | |
2118 | 2128 | | |
2119 | 2129 | | |
| |||
3080 | 3090 | | |
3081 | 3091 | | |
3082 | 3092 | | |
| 3093 | + | |
| 3094 | + | |
| 3095 | + | |
| 3096 | + | |
3083 | 3097 | | |
3084 | 3098 | | |
3085 | 3099 | | |
| |||
4612 | 4626 | | |
4613 | 4627 | | |
4614 | 4628 | | |
| 4629 | + | |
| 4630 | + | |
| 4631 | + | |
4615 | 4632 | | |
4616 | 4633 | | |
4617 | 4634 | | |
| |||
4949 | 4966 | | |
4950 | 4967 | | |
4951 | 4968 | | |
| 4969 | + | |
| 4970 | + | |
| 4971 | + | |
4952 | 4972 | | |
4953 | 4973 | | |
4954 | 4974 | | |
| |||
0 commit comments