Skip to content
This repository has been archived by the owner on Sep 30, 2024. It is now read-only.

Commit

Permalink
[Backport 5.1] [gitlab] Support custom GitLab SSO sign-on URLs (#55076)
Browse files Browse the repository at this point in the history
Co-authored-by: Petri-Johan Last <[email protected]>
Closes #50879
  • Loading branch information
github-actions[bot] authored Jul 26, 2023
1 parent c8cada7 commit b02d1e4
Show file tree
Hide file tree
Showing 8 changed files with 124 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ All notable changes to Sourcegraph are documented in this file.
-
- `golang.org/x/net/trace` instrumentation, previously available under `/debug/requests` and `/debug/events`, has been removed entirely from core Sourcegraph services. It remains available for Zoekt. [#53795](https://github.com/sourcegraph/sourcegraph/pull/53795)
- Sourcegraph now supports more than one auth provider per URL. [#54289](https://github.com/sourcegraph/sourcegraph/pull/54289)
- GitLab auth providers now support an `ssoURL` option that facilitates scenarios where a GitLab group requires SAML/SSO. [#54957](https://github.com/sourcegraph/sourcegraph/pull/54957)

### Fixed

Expand Down
15 changes: 15 additions & 0 deletions doc/admin/auth/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,21 @@ You can use the following filters to control how users can create accounts and s
}
```

### How to set up GitLab auth provider for use with GitLab group SAML/SSO

GitLab groups can require SAML/SSO sign-in to have access to the group. The regular OAuth sign-in won't work in this case, as users will be redirected to the normal GitLab sign-in page, requesting a username/password. In this scenario, add a `ssoURL` to your GitLab auth provider configuration:

```json
{
"type": "gitlab",
// ...
"ssoURL": "https://gitlab.com/groups/your-group/-/saml/sso?token=xxxxxxxx"
]
}
```

The `token` parameter can be found on the **Settings > SAML SSO** page on GitLab.

## Bitbucket Cloud

[Create a Bitbucket Cloud OAuth consumer](https://support.atlassian.com/bitbucket-cloud/docs/use-oauth-on-bitbucket-cloud/). Set the following values, replacing `sourcegraph.example.com` with the IP or hostname of your
Expand Down
5 changes: 5 additions & 0 deletions enterprise/cmd/frontend/internal/auth/gitlaboauth/BUILD.bazel

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

44 changes: 44 additions & 0 deletions enterprise/cmd/frontend/internal/auth/gitlaboauth/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,50 @@ import (
"github.com/sourcegraph/sourcegraph/lib/errors"
)

// SSOLoginHandler is a custom implementation of github.com/dghubble/gologin/oauth2's LoginHandler method.
// It takes an extra ssoAuthURL parameter, and adds the original authURL as a redirect parameter to that
// URL.
//
// This is used in cases where customers use SAML/SSO on their GitLab configurations. The default
// way GitLab handles redirects for groups that require SSO sign-on does not work, and users
// need to sign into GitLab outside of Sourcegraph, and can only then come back and use OAuth.
//
// This implementaion allows users to be directed to their GitLab SSO sign-in page, and then
// the redirect query parameter will redirect them to the OAuth sign-in flow that Sourcegraph
// requires.
func SSOLoginHandler(config *oauth2.Config, failure http.Handler, ssoAuthURL string) http.Handler {
if failure == nil {
failure = gologin.DefaultFailureHandler
}
fn := func(w http.ResponseWriter, req *http.Request) {
ctx := req.Context()
state, err := oauth2Login.StateFromContext(ctx)
if err != nil {
ctx = gologin.WithError(ctx, err)
failure.ServeHTTP(w, req.WithContext(ctx))
return
}
authURL, err := url.Parse(config.AuthCodeURL(state))
if err != nil {
ctx = gologin.WithError(ctx, err)
failure.ServeHTTP(w, req.WithContext(ctx))
return
}

ssoAuthURL, err := url.Parse(ssoAuthURL)
if err != nil {
ctx = gologin.WithError(ctx, err)
failure.ServeHTTP(w, req.WithContext(ctx))
return
}
queryParams := ssoAuthURL.Query()
queryParams.Add("redirect", authURL.Path+"?"+authURL.RawQuery)
ssoAuthURL.RawQuery = queryParams.Encode()
http.Redirect(w, req, ssoAuthURL.String(), http.StatusFound)
}
return http.HandlerFunc(fn)
}

func LoginHandler(config *oauth2.Config, failure http.Handler) http.Handler {
return oauth2Login.LoginHandler(config, failure)
}
Expand Down
46 changes: 46 additions & 0 deletions enterprise/cmd/frontend/internal/auth/gitlaboauth/login_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package gitlaboauth

import (
"context"
"net/http"
"net/http/httptest"
"net/url"
"testing"

oauth2Login "github.com/dghubble/gologin/oauth2"
"github.com/dghubble/gologin/testutils"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/oauth2"
)

func TestSSOLoginHandler(t *testing.T) {
expectedState := "state_val"
ssoURL := "https://api.example.com/-/saml/sso?token=1234"
expectedRedirectURL := "/authorize?client_id=client_id&redirect_uri=redirect_url&response_type=code&state=state_val"
config := &oauth2.Config{
ClientID: "client_id",
ClientSecret: "client_secret",
RedirectURL: "redirect_url",
Endpoint: oauth2.Endpoint{
AuthURL: "https://api.example.com/authorize",
},
}
failure := testutils.AssertFailureNotCalled(t)

// SSOLoginHandler assert that:
// - redirects to the SSO URL, with a redirect to the authURL
// - redirect status code is 302
// - redirect url is the OAuth2 Config RedirectURL with the ClientID and ctx state
loginHandler := SSOLoginHandler(config, failure, ssoURL)
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/", nil)
ctx := oauth2Login.WithState(context.Background(), expectedState)
loginHandler.ServeHTTP(w, req.WithContext(ctx))
assert.Equal(t, http.StatusFound, w.Code)
locationURL, err := url.Parse(w.HeaderMap.Get("Location"))
require.NoError(t, err)
locationRedirectURL, err := url.QueryUnescape(locationURL.Query().Get("redirect"))
require.NoError(t, err)
assert.Equal(t, expectedRedirectURL, locationRedirectURL)
}
6 changes: 6 additions & 0 deletions enterprise/cmd/frontend/internal/auth/gitlaboauth/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@ func parseProvider(logger log.Logger, db database.DB, callbackURL string, p *sch
ServiceID: codeHost.ServiceID,
ServiceType: codeHost.ServiceType,
Login: func(oauth2Cfg oauth2.Config) http.Handler {
// If p.SsoURL is set, we want to use our own SSOLoginHandler
// that takes care of GitLab SSO sign-in redirects.
if p.SsoURL != "" {
return SSOLoginHandler(&oauth2Cfg, nil, p.SsoURL)
}
// Otherwise use the normal LoginHandler
return LoginHandler(&oauth2Cfg, nil)
},
Callback: func(oauth2Cfg oauth2.Config) http.Handler {
Expand Down
2 changes: 2 additions & 0 deletions schema/schema.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions schema/site.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -2946,6 +2946,11 @@
"description": "URL of the GitLab instance, such as https://gitlab.com or https://gitlab.example.com.",
"default": "https://gitlab.com/"
},
"ssoURL": {
"type": "string",
"description": "An alternate sign-in URL used to ease SSO sign-in flows, such as https://gitlab.com/groups/your-group/saml/sso?token=xxxxxx",
"default": ""
},
"clientID": {
"type": "string",
"description": "The Client ID of the GitLab OAuth app, accessible from https://gitlab.com/oauth/applications (or the same path on your private GitLab instance)."
Expand Down

0 comments on commit b02d1e4

Please sign in to comment.