aws-sso: detect the real session end by a refresh attempt, renew before requests fail

Measured: a session lasted <= 6 h (2026-10-11), not the 8 h assumed, so a
clock cannot predict the end. token-providers 3.1127.0 refreshes only inside
a 5-min window and, on refusal, silently returns the still-valid token. The
keeper now makes that attempt itself (pi's own SDK, same cache) when the
window opens, and if expiresAt did not advance, logs in while the old token
still works. Clock-based renewal, signed-in and hours commands removed.
This commit is contained in:
pi@mbp-m1-2020
2026-10-11 22:33:40 +02:00
parent 32a397edb4
commit fdee3e42d6
3 changed files with 168 additions and 112 deletions
+25 -26
View File
@@ -359,44 +359,43 @@ Each extension is a TypeScript module loaded by [jiti](https://github.com/unjs/j
### `aws-sso.ts`
Keeps a Bedrock **AWS SSO** session alive, like Claude Code's `awsAuthRefresh`:
shortly before the IAM Identity Center session ends (or when a request fails on
an expired token) it runs `aws sso login` for you. **You still approve the login.**
Off until you enable it.
when the IAM Identity Center session ends it runs `aws sso login` for you, before a
request fails. **You still approve the login** (or your browser does it silently
while it is signed in). Off until you enable it.
| Command | Effect |
|---|---|
| `/aws-sso on` / `off` | Enable/disable (persisted in `~/.pi/agent/aws-sso/state.json`) |
| `/aws-sso status` | Mode, profile chain, time left, last login |
| `/aws-sso status` | Mode, profile chain, session state, last check, last login |
| `/aws-sso now` | Log in now |
| `/aws-sso mode mac` | Run `aws sso login` on the Docker host over SSH (`~/.ssh-local/config`, host alias `host` or `$PI_AWS_SSO_SSH_HOST`): your own browser opens. Default when the sidecar config exists. |
| `/aws-sso mode device` | Run `aws sso login --use-device-code --no-browser` here and show link + code in pi; approve on any device. For SSH/tmux sessions. |
| `/aws-sso continue on\|off` | After a request failed on an expired session and the renewal succeeded, send `continue` (default on) |
| `/aws-sso signed-in HH:MM` | Correct the estimated start of the browser's sign-in |
| `/aws-sso hours <n>` | Session length if your IdC admin changed it (default 8 h, measured) |
`PI_AWS_SSO_REFRESH=1` / `=0` force it on / off regardless of the saved state.
**When it renews — and why not earlier.** Per AWS ("Understanding authentication
sessions"), the CLI's 1-hour token is refreshed by the SDK "as long as the underlying
user interactive session remains valid" — that is the browser's AWS sign-in,
admin-capped (8 h measured 2026-10-10). A login while it is valid completes
silently ("…can be used until your session expires") but **inherits its end**, so
renewing early gains nothing. The keeper therefore renews **2 minutes after** the
estimated end: the browser must sign in again through the IdP (silent while
"stay signed in" holds) and a fresh session starts, while the CLI's last 1-hour
token still covers the gap. If a request fails on an expired token anyway, it
renews and sends `continue` (once per 10 min; `/aws-sso continue off` to disable).
**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]`
fingerprints (`~/.pi/agent/aws-sso/events.jsonl`); the token the SDK returns is
dropped unread.
The session start is not stored anywhere. The keeper watches the refresh token in
`~/.aws/sso/cache/<sha1(sso-session)>.json`: a new one **after** the end (or after
an observed expiry) starts a new session; a new one **before** the end is a silent
renewal and keeps the old end. `/aws-sso signed-in HH:MM` corrects the estimate.
Tokens are only ever logged as `sha256[:8]` fingerprints (`~/.pi/agent/aws-sso/events.jsonl`).
**Triggers:** a 60 s timer (2 min after the estimated end; one attempt per session,
so an ignored login does not loop), `before_agent_start` (session already expired →
log in and wait before sending the prompt), and `message_end` (a request failed
with an expired-token error → log in, then send `continue`).
**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 happen at most once per
30 min, so a login waiting for you does not pile up tabs. `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).
Only interactive sessions act; print/JSON children (`task`, `fork`) stay inert.
A lock file stops several pi processes from starting several logins.