diff --git a/internal/oauth/token.go b/internal/oauth/token.go index 1c66374a0f4c10611e9395da676fde83565b480f..16c5a750f359250d0b73e4186921c55c95dc6494 100644 --- a/internal/oauth/token.go +++ b/internal/oauth/token.go @@ -19,24 +19,35 @@ type Token struct { } // SetExpiresAt calculates and sets the ExpiresAt field based on the -// current time and ExpiresIn. If ExpiresIn is zero or negative, it -// defaults to 3600 seconds and logs a warning. +// current time and ExpiresIn. If ExpiresIn is zero or negative and +// ExpiresAt is already set (e.g. from the provider response), it is +// left unchanged. If neither is usable, ExpiresAt is set to zero so +// IsExpired treats the token as immediately expired, forcing a refresh +// rather than guessing a lifetime. func (t *Token) SetExpiresAt() { - if t.ExpiresIn <= 0 { - slog.Warn("OAuth token has invalid expires_in, defaulting to 3600s", "expires_in", t.ExpiresIn) - t.ExpiresIn = 3600 + if t.ExpiresIn > 0 { + t.ExpiresAt = time.Now().Add(time.Duration(t.ExpiresIn) * time.Second).Unix() + return } - t.ExpiresAt = time.Now().Add(time.Duration(t.ExpiresIn) * time.Second).Unix() + // ExpiresIn is missing or invalid. If ExpiresAt was already + // populated by the provider (some return exp directly), trust it. + if t.ExpiresAt > 0 { + slog.Warn("OAuth token has invalid expires_in but valid expires_at, using expires_at", + "expires_in", t.ExpiresIn, "expires_at", t.ExpiresAt) + return + } + // Neither field is usable. Mark as expired so the caller is forced + // to refresh rather than operating with a fabricated lifetime. + slog.Warn("OAuth token has no valid expiry information, marking as expired", + "expires_in", t.ExpiresIn, "expires_at", t.ExpiresAt) + t.ExpiresAt = 0 } // IsExpired checks if the token is expired or about to expire. It // uses a buffer of max(expires_in/10, minRefreshBuffer) seconds to // trigger proactive refresh before the token actually expires. func (t *Token) IsExpired() bool { - buffer := int64(t.ExpiresIn) / 10 - if buffer < minRefreshBuffer { - buffer = minRefreshBuffer - } + buffer := max(int64(t.ExpiresIn)/10, minRefreshBuffer) return time.Now().Unix() >= (t.ExpiresAt - buffer) }