CFTL-589 GitHub OAuth as a third git authentication method - #32
Open
Oscar-XXII wants to merge 8 commits into
Open
CFTL-589 GitHub OAuth as a third git authentication method#32Oscar-XXII wants to merge 8 commits into
Oscar-XXII wants to merge 8 commits into
Conversation
Cloning a private repository so far required the customer to create and rotate a credential by hand, and a classic PAT grants read and write across every private repository the user can reach. A GitHub App user access token is limited to Contents: Read-only on the repositories selected when the app is installed, so nothing has to be created or rotated manually. The token arrives outside "parameters", in the authorization section of the configuration, and is handed to git through a GIT_ASKPASS helper. That keeps it out of the command line, out of the cloned repository's .git/config and out of the environment of the executed user script. The authorization section is also stripped from the config.json written for that script. Besides the user's own token it carries the shared application secret of the Keboola GitHub App, which no user code may be able to read. The existing none/pat/ssh branches are untouched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Repository selection happens when the GitHub App is installed, not when the component is authorized, and the two are independent flows on GitHub's side. Authorizing without installing first yields a valid token that can see no repositories, and the job then fails with a bare "repository not found". Spelling the order out makes that failure avoidable rather than merely explainable after the fact. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Typing the repository URL by hand works, but it defers every mistake to the
first job run: a repository the app cannot see fails as "not found" only once
the clone is attempted. Listing what the installation actually exposes moves
that feedback into the configuration form, and an empty list is precisely the
signal that the app was authorized but never installed.
The list comes from /user/installations followed by
/user/installations/{id}/repositories, because a user access token carries no
installation id of its own. Both endpoints are paginated and one user may see
several installations, so the results are aggregated.
The picker writes a clone URL into git.repository. The free-text git.url stays
in place for the other authentication methods, because a schema field cannot be
a dropdown and a text input at the same time; GitConfiguration.repository_url
resolves which of the two applies.
Calls go through urllib to avoid adding an HTTP dependency for two endpoints.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…nnot fill in The free-text url field is hidden for auth: oauth, where the repository is picked from a dropdown into git.repository instead, but it stayed listed in git.required. That leaves an OAuth configuration demanding a field the form gives no way to fill in. Only auth stays required. An empty repository is still caught at runtime, now with wording that fits a dropdown rather than asking for a URL. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…tion The installation dialog defaults to a choice, not to a safe default, and the onboarding text so far only said that repositories are picked during installation. Picking "All repositories" grants Contents: Read-only across the whole account or organisation with a token that does not expire — the same over-scoped long-lived credential that motivated moving away from personal access tokens. Nothing on the Keboola side can narrow it afterwards, so the guidance has to arrive before the user clicks. Also records the app's client ID and the page where a user can review or revoke the access they granted. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The app slug is keboola-custom-python-read; it is derived from the app name and is not the client ID, so it could not be inferred from what the OAuth registration already recorded. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Job 53340777 cloned its repository over OAuth and then failed in `uv sync` with "could not read Username for https://github.com": uv shells out to git to fetch a private dependency, and that git had no credentials at all. The PAT path is unaffected because it leaves a ~/.netrc behind, which uv picks up on its own — but a file in the home directory is also readable by the executed user script, which is why the OAuth path does not write one. The credentials now travel to the dependency installation explicitly, through a new optional env argument on SubprocessRunner.run. The executed script keeps inheriting the untouched process environment, so the token still does not reach it. GitHandler now holds only the git-specific overrides and resolves the full environment when a subprocess is started, rather than snapshotting os.environ in the constructor. The snapshot predates the virtual environment selection, so handing it to `uv sync` would have pointed the install at the wrong environment. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The app is now "Keboola Custom Python", which moves its slug from keboola-custom-python-read to keboola-custom-python. The old slug returns 404 — GitHub does not redirect a renamed app — so the previous link would simply have been dead. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Cloning a private repository has so far required the customer to create and rotate a credential by hand, and a classic PAT grants read and write across every private repository the user can reach. Enterprise security reviews keep flagging it.
This adds a third option: authorize a Keboola-owned GitHub App through the OAuth broker and receive a user access token limited to
Contents: Read-onlyon the repositories chosen at installation time. SSH and PAT are untouched.What changed
oauthvalue ongit.auth. Appended to the existing enum, so the radio order for existing configurations does not move.parameters, in theauthorizationsection. It is handed to git through aGIT_ASKPASShelper, so it never appears in the command line, in the cloned repository's.git/config, or in the environment of the executed user script.authorizationis stripped from theconfig.jsonwritten for the user script. Besides the user's own token it carries#appSecret— the shared application secret of the Keboola GitHub App — which no user code may be able to read. This is the most important change in the PR and it is covered by a test.listRepositoriessync action backed by/user/installations+/user/installations/{id}/repositories, both paginated and aggregated across installations. A user access token carries no installation id of its own, hence the two calls.git.urlstays free text for the other auth methods; OAuth configurations select intogit.repositoryinstead, because a schema field cannot be a dropdown and a text input at once.uv syncshells out to git, which had no credentials — this is what broke the first real test run.SubprocessRunner.rungained an optionalenvand the dependency install now receives the git credentials explicitly. The executed script keeps inheriting the untouched process environment.UserExceptioninstead of a raw git or HTTP error.37 tests, flake8 clean.
Verified on the platform
Job 53340777 in project 4214 (us-east4.gcp), image
CFTL-589-143:That run confirms the broker token, the askpass helper and the clone all work end to end against real GitHub. It then failed in
uv syncon a private dependency — which is the bug fixed in aba710a. A re-run on a build containing that fix is still pending.Not verified
listRepositorieshas only been exercised against mocks. Against thekeboolaorganisation it returns a truncated response; whether that is a response size cap or the pod being killed mid-write is not yet established (running it twice and comparing the cut point settles it).Reviewer notes
_merge_user_parameterschange is the security-critical one.source_git.pylooks larger than it is:self.git_cfg.urlwas replaced by a single resolvedself.repo_url, andself.envbecame agit_envoverlay resolved at call time — the previous snapshot predated the virtual environment selection and would have pointeduv syncat the wrong environment.🤖 Generated with Claude Code