mirror of
https://github.com/coder/coder.git
synced 2026-06-02 20:48:20 +00:00
fix(coderd): refresh OAuth token before GitHub API calls in chat diff (#22415)
## Problem `resolveChatGitHubAccessToken` reads the `OAuthAccessToken` directly from the database without refreshing it. When the token expires, GitHub returns "bad credentials" and the chat diff features break. ## Fix Call `config.RefreshToken()` before returning the token — the same code path used by `provisionerdserver` when handing tokens to provisioners. - Builds a map of provider ID → `*externalauth.Config` during the existing config iteration - After fetching the `ExternalAuthLink` from the DB, calls `cfg.RefreshToken()` if a matching config exists - On refresh failure, falls through to the existing token (GitHub tokens without expiry still work) with a debug log
This commit is contained in:
@@ -26,6 +26,7 @@ import (
|
||||
"github.com/coder/coder/v2/coderd/database"
|
||||
"github.com/coder/coder/v2/coderd/database/db2sdk"
|
||||
"github.com/coder/coder/v2/coderd/database/dbauthz"
|
||||
"github.com/coder/coder/v2/coderd/externalauth"
|
||||
"github.com/coder/coder/v2/coderd/httpapi"
|
||||
"github.com/coder/coder/v2/coderd/httpapi/httperror"
|
||||
"github.com/coder/coder/v2/coderd/httpmw"
|
||||
@@ -1442,6 +1443,9 @@ func (api *API) resolveChatGitHubAccessToken(
|
||||
ctx context.Context,
|
||||
userID uuid.UUID,
|
||||
) string {
|
||||
// Build a map of provider ID -> config so we can refresh tokens
|
||||
// using the same code path as provisionerdserver.
|
||||
ghConfigs := make(map[string]*externalauth.Config)
|
||||
providerIDs := []string{"github"}
|
||||
for _, config := range api.ExternalAuthConfigs {
|
||||
if !strings.EqualFold(
|
||||
@@ -1451,6 +1455,7 @@ func (api *API) resolveChatGitHubAccessToken(
|
||||
continue
|
||||
}
|
||||
providerIDs = append(providerIDs, config.ID)
|
||||
ghConfigs[config.ID] = config
|
||||
}
|
||||
|
||||
seen := map[string]struct{}{}
|
||||
@@ -1471,6 +1476,24 @@ func (api *API) resolveChatGitHubAccessToken(
|
||||
continue
|
||||
}
|
||||
|
||||
// Refresh the token if there is a matching config, mirroring
|
||||
// the same code path used by provisionerdserver when handing
|
||||
// tokens to provisioners.
|
||||
if cfg, ok := ghConfigs[providerID]; ok {
|
||||
refreshed, refreshErr := cfg.RefreshToken(ctx, api.Database, link)
|
||||
if refreshErr != nil {
|
||||
api.Logger.Debug(ctx, "failed to refresh external auth token for chat diff",
|
||||
slog.F("provider_id", providerID),
|
||||
slog.F("user_id", userID),
|
||||
slog.Error(refreshErr),
|
||||
)
|
||||
// Fall through — the existing token may still work
|
||||
// (e.g. GitHub tokens with no expiry).
|
||||
} else {
|
||||
link = refreshed
|
||||
}
|
||||
}
|
||||
|
||||
token := strings.TrimSpace(link.OAuthAccessToken)
|
||||
if token != "" {
|
||||
return token
|
||||
|
||||
Reference in New Issue
Block a user