Enhancement proposal : Read only auth tokens

I’ve been taking a look at the AI integration options and the MCP server has been really useful, but it’s a bit scary that it has the full power of the user’s credentials. As AI is known to “go rogue” we’d like to limit the blast radius of such an event. Using Claude, I’ve been exploring such a control - and we’d like to contribute an option to create read-only auth tokens. The main use is reporting and integration accounts that only need to read tickets, assets and articles, without giving them a credential that can change data. The following has largely been crafted by Claude, overseen and tweaked by me, as it noted that the contribution instructions recommend asking here in rt-devel before crafting a PR.

Proposal (targeting 6.0-trunk):

  • New column AuthTokens.ReadOnly (default 0), set by a checkbox on the Create form. It can’t be changed afterwards (no write access, SetReadOnly denied, like SetOwner).
  • UserForAuthString records the token on the returned RT::CurrentUser. Because the web UI accepts Authorization: token and opens a full session, we’d enforce read-only in the core rather than in REST2 alone, so REST2, REST 1.0 and token-started web sessions all behave the same.
  • Rights: for a read-only session, RT::Principal::HasRight/HasRights only grant rights named See*/Show* (minus ShowSearchBulkUpdate) plus LoadSavedSearch. The filter sits outside $_ACL_CACHE, so the same user’s normal sessions aren’t affected.
  • Writes: RT::Record::Create, Delete and __Set refuse when the record’s CurrentUser is a read-only token session. This catches writes that don’t go through a rights check, such as RecentlyViewedTickets. System-user writes are unaffected.

Questions for the core team:

  1. Is enforcing at the model layer acceptable, or would you rather limit read-only tokens to REST2 and refuse them for web login?
  2. Is a naming-convention rule (See*/Show*) the right way to decide read rights, or would you prefer an explicit list, or a flag on AddRight?
  3. RT::Shortener rows are created when a web search runs. Should Shortener be exempt from the write guard as a cache, or should it fail and fall back to unshortened URLs?
  4. Method names on RT::CurrentUser: RT::User already has an AuthToken column, so we’d avoid that name. Any preference?
  5. Is branching 6.0-trunk with the upgrade step in etc/upgrade/6.0.4, OK?

We’ll include API, REST2 and web tests, plus POD and UPGRADING notes.

If this looks like a useful addition, I’d appreciate guidance on the questions above and then I can get on and create the PR.

Many thanks,

Simon.