Skip to content

serve: expose auth register-projects over REST (POST /auth/register-projects) #537

Description

@zajca

Context

PR #535 added programmatic browser auth (kbagent auth login|status|logout) and, in 273d02b, kbagent auth register-projects — which registers projects an existing session can access as local aliases with kbc-session://{project_id} sentinel tokens.

None of the auth group is exposed over kbagent serve. For login that is correct and should stay that way: the flow needs a loopback callback or a human typing a device code, which does not fit a stateless REST call.

register-projects is different. With --all / --project-id / --alias ID=ALIAS it is fully non-interactive, does no browser work, and only reads auth.json plus writes config.json. It is the one piece of the group that maps cleanly onto REST.

Why it matters

kbagent http post ... is the documented way for a scheduled agent task to reach the CLI's own functionality without forking a second kbagent process tree. Today an agent running inside kbagent serve that needs a session project registered has no path: it would have to shell out, or hand-write a sentinel token into config.json, which is exactly the footgun the picker was added to remove.

Current state (verified on 273d02b)

  • src/keboola_agent_cli/server/routers/ has no auth.py. There are 30 routers; auth is absent.
  • AuthService is not in ServiceRegistry (server/dependencies.py) — it is only constructed in cli.py:353 and put on the Typer context. So this is not just "add a router"; the service has to be registered first.

Proposed scope

  • POST /auth/register-projects accepting {stack?, all?, project_ids?, aliases?}, returning the existing RegisterProjectsResult shape (status, stack_url, registered_projects[], warnings[]).
  • Add AuthService to ServiceRegistry.
  • GET /auth/projects returning ProjectCandidatesResult (the candidate list with suggested collision-free aliases), so a caller can choose ids before registering rather than guessing.
  • Optionally GET /auth/status, which is read-only and equally REST-shaped.

Explicitly out of scope

  • auth login — requires a human at a browser; must not get an endpoint.
  • auth logout — revokes credentials for the whole machine, not just the caller. Wants its own think about who is allowed to trigger it over HTTP before it gets an endpoint.

Acceptance criteria

  • The interactive picker stays CLI-only; the endpoint is selector-driven and never prompts.
  • No endpoint returns a token value, including the kbc-session:// sentinel. The existing result dataclasses are token-free by construction — keep that property.
  • SESSION_NOT_FOUND maps to a sensible HTTP status rather than a 500.
  • Never overwrites an existing alias; the exists / skipped statuses survive the REST boundary.
  • The --deny-writes / permissions policy is honoured on the serve path too (auth.register-projects is registered as a write operation).
  • Tests + the doc surfaces in CLAUDE.md convention v0.6.0: Branch lifecycle management + security hardening #17.

Note

Not a blocker for #535 — the CLI path is complete and tested. Raised because CONTRIBUTING.md's "Plugin synchronization map" lists the serve REST surface, and this is a deliberate omission rather than an oversight.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions