Skip to content

fix(mp-client): honor expires_in instead of capping every token at 5 minutes - #77

Merged
chriskehayias merged 1 commit into
mainfrom
fix/mp-client-token-expires-in
Aug 21, 2026
Merged

fix(mp-client): honor expires_in instead of capping every token at 5 minutes#77
chriskehayias merged 1 commit into
mainfrom
fix/mp-client-token-expires-in

Conversation

@chriskehayias

Copy link
Copy Markdown
Contributor

Problem

MinistryPlatformClient.ensureValidToken() set expiresAt = now + 5min for every token, discarding the expires_in value the MP OAuth endpoint returns — while the surrounding comments claimed it was subtracting a safety buffer from the real expiration. Intent and behavior disagreed.

MinistryPlatformProvider is a singleton and ensureValidToken() runs before every service call, so a 1-hour MP token was being thrown away after 5 minutes: roughly 12× more token requests than necessary.

Fix

  • Lifetime is derived from expires_in, minus a 5-minute TOKEN_SAFETY_MARGIN, floored at MIN_TOKEN_LIFETIME (30s) so a pathologically short or negative value can't drive a refresh storm.
  • Missing or non-numeric expires_in falls back to DEFAULT_TOKEN_LIFETIME_SECONDS (3600).
  • TOKEN_LIFE renamed to TOKEN_SAFETY_MARGIN so the constant says what it is.
  • getClientCredentialsToken() now declares a ClientCredentialsToken return type instead of leaking any out of response.json() — previously creds.expires_in was completely unchecked at compile time.

Tests

Two existing tests pinned the wrong behavior — they advanced timers past the 5-minute mark and asserted a refresh, so the flat cap looked deliberate. (The TODO predicted the fix wouldn't break existing tests; it does, which is what made the cap look intentional in the first place.) Both are rewritten against the real boundary, and the Token Lifecycle block now covers:

  • expires_in: 3600 → valid at 54:59, refreshes past 55:00
  • expires_in absent → same 55-minute boundary via the 1-hour default
  • expires_in: 60 → valid at 29s, refreshes past 30s (floor applied, not negative)
  • expires_in: 'not-a-number' → falls back to the default instead of NaN

Assertions are behavioral (fake timers + call counts), matching the file's existing style — no new public surface on the client.

Verified by mutation: restoring the flat 5-minute cap fails all four new tests; dropping just the Math.max floor fails only the clamp test.

Full suite: 582 passed (32 files). npm run lint and tsc --noEmit clean.

Housekeeping

  • .claude/TODO/mp-client-token-lifetime-ignores-expires-in.md deleted
  • .claude/docs/TestCoverage.md §5.7 marked ✅ FIXED

Out of scope, noted for later: ensureValidToken() logs three console.log lines on every MP call, and there's still no in-flight dedup for concurrent first calls (documented as accepted in client.test.ts).

🤖 Generated with Claude Code

…minutes

`ensureValidToken()` set `expiresAt = now + 5min` for every token, discarding
the `expires_in` the OAuth endpoint returned — while the comment claimed it was
subtracting a safety buffer from the real expiration. Since the provider is a
singleton and `ensureValidToken()` runs before every service call, a 1-hour MP
token was thrown away after 5 minutes, roughly 12x more token requests than
necessary.

The lifetime now comes from `expires_in`, minus a 5-minute `TOKEN_SAFETY_MARGIN`,
floored at 30 seconds so a pathologically short or negative value cannot drive a
refresh storm. Missing or non-numeric values fall back to 3600s.
`getClientCredentialsToken()` declares a `ClientCredentialsToken` return type
instead of leaking `any` out of `response.json()`.

Two existing tests pinned the wrong behavior by advancing timers past the
5-minute mark and asserting a refresh; both are rewritten against the real
boundary, and the Token Lifecycle block now covers 3600s -> 55min, absent
`expires_in`, `expires_in: 60` -> 30s floor, and a non-numeric value. Verified by
mutation: restoring the flat cap fails all four, and dropping just the
`Math.max` floor fails the clamp test.

Closes the TODO; TestCoverage.md 5.7 marked fixed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@codecov

codecov Bot commented Aug 21, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@chriskehayias
chriskehayias merged commit 664adff into main Aug 21, 2026
3 checks passed
@chriskehayias
chriskehayias deleted the fix/mp-client-token-expires-in branch August 21, 2026 12:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant