Repository navigation
✨ feat(hub): add S256 PKCE verifier cookie to the Hub GitHub login - #14701
Conversation
|
@Karibhou this PR is back in your court ( Failing checks:
Reply here or push a new head to this branch (same sign-off); CI and the review swarm re-run automatically and the PR returns to the normal path. Your relay session received this as a |
Open PR overlap checkBase branch: Conflicts are checked with every PR merged onto the current tip of No conflicting open PRs or same-file overlaps were found. |
There was a problem hiding this comment.
Decision needed: @clubanderson should decide whether #14701 lands on v5 as-is, or is rebased onto v6 on top of #14683 (keeping only the PKCE additions) — issue #14633 names v6 as the target and both PRs rewrite the same lines of exchangeGitHubLogin.
The PKCE implementation itself reads correctly against the tree; what needs a maintainer is the base branch and its collision with #14683. Routing rather than confirming because this is the Hub login/credential path.
Verified (head b7acb94, diff vs b352ac8):
src/pkg/hub/oauth.go:612-627— 32-byte verifier → 43-char base64url (RFC 7636 minimum), stored inhive_oauth_pkcewith the sameHttpOnly/Secure/Lax/MaxAgeattributes as the state cookie atoauth.go:583-591;code_challenge/code_challenge_method=S256appended to the authorize URL. Matches #14633's spec line for line.oauth.go:744-747— verifier read and cookie cleared on every callback outcome before any branching, so it is genuinely single-use.oauth.go:821-829fails closed with 400 beforeexchangeGitHubLoginis called.TestPKCE_ReplayedCallbackIsRejectedandTestPKCE_CallbackRejectsBadStateBeforeExchangeAndClearsVerifiercover both orderings.- OIDC branch untouched (
TestPKCE_OIDCAuthorizeHasNoPKCECookie).
Findings:
- Base branch / conflict with #14683 (medium, intent-alignment). Issue #14633 says
**Target branch:** v6; this PR targetsv5. #14683 (basev6, hive-reviewed approve atcf9ec6e) already convertsexchangeGitHubLoginto a form POST and addsredirect_urito the body (oauth.goin #14683:form.Set("redirect_uri", oauthRedirectURI())). This PR rewrites the same function body (oauth.go:971-983) withoutredirect_uri. The two will not merge cleanly in the v5→v6 forward-merge, and whichever resolves second must remember to keep bothredirect_uriandcode_verifier. #14633's own wording — "passcode_verifier… in the form body" — presupposes #14683's form body already exists, i.e. this was scoped to land on top of it. - Empty PR body (low). The body is only the agent footer: no
Closes #14633, so merging will not close the issue, and there is no rationale a maintainer can read. The changelog fragment carries the reference but GitHub does not act on that. Sign-off survives the squashcheck isCANCELLEDon this head — worth a re-run before merge since the lane relies on it.
Open question (not a finding): GitHub's OAuth-App token endpoint historically ignored unknown parameters; if it does not enforce code_verifier for this app type, PKCE is defence-in-depth rather than a hard bind. The change is harmless either way, but the changelog line "cannot be redeemed by anyone without this browser's cookie" overstates it unless someone has confirmed enforcement.
I have not approved, closed, or changed anything.
— hive: agent=reviewer backend=copilot model=claude-fable-5.1 copilot=1.0.88
There was a problem hiding this comment.
Decision needed: @clubanderson should decide whether this PKCE change lands on v5 as-is, given #14683 (v6, already approved) rewrites the same exchangeGitHubLogin hunk as a form POST and also sends redirect_uri, so the v5→v6 forward-merge of this PR will conflict there.
Read src/pkg/hub/oauth.go at this head end to end around startProviderLogin, handleOAuthCallback and exchangeGitHubLogin, plus GitHub's current authorize-endpoint docs. The implementation looks correct to me:
oauth.go:611-624— the verifier cookie copies the state cookie's attributes exactly (Path=/,MaxAge=oauthStateTTL,HttpOnly,Secure,SameSite=Lax, cf.oauth.go:584-595), so it survives the same top-level redirect back from GitHub that the state cookie already depends on; no new deployment constraint.oauth.go:1490-1497/:1506-1522— 32 random bytes → 43-char base64url, which is both the RFC 7636 minimum and the length GitHub's docs require forcode_challenge; GitHub documents S256-only, which is whatpkceS256Challengesends.oauth.go:744-748and:821-829— the cookie is read and cleared on every callback outcome before anything else, and the GitHub branch fails closed with 400 before the code is exchanged or a session minted. The OIDC branch is untouched apart from the harmless cookie clear.oauth.go:971-983— the exchange movesclient_secret/code/code_verifierinto a form body with the rightContent-Type; sameAccept: application/jsonand timeout as before.
Routing to a human rather than calling it done because it is the Hub's login path, from a fork, and because of one overlap that is a sequencing call, not a defect:
- #14683 (base
v6, already hive-approved) rewrites the identicalexchangeGitHubLoginhunk (oauth.go:935-947on its base) as a form POST and additionally setsredirect_uri. This PR (basev5) does the same rewrite minusredirect_uriand pluscode_verifier. They are not duplicates — this one adds the PKCE round-trip #14683 lacks — but whichever lands second will conflict on that hunk at forward-merge, and the two disagree on whetherredirect_uriis sent. One of: land this on v5 and resolve toward the superset (code_verifier+redirect_uri) in the forward-merge, or retarget/rebase it onto v6 on top of #14683.
Open question, not a finding: the changelog says logins started before the upgrade are rejected and must restart — true for ≤15 min of in-flight logins at deploy time (oauthStateTTL); fine if that is accepted.
No other findings from the correctness, security, style or docs perspectives; api-reference.md line citations were refreshed in the diff.
— hive: agent=reviewer-2 backend=copilot model=claude-fable-5.1 copilot=1.0.88
|
Needs a decision, not a merge:
Head: verdict requires_human @b7acb94, 54/54 checks green, DCO yes, soak 0s of 30m0s. How to release it:
Clear the cause and the hive re-evaluates on its next pass; the stage moves to |
|
Needs a decision, not a merge:
Head: verdict requires_human @b7acb94, 54/54 checks green, DCO yes, soak 8m0s of 30m0s. How to release it:
Clear the cause and the hive re-evaluates on its next pass; the stage moves to |
|
/hive decision approve Decision: retarget this PR to Rationale: issue #14633 explicitly says Instructions for the fix agent:
Once those are applied on |
|
Needs a decision, not a merge:
Head: verdict requires_human @2b44f90, 53/53 checks green, DCO yes, soak 0s of 30m0s. How to release it:
Clear the cause and the hive re-evaluates on its next pass; the stage moves to |
|
@Karibhou the hive has adopted this PR ( |
|
Needs a decision, not a merge:
Head: verdict requires_human @2b44f90, 53/53 checks green, DCO yes, soak 30m0s elapsed. How to release it:
Clear the cause and the hive re-evaluates on its next pass; the stage moves to |
|
The hive adopted this PR ( Blocker: lane reason Moving to |
clubanderson
left a comment
There was a problem hiding this comment.
Owner-delegate re-review (follow-up to my 03:33Z changes-requested).
Both asks are addressed at head 2b44f90: the PR is now based on v6 and layers code_verifier onto #14683's existing form-POST exchange (redirect_uri is untouched context, not re-implemented), so the v5→v6 forward-merge hazard is gone. PKCE implementation unchanged and still correct (S256, single-use HttpOnly cookie, fail-closed 400 before exchange). Hub browser login only — no spoke/relay surface touched.
Approving; previous changes-requested is superseded by this review.
|
LGTM label has been added. DetailsGit tree hash: a534a485965edb0969246887a445549bbab708eb |
|
Merged by hive contributor-lane auto-merge: CI green for ≥30m, reviewer verdict not required, head |
Fixes #14633
Add S256 PKCE to the Hub GitHub browser login, retaining the v6 form-based token exchange and redirect URI.
🐝 Hive Agent:
contributor| SHA:4f4006e87— hive: backend=claude model=claude-opus-5-5 claude=2.1.311 (Claude Code)