Skip to content

Commit 4b9621f

Browse files
authored
fix(coderd): don't hang on first gitauth clone (#7331)
Previously, the `coder git ssh` command would hang on the API, which was endlessly polling the database for oauth tokens that expire in the future. Some oAuth implementations (including GitHub by default) will not give back a token expiry date, and the absence of such a date was represented as a zero data in the database as opposed to a null value. Follow-up calls to `git clone` would succeed because this hot path doesn't check expiry, perhaps originally by mistake. In addition to fixing the zero date issue, this PR removes all gitauth PubSub, which added too much complexity when the polling interval is 1 second.
1 parent 5582498 commit 4b9621f

File tree

1 file changed

+9
-38
lines changed

1 file changed

+9
-38
lines changed

coderd/workspaceagents.go

+9-38
Original file line numberDiff line numberDiff line change
@@ -1737,40 +1737,15 @@ func (api *API) workspaceAgentsGitAuth(rw http.ResponseWriter, r *http.Request)
17371737
}
17381738

17391739
if listen {
1740-
// If listening we await a new token...
1741-
authChan := make(chan struct{}, 1)
1742-
cancelFunc, err := api.Pubsub.Subscribe("gitauth", func(ctx context.Context, message []byte) {
1743-
ids := strings.Split(string(message), "|")
1744-
if len(ids) != 2 {
1745-
return
1746-
}
1747-
if ids[0] != gitAuthConfig.ID {
1748-
return
1749-
}
1750-
if ids[1] != workspace.OwnerID.String() {
1751-
return
1752-
}
1753-
select {
1754-
case authChan <- struct{}{}:
1755-
default:
1756-
}
1757-
})
1758-
if err != nil {
1759-
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
1760-
Message: "Failed to listen for git auth token.",
1761-
Detail: err.Error(),
1762-
})
1763-
return
1764-
}
1765-
defer cancelFunc()
1740+
// Since we're ticking frequently and this sign-in operation is rare,
1741+
// we are OK with polling to avoid the complexity of pubsub.
17661742
ticker := time.NewTicker(time.Second)
17671743
defer ticker.Stop()
17681744
for {
17691745
select {
17701746
case <-ctx.Done():
17711747
return
17721748
case <-ticker.C:
1773-
case <-authChan:
17741749
}
17751750
gitAuthLink, err := api.Database.GetGitAuthLink(ctx, database.GetGitAuthLinkParams{
17761751
ProviderID: gitAuthConfig.ID,
@@ -1786,7 +1761,12 @@ func (api *API) workspaceAgentsGitAuth(rw http.ResponseWriter, r *http.Request)
17861761
})
17871762
return
17881763
}
1789-
if gitAuthLink.OAuthExpiry.Before(database.Now()) {
1764+
1765+
// Expiry may be unset if the application doesn't configure tokens
1766+
// to expire.
1767+
// See
1768+
// https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-user-access-token-for-a-github-app.
1769+
if gitAuthLink.OAuthExpiry.Before(database.Now()) && !gitAuthLink.OAuthExpiry.IsZero() {
17901770
continue
17911771
}
17921772
if gitAuthConfig.ValidateURL != "" {
@@ -1932,20 +1912,11 @@ func (api *API) gitAuthCallback(gitAuthConfig *gitauth.Config) http.HandlerFunc
19321912
}
19331913
}
19341914

1935-
err = api.Pubsub.Publish("gitauth", []byte(fmt.Sprintf("%s|%s", gitAuthConfig.ID, apiKey.UserID)))
1936-
if err != nil {
1937-
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
1938-
Message: "Failed to publish auth update.",
1939-
Detail: err.Error(),
1940-
})
1941-
return
1942-
}
1943-
19441915
redirect := state.Redirect
19451916
if redirect == "" {
1917+
// This is a nicely rendered screen on the frontend
19461918
redirect = "/gitauth"
19471919
}
1948-
// This is a nicely rendered screen on the frontend
19491920
http.Redirect(rw, r, redirect, http.StatusTemporaryRedirect)
19501921
}
19511922
}

0 commit comments

Comments
 (0)