Skip to content

Personal Access Tokens (PATs) for API & MCP access #357

Description

@okt-limonikas

Summary

Let users issue long-lived API tokens so external clients — Claude Code, Codex and other LLM
agents talking to the Bublik MCP server, plus CI jobs and scripts — can call the Bublik API
on behalf of the user. A request made with a token should be able to do exactly what that
user can do through the web UI: leave a comment on a run, classify results under an issue, and
every other action.

Today the only way in is the short-lived JWT cookie pair set at login, which a headless client
can't realistically use. The MCP server has no authentication and no notion of a calling user,
so it can only ever expose read-only tools.

What we want

  • Users issue named tokens from their profile.
  • Expiry is picked from fixed options: 7 / 30 / 90 days, or never.
  • The token value is shown exactly once, at creation. It can never be retrieved again.
  • One token works for both the REST API and the MCP server.
  • Everything a token does is attributed to its owner.
  • Users see their own tokens — name, prefix, created, expires, last used, status — and can
    revoke them.
  • Admins see all users' tokens and can revoke any of them.

Not in this ticket

  • Per-token scopes or permissions. A token simply inherits its owner's permissions.
  • Service accounts not tied to a human user.
  • Rotating a token in place (rotating means revoke + issue a new one).

Behaviour

Tokens carry a bpat_ prefix so they're recognisable in logs and by secret scanners. The
first few characters stay visible in the UI as a handle, so a user can tell which token a client
is configured with without ever seeing the secret again.

Storage keeps no recoverable copy of the token — there must be no code path, for a user or an
admin, that can display a token after creation.

Revocation takes effect on the very next request. Revoked tokens stay in the list as a record
of who revoked what, rather than disappearing.

Expiry is enforced per request. "Never" really means never.

Last used is recorded on use and shown in the UI at minute granularity.

Deactivating a user stops that user's tokens from working.

Existing browser login is unaffected — cookie sessions keep working exactly as they do now.

User flows

Issuing a token for Claude Code

  1. User opens Profile → Access tokens and clicks "New token".
  2. Names it (code-laptop) and picks an expiry: 7 / 30 / 90 days / Never.
  3. The token appears once, with a copy button and a clear warning that it won't be shown again.
    The dialog also shows how to plug it into an MCP client.
  4. User pastes it into their code config. From then on, agent actions — including
    comments left on runs — show up in Bublik as that user's actions.
  5. Back in the list, the token shows only its prefix, its expiry, and "Never used" until first use.

Revoking a leaked token

  1. User notices a token they no longer recognise, or one they pasted somewhere public.
  2. They see when it was last used, which tells them whether it's actually been abused.
  3. They revoke it; the next request made with it fails.
  4. They issue a replacement and update their client.

Admin cleaning up

  1. An admin opens the admin token list and sees every token across all users, with owner,
    expiry and last-used.
  2. They spot tokens belonging to someone who has left, or ones unused for months.
  3. They revoke them. The record shows which admin did it.
  4. Admins never see token values — that's structurally impossible, and it's the point.

Security requirements

  • Token values never appear in logs, error reports or the eventlog.
  • Expired, revoked and invalid tokens each fail with a distinguishable message, so agents can be
    debugged without guesswork.
  • Cap the number of tokens per user (suggest 20) and rate-limit creation.
  • MCP responses are cached today. Once requests carry an identity, that cache must never serve
    one user's response to another. Treat this as blocking — it's the one place where adding
    authentication could actively leak data.

Acceptance criteria

  • A user can create a token with each expiry option and sees the value exactly once.
  • The token list never shows the value again.
  • A token can perform a write action (e.g. commenting on a run) attributed to its owner.
  • The same token authenticates against MCP; unauthenticated MCP requests are rejected.
  • MCP caching cannot leak one user's response to another.
  • Last-used is recorded and visible.
  • Revoking works immediately; expired tokens are rejected; "never" tokens don't expire.
  • Admins can list and revoke any user's token, and the record shows who revoked it.
  • Non-admins can't see or revoke anyone else's tokens.
  • Deactivating a user stops their tokens working.
  • Existing browser login flows are unchanged.

Open questions

  • Is MCP authentication mandatory, or opt-in via a setting? Mandatory breaks existing
    unauthenticated local setups. Probably required in production, optional locally — needs a call.
  • Should a password reset also revoke tokens? Leaning no: it would silently break users' CI,
    and tokens are independently revocable.
  • Should users get a warning email before a token expires, to avoid silent CI breakage?

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