From b7fe0ab1f7cebbdb8e54f959fcb082efc36b863e Mon Sep 17 00:00:00 2001 From: Hugo Dutka Date: Mon, 3 Mar 2025 20:49:04 +0000 Subject: [PATCH] fix: allow orgs with default github provider (#16755) This PR fixes 2 bugs: ## Problem 1 The server would fail to start when the default github provider was configured and the flag `--oauth2-github-allowed-orgs` was set. The error was ``` error: configure github oauth2: allow everyone and allowed orgs cannot be used together ``` This PR fixes it by enabling "allow everone" with the default provider only if "allowed orgs" isn't set. ## Problem 2 The default github provider uses the device flow to authorize users, and that's handled differently by our web UI than the standard oauth flow. In particular, the web UI only handles JSON responses rather than HTTP redirects. There were 2 code paths that returned redirects, and the PR changes them to return JSON messages instead if the device flow is configured. --- cli/server.go | 4 +++- cli/server_test.go | 11 ++++++++++- coderd/userauth.go | 24 ++++++++++++++++++++++-- 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/cli/server.go b/cli/server.go index 933ab64ab267a..745794a236200 100644 --- a/cli/server.go +++ b/cli/server.go @@ -1911,8 +1911,10 @@ func getGithubOAuth2ConfigParams(ctx context.Context, db database.Store, vals *c } params.clientID = GithubOAuth2DefaultProviderClientID - params.allowEveryone = GithubOAuth2DefaultProviderAllowEveryone params.deviceFlow = GithubOAuth2DefaultProviderDeviceFlow + if len(params.allowOrgs) == 0 { + params.allowEveryone = GithubOAuth2DefaultProviderAllowEveryone + } return ¶ms, nil } diff --git a/cli/server_test.go b/cli/server_test.go index d4031faf94fbe..64ad535ea34f3 100644 --- a/cli/server_test.go +++ b/cli/server_test.go @@ -314,6 +314,7 @@ func TestServer(t *testing.T) { githubDefaultProviderEnabled string githubClientID string githubClientSecret string + allowedOrg string expectGithubEnabled bool expectGithubDefaultProviderConfigured bool createUserPreStart bool @@ -355,7 +356,9 @@ func TestServer(t *testing.T) { if tc.githubDefaultProviderEnabled != "" { args = append(args, fmt.Sprintf("--oauth2-github-default-provider-enable=%s", tc.githubDefaultProviderEnabled)) } - + if tc.allowedOrg != "" { + args = append(args, fmt.Sprintf("--oauth2-github-allowed-orgs=%s", tc.allowedOrg)) + } inv, cfg := clitest.New(t, args...) errChan := make(chan error, 1) go func() { @@ -439,6 +442,12 @@ func TestServer(t *testing.T) { expectGithubEnabled: true, expectGithubDefaultProviderConfigured: false, }, + { + name: "AllowedOrg", + allowedOrg: "coder", + expectGithubEnabled: true, + expectGithubDefaultProviderConfigured: true, + }, } { tc := tc t.Run(tc.name, func(t *testing.T) { diff --git a/coderd/userauth.go b/coderd/userauth.go index d8f52f79d2b60..3c1481b1f9039 100644 --- a/coderd/userauth.go +++ b/coderd/userauth.go @@ -922,7 +922,17 @@ func (api *API) userOAuth2Github(rw http.ResponseWriter, r *http.Request) { } } if len(selectedMemberships) == 0 { - httpmw.CustomRedirectToLogin(rw, r, redirect, "You aren't a member of the authorized Github organizations!", http.StatusUnauthorized) + status := http.StatusUnauthorized + msg := "You aren't a member of the authorized Github organizations!" + if api.GithubOAuth2Config.DeviceFlowEnabled { + // In the device flow, the error is rendered client-side. + httpapi.Write(ctx, rw, status, codersdk.Response{ + Message: "Unauthorized", + Detail: msg, + }) + } else { + httpmw.CustomRedirectToLogin(rw, r, redirect, msg, status) + } return } } @@ -959,7 +969,17 @@ func (api *API) userOAuth2Github(rw http.ResponseWriter, r *http.Request) { } } if allowedTeam == nil { - httpmw.CustomRedirectToLogin(rw, r, redirect, fmt.Sprintf("You aren't a member of an authorized team in the %v Github organization(s)!", organizationNames), http.StatusUnauthorized) + msg := fmt.Sprintf("You aren't a member of an authorized team in the %v Github organization(s)!", organizationNames) + status := http.StatusUnauthorized + if api.GithubOAuth2Config.DeviceFlowEnabled { + // In the device flow, the error is rendered client-side. + httpapi.Write(ctx, rw, status, codersdk.Response{ + Message: "Unauthorized", + Detail: msg, + }) + } else { + httpmw.CustomRedirectToLogin(rw, r, redirect, msg, status) + } return } }