Skip to content

project add / project refresh: created Storage tokens lack canManageTokens, breaking 'config oauth-url' out of the box #260

@padak

Description

@padak

Problem

kbagent config oauth-url requires a master Storage API token (privilege canManageTokens) on the target project. The flow is:

  1. The CLI calls POST /v2/storage/tokens (via KeboolaClient.create_short_lived_token()) to mint a short-lived child token scoped to the OAuth-requiring component.
  2. That child token is embedded into the https://external.keboola.com/oauth/index.html?token=...&sapiUrl=...#/<component-id>/<config-id> URL the user opens in their browser.

POST /v2/storage/tokens requires canManageTokens=true, which only master tokens carry by default.

Tokens created or refreshed by:

  • kbagent project add --token <USER_PROVIDED_TOKEN> — passes through whatever the user supplied. AI agents typically pull a non-master token from the Keboola UI ("Storage Tokens" section), so they end up with isMasterToken: false.
  • kbagent project refresh --project <ALIAS> — calls Manage API to mint a new project token; the default privileges set there does not include canManageTokens. Result: a non-master token, even though kbagent project refresh is the ergonomic happy path our docs recommend.

Either path leaves the project unable to run config oauth-url. The Storage API returns a vague 500 "Application error" instead of a 403, which used to mislead operators into thinking the OAuth wizard itself was broken (PR #255 commit e053a44 adds a pre-flight is_master_token check that surfaces a clear MISSING_MASTER_TOKEN exit-3 error before any HTTP write happens — but that's a workaround, not a fix).

Impact

  • AI agents that follow the documented kbagent project add / project refresh setup cannot run OAuth flows on the project.
  • Any future kbagent command that needs to mint Storage child tokens (sub-token rotation, scoped runner tokens, encrypted-credential vault flows) will hit the same wall.
  • kbagent config oauth-url looks broken from the agent's perspective until they manually swap the token in ~/.kbagent/config.json for a master token from the Keboola UI.

Proposal

Two non-exclusive options, in order of preference:

Option A: project refresh mints a master token by default

kbagent project refresh already talks to the Manage API to create a fresh storage token. The Manage API supports the canManageTokens privilege flag on the new-token request. The default for project refresh should set canManageTokens=true so refreshed tokens are usable by the full kbagent command surface (including config oauth-url).

This is a small, contained change in manage_client.py / services/project_service.py around the refresh path.

Option B: opt-in flag --master on project refresh

For users who want a non-master token (e.g. read-only audit setups), add a --master / --no-master toggle (default --master).

kbagent project refresh --project P                  # current behavior, but master-by-default
kbagent project refresh --project P --no-master      # opt out, get a regular token

Option C: warn at project add time

If we keep accepting any user-supplied token in project add, log a warning when verify_token().isMasterToken is false:

⚠ Token for 'padak' is not a master token. `kbagent config oauth-url` and any
  future child-token-minting command will fail. Run `kbagent project refresh
  --project padak` to mint a master token, or pass --no-warn-non-master to
  silence this.

This is the lowest-impact change but doesn't actually fix the root cause.

References

Acceptance criteria

  • kbagent project refresh --project <X> creates a token where verify_token().isMasterToken == true
  • kbagent config oauth-url --project <X> ... succeeds end-to-end without manual token surgery
  • gotchas.md "config oauth-url requires a master Storage API token" entry is updated to reflect the new default
  • (optional) kbagent project add warns when given a non-master token

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions