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.