You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The three calendar-token providers (Google, Microsoft, CalDAV) plus Zoom each
hand-rolled their own AES-GCM crypto and OAuth-refresh-persistence wrapper —
built at different times, never consolidated even after a shared crypto package
existed. microsoft.go's own doc comment flagged this as intentional-for-now tech
debt ("a future cleanup can extract a shared calendar token store"). This is
that cleanup's first two pieces.
Crypto: verified byte-for-byte identical algorithm across all three providers
(AES-256-GCM, nonce prepended to ciphertext, base64.StdEncoding) before touching
anything — Zoom already correctly delegates to internal/secret; gcal/microsoft/
caldav's `encrypt`/`decrypt` methods (the token/credential storage path) now
delegate to it too, so existing stored tokens keep decrypting unchanged. Their
`encryptEncoding`/`decryptEncoding` (URLEncoding, used only for OAuth CSRF
state) are untouched — secret.Encrypt/Decrypt doesn't cover that encoding.
Verified via each provider's own encrypt/decrypt round-trip tests, which
exercise exactly this path and all still pass.
OAuth refresh persistence: gcal, microsoft, and zoom each defined an identical
struct — wrap an oauth2.TokenSource, compare AccessToken to the last-seen value,
and on change spin up a 10s context and persist the new token, logging on
failure. New internal/oauthstore.SavingTokenSource is the one implementation,
parameterized by a Save closure so each provider still calls its own saveToken
with its own identifiers (gcal/microsoft need calID+accountEmail; microsoft
additionally passes kind="" on refresh to preserve account_kind; zoom needs
neither). Added direct unit tests for the new package (saves-on-first-token,
skips-resave-when-unchanged, resaves-on-refresh, save-error-doesn't-fail-Token,
inner-error-propagates) since it's genuinely new shared code, not just moved.
Connection-loading/upsert (the third, most complex piece the same audit
finding named) is intentionally left for a follow-up — it has more business-
logic subtlety (the "claim destination only if none exists yet" rule) that's
worth its own careful pass rather than bundling into this one.
Verified: go build/vet, full test suite (including all three providers' own
suites) — all pass unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
0 commit comments