aws-sso: read the session end from AWS's token cap; log in 30 s after it

Overnight 2026-10-12: in the session's last hour AWS issued tokens capped at
the end (00:18:48 local, 8 h after sign-in); the old 'expiresAt advanced' rule
read their millisecond jitter as 'refreshed'. A refusal at 00:13 led to a
login that inherited the ending session, and the 30-min wait then delayed the
useful login to 00:47.

Now: new full token = refreshed; new token issued for < 55 min = capped, its
expiry is the end (session_end_seen, status 'until HH:MM'), login at end+30 s;
nothing written = refused, login at once. anchor logs token_min/inherited from
the cap, not a clock. 2 min between unprompted logins after a success;
30 min, 1 h, 2 h, 4 h after unanswered ones.
This commit is contained in:
pi@mbp-m1-2020
2026-10-12 07:57:15 +02:00
parent 0ce96fdb8b
commit 4e48456591
4 changed files with 170 additions and 86 deletions
+18 -13
View File
@@ -394,23 +394,28 @@ Status and logs show only the ntfy server and a fingerprint of the topic.
**How it detects the end.** Per AWS ("Understanding authentication sessions"), the
CLI's 1-hour token is refreshed "as long as the underlying user interactive session
remains valid", meaning the browser's AWS sign-in. That sign-in is admin-capped and
its length is not predictable (measured 8 h on 2026-10-10, at most 6 h on 2026-10-11).
When it ends, the next refresh is refused. Inside the SDK's 5-minute pre-expiry
window, a refused refresh is *silent*: the SDK keeps returning the still-valid token.
So the keeper makes that refresh attempt itself as soon as the window opens: it uses
pi's own `@aws-sdk/token-providers` and the same cache file. It then checks whether
`expiresAt` in `~/.aws/sso/cache/<sha1(sso-session)>.json` advanced. If it did not,
the session is over, and the keeper logs in right away while the old token still
covers requests. Nothing is renewed on a clock: a login while the browser sign-in
is still valid completes silently ("…can be used until your session expires") but
inherits its end, so it gains nothing. Tokens are only ever logged as `sha256[:8]`
measured 8 h here (2026-10-10 from the portal cookie; 2026-10-12 from the token cap
below). In the session's last hour AWS still issues new 1-hour tokens but **caps them
at the session's end**; after the end it refuses. Inside the SDK's 5-minute pre-expiry
window both are *silent*: the SDK keeps returning a still-valid token. So the keeper
makes that refresh attempt itself as soon as the window opens, with pi's own
`@aws-sdk/token-providers` and the same cache file
(`~/.aws/sso/cache/<sha1(sso-session)>.json`), and reads the result from the file:
a new full token = alive; a new token issued for under 55 min = **capped**, and its
expiry is the session's end; nothing written = **refused**. On a cap the keeper logs
in 30 s after that end. On a refusal it logs in at once while the old token still
covers requests. Refusals also happened with time left (cause unknown): a probe at
00:13 local on 2026-10-12, 5 min before the end, and a failed request ~2 h before it;
both times a login worked at once. Nothing is renewed on a clock, and never
before a known end: a login while the browser sign-in is valid completes silently
("…can be used until your session expires") but inherits its end, so it gains nothing. Tokens are only ever logged as `sha256[:8]`
fingerprints (`~/.pi/agent/aws-sso/events.jsonl`); the token the SDK returns is
dropped unread.
**Triggers:** a 20 s timer that checks every 60 s inside the refresh window (every
10 min once the token has run out). Unprompted logins back off (retries after 30 min,
1 h, 2 h, then every 4 h), so one waiting for you does not pile up tabs or
pushes overnight. `before_agent_start`
10 min once the token has run out). At least 2 min pass between unprompted logins;
after an unanswered one the keeper retries after 30 min, 1 h, 2 h, then every 4 h, so
a login waiting for you does not pile up tabs or pushes overnight. `before_agent_start`
checks first if the token is in its window and logs in (and waits) if the session
is over. `message_end` is the fallback: if a request still fails on an expired
token, the keeper renews and sends `continue` (once per 10 min), also when you