Task: Issue tokens with their permissions

Table of Contents

This page documents a task in the Give a person's token the permissions they hold story. It captures the goal, current status, acceptance, and any notes or results.

1. Goal

Every token a person holds carries the permissions their roles grant, read from the database, and a role change reaches the next token, as the story's decisions 1 to 3 say.

2. Status

Field Value
State DONE
Parent story Give a person's token the permissions they hold
Now Nothing.
Waiting on Nothing.
Next Nothing.
Last touched 2026-10-03

3. Acceptance

  • Login with one party, party selection, party switching and refresh issue tokens carrying the account's effective permissions.
  • Refresh reads the permissions again, so a granted or revoked role shows in the next token.
  • The party-choice token still carries none.
  • Proved against the running deployment.

4. Plan

(Implementation strategy. Written when work starts; key decisions are distilled into the parent story's * Decisions at close, but the plan itself stays — it is the historical record of what we did.)

5. Measurements

Every number this task claims about a tree, with the command that produced it and the commit it was measured at. A number without both is not a measurement, because a clean-up record describes a tree and trunk moves under a branch: a consumer count, a gate's component count and a suite's size all change with no code changing, and the reader of a bare number cannot tell which tree it came from. Where the raw output matters, put it in an evidence/ file beside the task and name that file in the Command column.

What Value Command Commit
       

6. Notes

7. Test Scenarios

Manual QA scenarios (scaffolded via compass add test_scenario) that verify this task. Link new ones here as they're created; the scenario doc itself links back via its "Verifies task" field.

Scenario State Notes
     

8. PRs

PR Title
#2553 [iam] Issue a person's tokens with their permissions

9. Review

# Comment summary File Decision Notes
R1.1 Refreshing a party-choice token would hand back a full token auth_handler.hpp Accept Real: no signer or verifier sets an audience, so refresh accepted it (probe, before). auth_refresh_refusal now refuses the select_party_only audience; a service token, which names no party, still refreshes. Tests and the probe cover both. The design's claim that other verifiers reject that audience was wrong and is corrected; refusing it in request contexts moves to the next task.
R1.2 A failure inside refresh must still reply auth_handler.hpp Accept Confirmed: the body is inside a try whose catch replies with a failure; a test covers the two throwing cases.
R1.3 The two party handlers repeat the permission read account_operations_handler.hpp Decline Both read in the tenant context they already built; a helper would wrap one call.
R1.4 Use the UUID string generator instead of lexical_cast auth_handler.hpp Accept Done; the include is gone.
R1.5 Format the long lines both handlers Accept git clang-format on this branch's lines only.
R1.7 No automated test messaging_auth_refresh_tests.cpp Accept Five cases: which tokens refresh renews, and the read refusing a token it cannot place.

10. Result

Login with one party, party selection and party switching now read the account's effective permissions with authorization_service in the tenant's context and put them in the token. Refresh reads them again through refreshed_permissions instead of copying the old list; a token whose subject or tenant cannot be read fails the refresh. The party-choice token is unchanged and carries none.

Proof, against the running deployment, with the probes in evidence/, which sign in over NATS the way the BFF does and decode the token:

Token Before After
System administrator, login and refresh [] * and the nine tenant codes
Acme administrator, party choice pending [] []
Acme administrator, after selection, switch and refresh [] *

refresh_rereads.mjs grants Viewer to Acme's administrator and refreshes, and the token gains Viewer's three read codes; it revokes Viewer and refreshes, and they go (evidence/refresh_rereads.out). The build is clean with no warnings, ores.iam.core.tests passes 187 of 187, and the tenant live check passes 15 of

Refresh refuses the party-choice token, which before this change it renewed into a full token (evidence/party_choice_refresh.out). Until the next task, an empty list still passes the token check, so a token issued before this change keeps working until its next refresh fills it.

Emacs 29.3 (Org mode 9.6.15)