FJ-39: authenticate AGit https push with the stored keys.json token #48

Merged
David merged 1 commit from david/feat/agit-keys-token-FJ-39 into main 2026-06-08 10:55:39 +02:00 AGit
Owner

Implements FJ-39. Depends on FJ-38 (TLS), which shipped in v0.8.0.

Problem

fj keeps two transports: the REST API client (built from the keys.json token) and the libgit2 git transport used only by the AGit path. The AGit fetch/push authenticated solely through auth-git2 (ssh-agent, ssh keys, credential helpers), which never reads keys.json or ~/.netrc. So fj pr create -aA over https failed with "all authentication attempts failed" on a host where none of those were configured, even though fj was fully authenticated for that host's API. This blocked the claude-run native YouTrack finalize, which calls fj pr create -aA.

Change

  • crates/fj/src/prs.rs: PrCommand::run resolves the host's stored (username, token) via KeyInfo::get_login (after get_api, so any OAuth refresh is reflected) and threads it into create_pr. The AGit fetch/push credential callbacks now offer that token as HTTP basic auth for https:// remotes via a new agit_credentials wrapper, falling back to auth-git2 for ssh remotes, unknown hosts, or when no token is stored.
  • The token is offered once per callback, so a rejected token cannot spin libgit2's credential retry forever (unit-tested).
  • A one-line notice (msg-pr-create-agit_token_auth, en-US + de-DE) names the host when the stored token is used, so the credential source is transparent. Consent is the existing fj auth add-key grant; no prompt is introduced, so the path works headless.
  • No ~/.netrc parsing added.

Verification

This PR was opened with the fixed local binary via fj pr create --agit over the https remote, with no credential helper, no ssh key applicable to https, and ~/.netrc (which libgit2 ignores) present. The push authenticated purely with the keys.json token, exercising acceptance criteria 1 and 2 end to end. Unit tests cover the offer-once / fallback / non-plaintext-skip semantics.

#FJ-39

Implements FJ-39. Depends on FJ-38 (TLS), which shipped in v0.8.0. ## Problem `fj` keeps two transports: the REST API client (built from the keys.json token) and the libgit2 git transport used only by the AGit path. The AGit fetch/push authenticated solely through `auth-git2` (ssh-agent, ssh keys, credential helpers), which never reads keys.json or `~/.netrc`. So `fj pr create -aA` over https failed with "all authentication attempts failed" on a host where none of those were configured, even though fj was fully authenticated for that host's API. This blocked the claude-run native YouTrack finalize, which calls `fj pr create -aA`. ## Change - `crates/fj/src/prs.rs`: `PrCommand::run` resolves the host's stored `(username, token)` via `KeyInfo::get_login` (after `get_api`, so any OAuth refresh is reflected) and threads it into `create_pr`. The AGit fetch/push credential callbacks now offer that token as HTTP basic auth for `https://` remotes via a new `agit_credentials` wrapper, falling back to `auth-git2` for ssh remotes, unknown hosts, or when no token is stored. - The token is offered once per callback, so a rejected token cannot spin libgit2's credential retry forever (unit-tested). - A one-line notice (`msg-pr-create-agit_token_auth`, en-US + de-DE) names the host when the stored token is used, so the credential source is transparent. Consent is the existing `fj auth add-key` grant; no prompt is introduced, so the path works headless. - No `~/.netrc` parsing added. ## Verification This PR was opened with the fixed local binary via `fj pr create --agit` over the https remote, with no credential helper, no ssh key applicable to https, and `~/.netrc` (which libgit2 ignores) present. The push authenticated purely with the keys.json token, exercising acceptance criteria 1 and 2 end to end. Unit tests cover the offer-once / fallback / non-plaintext-skip semantics. #FJ-39
feat(prs): authenticate AGit https push with the stored keys.json token
All checks were successful
Check / fmt + clippy + build + tests (pull_request) Successful in 41s
Create release / Create release from merged PR (pull_request) Has been skipped
eadcd6ea6b
`fj` keeps two transports: the REST API client (built from the keys.json token) and the libgit2 git transport used only by the AGit path. The AGit fetch/push authenticated solely through auth-git2 (ssh-agent, ssh keys, credential helpers), which never reads keys.json or ~/.netrc, so `fj pr create -aA` over https failed with "all authentication attempts failed" on a host where none of those were configured, even though fj was fully authenticated for that host's API.

Hand the token fj already holds for the host to the libgit2 credential callback as HTTP basic auth for https remotes, falling back to auth-git2 for ssh remotes, unknown hosts, or when no token is stored. The token is offered once per callback so a rejected token cannot spin libgit2's credential retry forever. Consent is the existing `fj auth add-key` grant; no prompt is introduced, so the path works headless (the claude-run native finalize case). When the stored token is used, fj prints a one-line notice naming the host so the credential source is visible. No ~/.netrc parsing is added.

#FJ-39

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
David merged commit 9b2b18e5a9 into main 2026-06-08 10:55:39 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
pandoras-box/forgejo-cli!48
No description provided.