Skip to content

Commit 9460b39

Browse files
committed
add comments
1 parent aec8e95 commit 9460b39

File tree

2 files changed

+27
-1
lines changed

2 files changed

+27
-1
lines changed

coderd/database/queries/externalauth.sql

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,18 @@ UPDATE external_auth_links SET
4141
oauth_refresh_token_key_id = $7,
4242
oauth_expiry = $8,
4343
oauth_extra = $9,
44-
-- Only 'UpdateExternalAuthLinkRefreshToken' supports updating the oauth_refresh_failure_reason
44+
-- Only 'UpdateExternalAuthLinkRefreshToken' supports updating the oauth_refresh_failure_reason.
45+
-- Any updates to the external auth link, will be assumed to change the state and clear
46+
-- any cached errors.
4547
oauth_refresh_failure_reason = ''
4648
WHERE provider_id = $1 AND user_id = $2 RETURNING *;
4749

4850
-- name: UpdateExternalAuthLinkRefreshToken :exec
4951
UPDATE
5052
external_auth_links
5153
SET
54+
-- oauth_refresh_failure_reason can be set to cache the failure reason
55+
-- for subsequent refresh attempts.
5256
oauth_refresh_failure_reason = @oauth_refresh_failure_reason,
5357
oauth_refresh_token = @oauth_refresh_token,
5458
updated_at = @updated_at

coderd/externalauth/externalauth.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"strings"
1515
"time"
1616

17+
"github.com/dustin/go-humanize"
1718
"golang.org/x/oauth2"
1819
"golang.org/x/xerrors"
1920

@@ -29,6 +30,9 @@ import (
2930
)
3031

3132
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.
3236
failureReasonLimit = 200
3337
)
3438

@@ -137,6 +141,9 @@ func (c *Config) RefreshToken(ctx context.Context, db database.Store, externalAu
137141
if externalAuthLink.OauthRefreshFailureReason != "" {
138142
// If the refresh token is invalid, do not try to keep using it. This will
139143
// 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.
140147
refreshToken = ""
141148
}
142149

@@ -173,12 +180,27 @@ func (c *Config) RefreshToken(ctx context.Context, db database.Store, externalAu
173180
}
174181
// The refresh token was cleared
175182
externalAuthLink.OAuthRefreshToken = ""
183+
externalAuthLink.UpdatedAt = dbtime.Now()
176184
}
177185

178186
// Unfortunately have to match exactly on the error message string.
179187
// Improve the error message to account refresh tokens are deleted if
180188
// 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.
181192
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+
182204
return externalAuthLink, InvalidTokenError("token expired, refreshing is either disabled or refreshing failed and will not be retried")
183205
}
184206

0 commit comments

Comments
 (0)