Skip to content

feat(auth): add kbagent auth pat-create/pat-revoke for CI/CD credentials - #561

Draft
Matovidlo wants to merge 1 commit into
mainfrom
martinvasko-kbagent-ci-mfa-token-setup
Draft

feat(auth): add kbagent auth pat-create/pat-revoke for CI/CD credentials#561
Matovidlo wants to merge 1 commit into
mainfrom
martinvasko-kbagent-ci-mfa-token-setup

Conversation

@Matovidlo

@Matovidlo Matovidlo commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds Personal Access Token (PAT) support: kbagent auth pat-create --name NAME [--totp-code CODE] [--read-only] [--ttl-days N] [--project-id ID ...] and kbagent auth pat-revoke PAT_ID [--yes], built on top of the existing browser-login session from DMD-1825 feat(auth): browser login via PKCE + device authorization (programmatic auth) #535.
  • pat-create spends an already-live auth login session's access token to do a TOTP step-up (POST /v1/auth/sudo) and then mint a scoped, long-lived kbc_pat_... token (POST /v1/auth/pat). It never opens a browser and never logs in itself -- it does not relax auth login's "requires a human at a browser" boundary; a live TOTP code is still required (interactive prompt, or --totp-code, and REQUIRED under --json/non-TTY -- there is no unattended path to mint one).
  • --project-id (repeatable) scopes the PAT to an explicit allow-list instead of every accessible project, for a one-project-per-CI-secret setup.
  • The resulting PAT is a drop-in for KBC_TOKEN under KBAGENT_PROJECT_FROM_ENV=1 (or project add --token): make_client_factory now recognizes the kbc_pat_ prefix on a plain static token and routes it to Authorization: Bearer instead of X-StorageApi-Token -- confirmed against the Storage API's own live OpenAPI security schemes that these are genuinely distinct auth mechanisms, not interchangeable encodings of one, before writing any of this.
  • pat-revoke needs no step-up and is idempotent.
  • New error codes: AUTH_SUDO_REQUIRED, AUTH_MFA_INVALID.

Follow-up in #563 (stacked on this branch): a --webauthn step-up alternative to --totp-code, opening a browser for a passkey ceremony instead of a typed code. Split out because it depends on a not-yet-confirmed server-hosted ceremony page/redirect contract that needs live-stack testing before it's ready for review; this PR (TOTP-only) has no such dependency and is ready now.

Why

CI/CD pipelines need a long-lived, non-interactive credential, but the only two options today are a raw Storage API token (no scoping, no easy revoke story tied to a person) or the browser-login session from #535 (explicitly human-only, never usable in CI). The Connection Auth Service already has first-class PAT support (/v1/auth/pat, /v1/auth/sudo) purpose-built for exactly this "authenticate once interactively, mint a durable scoped credential, use it non-interactively forever after" pattern -- this PR wires kbagent up to it. The kbagent-cicd-migration skill (#402) already recommends this command for CI secrets.

Change type

Feature -- new CLI commands + a small credential-dispatch extension. No breaking changes; version bump 0.80.0 -> 0.81.0.

Impact analysis

  • services/base.py: make_client_factory gains one new branch (PAT-prefix detection) before the existing session-sentinel branch; a plain static token or session-sentinel token takes the byte-identical path as before.
  • auth/auth_client.py / auth/models.py / auth/token_provider.py: three new client methods (sudo_totp, create_pat, revoke_pat), three new wire models (SudoResult, PatItem, PatCreateResult), one new lightweight StaticBearerAuth (no refresh/retry -- a PAT doesn't rotate).
  • services/auth_service.py, commands/auth.py, permissions.py: two new commands, classified admin risk (same class as project add's pasted static token / project remove).
  • Fully backwards-compatible; no changes to existing command behavior.

Test plan

  • New unit tests: test_auth_client.py (sudo/create/revoke against a mocked HTTP transport, including the 404 feature-flag mapping and the sudo-not-active 403), test_auth_service.py (sudo-before-create ordering, sudo-failure aborts before create, no-session error, ttl/project-id forwarding), test_cli_auth.py (argument wiring, --json-without---totp-code fails fast, admin permission-class gating, revoke confirm/abort), test_base_service.py (PAT-prefix dispatch to Authorization: Bearer, plain static token unaffected).
  • make check green end-to-end: ruff, ty (0 new diagnostics vs. main), make skill-check/command-sync-check/changelog-check/check-error-codes/check-sentinel-guards/loc-check, full test suite (5436 passed, 11 skipped, 0 failed).

Deployment

Merge & automatic deploy. No migration.

Rollback plan

Revert of this PR.

Adds Personal Access Token (PAT) support on top of the existing browser-login
session (auth login): `auth pat-create` spends an already-live session's
access token to do a TOTP step-up (POST /v1/auth/sudo) then mint a scoped,
long-lived kbc_pat_... token (POST /v1/auth/pat) -- the sanctioned
alternative to pasting a raw Storage token into a CI secret. `auth pat-revoke`
revokes one. Neither opens a browser or logs in itself; minting/revoking a
PAT is an ordinary bearer-authenticated call once a session exists, so this
does not relax auth login's own "requires a human at a browser" boundary --
a live TOTP code is still required and never something an agent may invent.

The resulting PAT is a drop-in for KBC_TOKEN under
KBAGENT_PROJECT_FROM_ENV=1 (or `project add --token`): make_client_factory
now recognizes the kbc_pat_ prefix on a plain static token and routes it to
Authorization: Bearer instead of X-StorageApi-Token, since the Storage API's
own OpenAPI security schemes define those as distinct auth mechanisms, not
interchangeable encodings of one -- confirmed against the live spec before
writing this. A PAT does not rotate like a session does; replace it
(pat-create again) instead of expecting a refresh.

New error codes: AUTH_SUDO_REQUIRED, AUTH_MFA_INVALID.
@Matovidlo

Copy link
Copy Markdown
Contributor Author

@claude review

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds Personal Access Token (PAT) support to kbagent as a CI/CD-friendly credential option, building on the existing browser-login session flow by performing a TOTP sudo step-up and then minting/revoking PATs via the auth service. It also extends client credential dispatch so kbc_pat_... tokens are treated as Bearer auth (not Storage-token header auth), allowing PATs to work as a drop-in for KBC_TOKEN.

Changes:

  • Add new CLI commands kbagent auth pat-create and kbagent auth pat-revoke, including permission classification and human/JSON output handling.
  • Add auth-layer support for sudo step-up + PAT create/revoke (client methods + wire models) and service methods that orchestrate “sudo then mint”.
  • Extend make_client_factory to route kbc_pat_... tokens to Authorization: Bearer, and bump version/docs/changelog accordingly.

Reviewed changes

Copilot reviewed 22 out of 23 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
uv.lock Bumps locked version metadata to 0.81.0.
pyproject.toml Bumps project version to 0.81.0.
plugins/kbagent/.claude-plugin/plugin.json Syncs plugin version to 0.81.0.
.claude-plugin/marketplace.json Syncs marketplace version to 0.81.0.
src/keboola_agent_cli/constants.py Adds PAT token prefix constant and auth endpoint constants.
src/keboola_agent_cli/errors.py Adds new error codes for sudo/PAT flows and categorization.
src/keboola_agent_cli/services/base.py Adds PAT-prefix detection and Bearer-auth dispatch in make_client_factory.
src/keboola_agent_cli/services/auth_service.py Implements service-level create/revoke PAT flows and CLI result dataclasses.
src/keboola_agent_cli/auth/auth_client.py Adds API client methods for sudo_totp, create_pat, and revoke_pat.
src/keboola_agent_cli/auth/models.py Adds wire models for sudo + PAT create responses.
src/keboola_agent_cli/auth/token_provider.py Adds StaticBearerAuth for non-rotating PAT Bearer auth.
src/keboola_agent_cli/commands/auth.py Wires new auth pat-create / auth pat-revoke commands and output formatters.
src/keboola_agent_cli/commands/context.py Updates embedded CLI context/help text to include PAT commands and guidance.
src/keboola_agent_cli/permissions.py Registers PAT commands as admin operations.
src/keboola_agent_cli/changelog.py Adds 0.81.0 changelog entries describing PAT support.
docs/error-codes.md Documents new error codes.
CLAUDE.md Updates the “All CLI Commands” section for the new auth commands and PAT behavior.
plugins/kbagent/skills/kbagent/SKILL.md Updates skill decision table entries for PAT commands.
plugins/kbagent/skills/kbagent/references/commands-reference.md Adds reference documentation for PAT create/revoke commands.
tests/test_cli_auth.py Adds CLI tests for new auth commands, JSON/TTY behavior, and permission gating.
tests/test_auth_client.py Adds unit tests for sudo and PAT client calls.
tests/test_auth_service.py Adds service-layer tests for sudo-before-create ordering and revoke behavior.
tests/test_base_service.py Adds tests ensuring PAT tokens dispatch via Bearer auth and static tokens remain unchanged.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

formatter = get_formatter(ctx)
check_cli_operation(ctx, "auth.pat-create")
if totp_code is None:
if formatter.json_mode or not _is_stdout_tty():
error_code=ErrorCode.INVALID_ARGUMENT,
)
raise typer.Exit(code=2)
totp_code = typer.prompt("Current TOTP code")
@auth_app.command("pat-revoke")
def auth_pat_revoke(
ctx: typer.Context,
pat_id: str = typer.Argument(..., help="PAT id (UUID) to revoke"),
Matovidlo added a commit that referenced this pull request Aug 7, 2026
Depends on #561 (kbagent auth pat-create/pat-revoke, v0.81.0). Updates the
migration skill to recommend minting a scoped Personal Access Token
(kbagent auth pat-create --project-id <id>) for each project's
KBC_TOKEN_<ALIAS> secret instead of pasting a raw Storage token from the
Keboola UI -- a PAT is scoped to one project, has a controllable expiry
(--ttl-days), and revokes independently of the account
(kbagent auth pat-revoke) without touching anything else that account can do.

No change to the generated GitHub Actions YAML itself: a kbc_pat_... value
is a drop-in for KBC_TOKEN under KBAGENT_PROJECT_FROM_ENV=1 (kbagent detects
the prefix and sends it as Authorization: Bearer automatically), so this is
purely a change in how the operator obtains the secret's value. The raw
Storage token path remains documented as the fallback for stacks/accounts
that can't complete auth login + TOTP step-up.
@Matovidlo
Matovidlo force-pushed the martinvasko-kbagent-ci-mfa-token-setup branch from ccd61f3 to 8ab16fc Compare August 7, 2026 10:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants