@@ -19,24 +19,35 @@ type Token struct {
21// SetExpiresAt calculates and sets the ExpiresAt field based on the
21// SetExpiresAt calculates and sets the ExpiresAt field based on the
22−// current time and ExpiresIn. If ExpiresIn is zero or negative, it
22+// current time and ExpiresIn. If ExpiresIn is zero or negative and
23−// defaults to 3600 seconds and logs a warning.
23+// ExpiresAt is already set (e.g. from the provider response), it is
24+// left unchanged. If neither is usable, ExpiresAt is set to zero so
25+// IsExpired treats the token as immediately expired, forcing a refresh
26+// rather than guessing a lifetime.
24func (t *Token) SetExpiresAt() {
27func (t *Token) SetExpiresAt() {
25− if t.ExpiresIn <= 0 {
28+ if t.ExpiresIn > 0 {
26− slog.Warn("OAuth token has invalid expires_in, defaulting to 3600s", "expires_in", t.ExpiresIn)
29+ t.ExpiresAt = time.Now().Add(time.Duration(t.ExpiresIn) * time.Second).Unix()
27− t.ExpiresIn = 3600
30+ return
29− t.ExpiresAt = time.Now().Add(time.Duration(t.ExpiresIn) * time.Second).Unix()
32+ // ExpiresIn is missing or invalid. If ExpiresAt was already
33+ // populated by the provider (some return exp directly), trust it.
35+ slog.Warn("OAuth token has invalid expires_in but valid expires_at, using expires_at",
36+ "expires_in", t.ExpiresIn, "expires_at", t.ExpiresAt)
39+ // Neither field is usable. Mark as expired so the caller is forced
40+ // to refresh rather than operating with a fabricated lifetime.
41+ slog.Warn("OAuth token has no valid expiry information, marking as expired",
42+ "expires_in", t.ExpiresIn, "expires_at", t.ExpiresAt)
32// IsExpired checks if the token is expired or about to expire. It
46// IsExpired checks if the token is expired or about to expire. It
33// uses a buffer of max(expires_in/10, minRefreshBuffer) seconds to
47// uses a buffer of max(expires_in/10, minRefreshBuffer) seconds to
34// trigger proactive refresh before the token actually expires.
48// trigger proactive refresh before the token actually expires.
35func (t *Token) IsExpired() bool {
49func (t *Token) IsExpired() bool {
36− buffer := int64(t.ExpiresIn) / 10
50+ buffer := max(int64(t.ExpiresIn)/10, minRefreshBuffer)
37− if buffer < minRefreshBuffer {
38− buffer = minRefreshBuffer
40 return time.Now().Unix() >= (t.ExpiresAt - buffer)
51 return time.Now().Unix() >= (t.ExpiresAt - buffer)