Problem
kbagent config oauth-url requires a master Storage API token (privilege canManageTokens) on the target project. The flow is:
- 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.
- 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
Problem
kbagent config oauth-urlrequires a master Storage API token (privilegecanManageTokens) on the target project. The flow is:POST /v2/storage/tokens(viaKeboolaClient.create_short_lived_token()) to mint a short-lived child token scoped to the OAuth-requiring component.https://external.keboola.com/oauth/index.html?token=...&sapiUrl=...#/<component-id>/<config-id>URL the user opens in their browser.POST /v2/storage/tokensrequirescanManageTokens=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 withisMasterToken: false.kbagent project refresh --project <ALIAS>— calls Manage API to mint a new project token; the default privileges set there does not includecanManageTokens. Result: a non-master token, even thoughkbagent project refreshis 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 commite053a44adds a pre-flightis_master_tokencheck that surfaces a clearMISSING_MASTER_TOKENexit-3 error before any HTTP write happens — but that's a workaround, not a fix).Impact
kbagent project add/project refreshsetup cannot run OAuth flows on the project.kbagent config oauth-urllooks broken from the agent's perspective until they manually swap the token in~/.kbagent/config.jsonfor a master token from the Keboola UI.Proposal
Two non-exclusive options, in order of preference:
Option A:
project refreshmints a master token by defaultkbagent project refreshalready talks to the Manage API to create a fresh storage token. The Manage API supports thecanManageTokensprivilege flag on the new-token request. The default for project refresh should setcanManageTokens=trueso refreshed tokens are usable by the full kbagent command surface (includingconfig oauth-url).This is a small, contained change in
manage_client.py/services/project_service.pyaround the refresh path.Option B: opt-in flag
--masteronproject refreshFor users who want a non-master token (e.g. read-only audit setups), add a
--master / --no-mastertoggle (default--master).Option C: warn at
project addtimeIf we keep accepting any user-supplied token in
project add, log a warning whenverify_token().isMasterTokenisfalse:This is the lowest-impact change but doesn't actually fix the root cause.
References
e053a44— pre-flightis_master_tokencheck (workaround, not fix)Acceptance criteria
kbagent project refresh --project <X>creates a token whereverify_token().isMasterToken == truekbagent config oauth-url --project <X> ...succeeds end-to-end without manual token surgerykbagent project addwarns when given a non-master token