aws-samples / aws-samples/sample-autonomous-cloud-coding-agents

feat(observability): Linear OAuth token-lineage logging for revocation investigation

Abierto
#807 1 comentario 0 reacciones 1 asignado Reclamado por @isadeks Ver en GitHub
approved enhancement P1 security
Lenguaje dominante
TypeScript
Estrellas
143
Forks
46
Merge medio
3 d 10 h
PR fusionados (30 d)
24

Descripción

## Summary

Add resolver-local **diagnostic logging** to the Linear OAuth refresh path
(`cdk/src/handlers/shared/linear-oauth-resolver.ts`) so we can investigate a recurring
per-workspace grant revocation. Additive observability only — **no behavior change** to the refresh
or persist logic.

## Motivation

One Linear workspace's OAuth grant is being revoked ~25h after each (re-)onboard: the platform's
refresh returns `invalid_grant / "Refresh token revoked"`. A second workspace on the identical code
path is durable, so the refresh code itself is sound (verified: the rotated `refresh_token` is
persisted on success). The cause is upstream/app-side, but today's logs can't reconstruct the
token's lifecycle to prove *what* Linear is rejecting — we can't distinguish "a spent/rotated token
was replayed" from "a live grant was killed server-side," nor correlate a failure to the token we
last persisted, nor see the token's age at death.

## Proposed logging (resolver-local, one file)

Threaded through `refreshLinearToken` / `tryRefreshOnce` / the persist path:

1. **Refresh-token fingerprint** — `sha256(refresh_token).slice(0,12)`, logged on every refresh
outcome. Identifies *which* token across events; the raw token is **never** logged.
2. **Token age** — `token_age_h` (now − `installed_at`) and `since_last_refresh_h`
(now − `updated_at`) on each outcome. Both fields already exist on the stored bundle; no schema
change.
3. **Rotation trail** — on successful persist, log `rotated → `, so the next
failure can be correlated to the token we just wrote (server-side kill vs stale/race).
4. **Revocation forensics line** — at "permanently rejected," one structured line:
`{workspace_id, refresh_token_fp, token_age_h, since_last_refresh_h, installed_at, updated_at}`.

## Non-goals

- No change to refresh/persist behavior, cache, or the `invalid_grant` re-read/race handling.
- No trigger/caller threading into other handlers (kept resolver-local to bound blast radius on a
security-sensitive credential file; can follow up if the fingerprint+age data proves insufficient).
- Never log token material — fingerprints (truncated sha256) only.

## Acceptance criteria

- [ ] Every refresh outcome (success, `invalid_grant`, failure, permanently-rejected) logs the
refresh-token fingerprint + `token_age_h` + `since_last_refresh_h`.
- [ ] Successful persist logs the old→new fingerprint rotation.
- [ ] No raw token/secret value appears in any log (verified against the masking gate).
- [ ] No behavior change: existing resolver tests pass unchanged; new tests assert the fingerprint
is a truncated hash and never the raw token.

## Security / priority

Diagnostic-only, additive; touches a credential path so reviewed accordingly (masking gate). Suggest
**P1** — it unblocks an active, recurring auth outage investigation.

Guía de contribución

Abrir la guía de contribución

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.