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
✨ feat(auth): proactive token refresh with configurable skew
Add ReuseTokenSourceWithSkew, a drop-in oauth2.ReuseTokenSource wrapper
that refreshes cached tokens when time.Until(exp) <= skew instead of
waiting for expiry to pass. Closes the in-flight 401 window where a
request starts at T-100ms with a token expiring at T and reaches GitHub
already expired.
NewApplicationTokenSource and NewInstallationTokenSource now wrap with
DefaultExpirySkew (30s); tune via WithExpirySkew and
WithInstallationExpirySkew. Zero/negative skew delegates to
oauth2.ReuseTokenSource verbatim for backwards compatibility.
Tests cover the skew window, always-refresh when skew > validity,
zero-skew parity with oauth2.ReuseTokenSource, 100-goroutine concurrent
collapse to a single upstream fetch, seed reuse, and error-not-cached.
Copy file name to clipboardExpand all lines: README.md
+32-1Lines changed: 32 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -47,7 +47,7 @@
47
47
- Sign JWTs with external `crypto.Signer` backends (AWS KMS, GCP KMS, Azure Key Vault, HashiCorp Vault Transit, PKCS#11 HSMs, ssh-agent) so the private key never touches process memory
48
48
- RS256-signed JWTs with proper clock drift protection
49
49
- Support for both legacy App IDs and modern Client IDs (recommended by GitHub)
50
-
- Intelligent token caching with automatic refresh for optimal performance
50
+
- Intelligent token caching with **proactive refresh** — tokens are regenerated 30s before expiry to eliminate in-flight 401s (tunable via `WithExpirySkew` / `WithInstallationExpirySkew`)
51
51
- Clean HTTP clients with connection pooling and no shared state
52
52
53
53
### Requirements
@@ -278,6 +278,37 @@ func main() {
278
278
}
279
279
```
280
280
281
+
### Proactive Token Refresh
282
+
283
+
`oauth2.ReuseTokenSource` only refreshes a cached token *after* its expiry has passed. A request that starts at `T-100ms` with a token expiring at `T` can arrive at GitHub with an already-expired credential and receive a 401 that the caller must manually retry. This is especially painful with short application-token windows (default 10 min, optionally lower).
284
+
285
+
Both `NewApplicationTokenSource` and `NewInstallationTokenSource` wrap the returned source in `ReuseTokenSourceWithSkew`, which refreshes when `time.Until(exp) <= skew`. The default `DefaultExpirySkew` is **30 seconds**, so a default 10-minute application JWT has ~9m30s of effective validity — the tail risk is closed, the overhead is negligible.
286
+
287
+
```go
288
+
// Use a shorter skew (e.g. to maximize validity for very short JWTs).
Passing `WithExpirySkew(0)` (or any non-positive value) disables the skew and falls back to the exact `oauth2.ReuseTokenSource` behavior. For third-party `oauth2.TokenSource` implementations outside this package, `ReuseTokenSourceWithSkew` is exported directly:
For regulated or high-security environments, the private key should never leave its secure boundary. `NewApplicationTokenSourceFromSigner` accepts any `crypto.Signer` whose public key is RSA — AWS KMS, Google Cloud KMS, Azure Key Vault, HashiCorp Vault Transit, PKCS#11 HSMs, and ssh-agent all fit.
0 commit comments