Skip to content

Commit 1365a50

Browse files
committed
Update GitLab IDP to use OIDC instead of OAuth2
This change updates the GitLab IDP integration to use OpenID Connect instead of OAuth2. This has the benefit of using a standard that was meant for authentication, which allows us to remove the GitLab specific code from our integration. We can simply reuse the OIDC code with a GitLab specific configuration. From the perspective of the end user, nothing changes. The configuration of the IDP remains exactly the same and the identity/user objects are unchanged. The only difference is that a very recent version of GitLab is required: version 11.1.0 or later. A positive side effect from this change is that it allows us to use the openid scope instead of the api scope. This means that OpenShift only gets limited read access to perform authentication instead of full read/write access to the user's GitLab account. The user may have to reauthenticate OpenShift's OAuth client on first login. In the future this change will make it easy to pull group membership information from GitLab (the tokens already include a groups claim, we simply need to add code in OpenShift that uses it). Trello: https://trello.com/c/DXntmEOV Signed-off-by: Monis Khan <[email protected]>
1 parent 4839513 commit 1365a50

File tree

3 files changed

+41
-135
lines changed

3 files changed

+41
-135
lines changed
+40-106
Original file line numberDiff line numberDiff line change
@@ -1,135 +1,69 @@
11
package gitlab
22

33
import (
4-
"encoding/json"
54
"errors"
6-
"fmt"
7-
"io/ioutil"
85
"net/http"
96
"net/url"
107
"path"
118

12-
"github.com/RangelReale/osincli"
13-
"github.com/golang/glog"
14-
15-
authapi "github.com/openshift/origin/pkg/oauthserver/api"
169
"github.com/openshift/origin/pkg/oauthserver/oauth/external"
10+
"github.com/openshift/origin/pkg/oauthserver/oauth/external/openid"
1711
)
1812

1913
const (
20-
// Uses the GitLab User-API (http://doc.gitlab.com/ce/api/users.html#current-user)
21-
// and OAuth-Provider (http://doc.gitlab.com/ce/integration/oauth_provider.html)
22-
// with default OAuth scope (http://doc.gitlab.com/ce/api/users.html#current-user)
23-
// Requires GitLab 7.7.0 or higher
14+
// https://gitlab.com/help/integration/openid_connect_provider.md
15+
// Uses GitLab OIDC, requires GitLab 11.1.0 or higher
16+
// Earlier versions do not work: https://gitlab.com/gitlab-org/gitlab-ce/issues/47791#note_81269161
2417
gitlabAuthorizePath = "/oauth/authorize"
2518
gitlabTokenPath = "/oauth/token"
26-
gitlabUserAPIPath = "/api/v3/user"
27-
gitlabOAuthScope = "api"
19+
gitlabUserInfoPath = "/oauth/userinfo"
20+
21+
// https://gitlab.com/gitlab-org/gitlab-ce/blob/master/config/locales/doorkeeper.en.yml
22+
// Authenticate using OpenID Connect
23+
// The ability to authenticate using GitLab, and read-only access to the user's profile information and group memberships
24+
gitlabOIDCScope = "openid"
25+
26+
// An opaque token that uniquely identifies the user
27+
// Along with providerName, builds the identity object's Name field (see Identity.ProviderUserName)
28+
gitlabIDClaim = "sub"
29+
// The user's GitLab username
30+
// Used as the Name field of the user object (stored in Identity.Extra, see IdentityPreferredUsernameKey)
31+
gitlabPreferredUsernameClaim = "nickname"
32+
// The user's public email address
33+
// The value can optionally be used during manual provisioning (stored in Identity.Extra, see IdentityEmailKey)
34+
gitlabEmailClaim = "email"
35+
// The user's full name
36+
// Used as the FullName field of the user object (stored in Identity.Extra, see IdentityDisplayNameKey)
37+
gitlabDisplayNameClaim = "name"
2838
)
2939

30-
type provider struct {
31-
providerName string
32-
transport http.RoundTripper
33-
authorizeURL string
34-
tokenURL string
35-
userAPIURL string
36-
clientID string
37-
clientSecret string
38-
}
39-
40-
type gitlabUser struct {
41-
ID uint64
42-
Username string
43-
Email string
44-
Name string
45-
}
46-
47-
func NewProvider(providerName string, transport http.RoundTripper, URL, clientID, clientSecret string) (external.Provider, error) {
40+
func NewProvider(providerName, URL, clientID, clientSecret string, transport http.RoundTripper) (external.Provider, error) {
4841
// Create service URLs
4942
u, err := url.Parse(URL)
5043
if err != nil {
51-
return nil, errors.New("Host URL is invalid")
44+
return nil, errors.New("gitlab host URL is invalid")
5245
}
5346

54-
return &provider{
55-
providerName: providerName,
56-
transport: transport,
57-
authorizeURL: appendPath(*u, gitlabAuthorizePath),
58-
tokenURL: appendPath(*u, gitlabTokenPath),
59-
userAPIURL: appendPath(*u, gitlabUserAPIPath),
60-
clientID: clientID,
61-
clientSecret: clientSecret,
62-
}, nil
63-
}
47+
config := openid.Config{
48+
ClientID: clientID,
49+
ClientSecret: clientSecret,
6450

65-
func appendPath(u url.URL, subpath string) string {
66-
u.Path = path.Join(u.Path, subpath)
67-
return u.String()
68-
}
51+
AuthorizeURL: appendPath(*u, gitlabAuthorizePath),
52+
TokenURL: appendPath(*u, gitlabTokenPath),
53+
UserInfoURL: appendPath(*u, gitlabUserInfoPath),
6954

70-
func (p *provider) GetTransport() (http.RoundTripper, error) {
71-
return p.transport, nil
72-
}
55+
Scopes: []string{gitlabOIDCScope},
7356

74-
// NewConfig implements external/interfaces/Provider.NewConfig
75-
func (p *provider) NewConfig() (*osincli.ClientConfig, error) {
76-
config := &osincli.ClientConfig{
77-
ClientId: p.clientID,
78-
ClientSecret: p.clientSecret,
79-
ErrorsInStatusCode: true,
80-
SendClientSecretInParams: true,
81-
AuthorizeUrl: p.authorizeURL,
82-
TokenUrl: p.tokenURL,
83-
Scope: gitlabOAuthScope,
57+
IDClaims: []string{gitlabIDClaim},
58+
PreferredUsernameClaims: []string{gitlabPreferredUsernameClaim},
59+
EmailClaims: []string{gitlabEmailClaim},
60+
NameClaims: []string{gitlabDisplayNameClaim},
8461
}
85-
return config, nil
86-
}
8762

88-
// AddCustomParameters implements external/interfaces/Provider.AddCustomParameters
89-
func (p *provider) AddCustomParameters(req *osincli.AuthorizeRequest) {
63+
return openid.NewProvider(providerName, transport, config)
9064
}
9165

92-
// GetUserIdentity implements external/interfaces/Provider.GetUserIdentity
93-
func (p *provider) GetUserIdentity(data *osincli.AccessData) (authapi.UserIdentityInfo, bool, error) {
94-
req, _ := http.NewRequest("GET", p.userAPIURL, nil)
95-
req.Header.Set("Authorization", fmt.Sprintf("bearer %s", data.AccessToken))
96-
97-
client := http.DefaultClient
98-
if p.transport != nil {
99-
client = &http.Client{Transport: p.transport}
100-
}
101-
res, err := client.Do(req)
102-
if err != nil {
103-
return nil, false, err
104-
}
105-
defer res.Body.Close()
106-
107-
body, err := ioutil.ReadAll(res.Body)
108-
if err != nil {
109-
return nil, false, err
110-
}
111-
112-
userdata := gitlabUser{}
113-
err = json.Unmarshal(body, &userdata)
114-
if err != nil {
115-
return nil, false, err
116-
}
117-
118-
if userdata.ID == 0 {
119-
return nil, false, errors.New("Could not retrieve GitLab id")
120-
}
121-
122-
identity := authapi.NewDefaultUserIdentityInfo(p.providerName, fmt.Sprintf("%d", userdata.ID))
123-
if len(userdata.Name) > 0 {
124-
identity.Extra[authapi.IdentityDisplayNameKey] = userdata.Name
125-
}
126-
if len(userdata.Username) > 0 {
127-
identity.Extra[authapi.IdentityPreferredUsernameKey] = userdata.Username
128-
}
129-
if len(userdata.Email) > 0 {
130-
identity.Extra[authapi.IdentityEmailKey] = userdata.Email
131-
}
132-
glog.V(4).Infof("Got identity=%#v", identity)
133-
134-
return identity, true, nil
66+
func appendPath(u url.URL, subpath string) string {
67+
u.Path = path.Join(u.Path, subpath)
68+
return u.String()
13569
}

pkg/oauthserver/oauth/external/gitlab/gitlab_test.go

-28
This file was deleted.

pkg/oauthserver/oauthserver/auth.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,7 @@ func (c *OAuthServerConfig) getOAuthProvider(identityProvider configapi.Identity
481481
if err != nil {
482482
return nil, err
483483
}
484-
return gitlab.NewProvider(identityProvider.Name, transport, provider.URL, provider.ClientID, clientSecret)
484+
return gitlab.NewProvider(identityProvider.Name, provider.URL, provider.ClientID, clientSecret, transport)
485485

486486
case (*configapi.GoogleIdentityProvider):
487487
clientSecret, err := configapi.ResolveStringValue(provider.ClientSecret)

0 commit comments

Comments
 (0)