@@ -14,6 +14,7 @@ import (
14
14
"strings"
15
15
"time"
16
16
17
+ "github.com/dustin/go-humanize"
17
18
"golang.org/x/oauth2"
18
19
"golang.org/x/xerrors"
19
20
@@ -29,6 +30,9 @@ import (
29
30
)
30
31
31
32
const (
33
+ // failureReasonLimit is the maximum text length of an error to be cached to the
34
+ // database for a failed refresh token. In rare cases, the error could be a large
35
+ // HTML payload.
32
36
failureReasonLimit = 200
33
37
)
34
38
@@ -137,6 +141,9 @@ func (c *Config) RefreshToken(ctx context.Context, db database.Store, externalAu
137
141
if externalAuthLink .OauthRefreshFailureReason != "" {
138
142
// If the refresh token is invalid, do not try to keep using it. This will
139
143
// prevent spamming the IdP with refresh attempts that will fail.
144
+ //
145
+ // An empty refresh token will cause `TokenSource(...).Token()` to fail
146
+ // without sending a request to the IdP if the token is expired.
140
147
refreshToken = ""
141
148
}
142
149
@@ -173,12 +180,27 @@ func (c *Config) RefreshToken(ctx context.Context, db database.Store, externalAu
173
180
}
174
181
// The refresh token was cleared
175
182
externalAuthLink .OAuthRefreshToken = ""
183
+ externalAuthLink .UpdatedAt = dbtime .Now ()
176
184
}
177
185
178
186
// Unfortunately have to match exactly on the error message string.
179
187
// Improve the error message to account refresh tokens are deleted if
180
188
// invalid on our end.
189
+ //
190
+ // This error messages comes from the oauth2 package on our client side.
191
+ // So this check is not against a server generated error message.
181
192
if err .Error () == "oauth2: token expired and refresh token is not set" {
193
+ if externalAuthLink .OauthRefreshFailureReason != "" {
194
+ // A cached refresh failure error exists. So the refresh token was set, but was invalid.
195
+ // Return this cached error for the original refresh attempt. This token will never again be valid.
196
+ return externalAuthLink , InvalidTokenError (fmt .Sprintf ("token expired and refreshing failed %s with: %s" ,
197
+ // Do not return the exact time, because then we have to know what timezone the
198
+ // user is in. This approximate time is good enough.
199
+ humanize .Time (externalAuthLink .UpdatedAt ),
200
+ externalAuthLink .OauthRefreshFailureReason ,
201
+ ))
202
+ }
203
+
182
204
return externalAuthLink , InvalidTokenError ("token expired, refreshing is either disabled or refreshing failed and will not be retried" )
183
205
}
184
206
0 commit comments