diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f09d92..d511165 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,9 +13,13 @@ ## Breaking Changes +- [#1239](https://github.com/oauth2-proxy/oauth2-proxy/pull/1239) GitLab groups sent in the `X-Forwarded-Groups` header + to the upstream server will no longer be prefixed with `group:` + ## Changes since v7.1.3 - [#1337](https://github.com/oauth2-proxy/oauth2-proxy/pull/1337) Changing user field type to text when using htpasswd (@pburgisser) +- [#1239](https://github.com/oauth2-proxy/oauth2-proxy/pull/1239) Base GitLab provider implementation on OIDCProvider (@NickMeves) - [#1276](https://github.com/oauth2-proxy/oauth2-proxy/pull/1276) Update crypto and switched to new github.com/golang-jwt/jwt (@JVecsei) - [#1264](https://github.com/oauth2-proxy/oauth2-proxy/pull/1264) Update go-oidc to v3 (@NickMeves) - [#1233](https://github.com/oauth2-proxy/oauth2-proxy/pull/1233) Extend email-domain validation with sub-domain capability (@morarucostel) diff --git a/providers/gitlab.go b/providers/gitlab.go index 84ceda8..af0bcda 100644 --- a/providers/gitlab.go +++ b/providers/gitlab.go @@ -15,6 +15,7 @@ import ( const ( gitlabProviderName = "GitLab" gitlabDefaultScope = "openid email" + gitlabUserClaim = "nickname" ) // GitLabProvider represents a GitLab based Identity Provider @@ -29,6 +30,7 @@ var _ Provider = (*GitLabProvider)(nil) // NewGitLabProvider initiates a new GitLabProvider func NewGitLabProvider(p *ProviderData) *GitLabProvider { p.ProviderName = gitlabProviderName + p.UserClaim = gitlabUserClaim if p.Scope == "" { p.Scope = gitlabDefaultScope } @@ -119,17 +121,24 @@ func (p *GitLabProvider) EnrichSession(ctx context.Context, s *sessions.SessionS return fmt.Errorf("user email is not verified") } - s.User = userinfo.Username - s.Email = userinfo.Email - s.Groups = append(s.Groups, userinfo.Groups...) + if userinfo.Nickname != "" { + s.User = userinfo.Nickname + } + if userinfo.Email != "" { + s.Email = userinfo.Email + } + if len(userinfo.Groups) > 0 { + s.Groups = userinfo.Groups + } + // Add projects as `project:blah` to s.Groups p.addProjectsToSession(ctx, s) return nil } type gitlabUserinfo struct { - Username string `json:"nickname"` + Nickname string `json:"nickname"` Email string `json:"email"` EmailVerified bool `json:"email_verified"` Groups []string `json:"groups"` diff --git a/providers/provider_data.go b/providers/provider_data.go index ccd6e47..de2aae3 100644 --- a/providers/provider_data.go +++ b/providers/provider_data.go @@ -41,6 +41,7 @@ type ProviderData struct { // Common OIDC options for any OIDC-based providers to consume AllowUnverifiedEmail bool + UserClaim string EmailClaim string GroupsClaim string Verifier *oidc.IDTokenVerifier @@ -156,6 +157,17 @@ func (p *ProviderData) buildSessionFromClaims(idToken *oidc.IDToken) (*sessions. ss.Email = claims.Email ss.Groups = claims.Groups + // Allow specialized providers that embed OIDCProvider to control the User + // claim. Not exposed as a configuration flag to generic OIDC provider + // users (yet). + if p.UserClaim != "" { + user, ok := claims.raw[p.UserClaim].(string) + if !ok { + return nil, fmt.Errorf("unable to extract custom UserClaim (%s)", p.UserClaim) + } + ss.User = user + } + // TODO (@NickMeves) Deprecate for dynamic claim to session mapping if pref, ok := claims.raw["preferred_username"].(string); ok { ss.PreferredUsername = pref diff --git a/providers/provider_data_test.go b/providers/provider_data_test.go index 09a19de..2748416 100644 --- a/providers/provider_data_test.go +++ b/providers/provider_data_test.go @@ -211,6 +211,7 @@ func TestProviderData_buildSessionFromClaims(t *testing.T) { testCases := map[string]struct { IDToken idTokenClaims AllowUnverified bool + UserClaim string EmailClaim string GroupsClaim string ExpectedError error @@ -259,6 +260,27 @@ func TestProviderData_buildSessionFromClaims(t *testing.T) { PreferredUsername: "Complex Claim", }, }, + "User Claim Switched": { + IDToken: defaultIDToken, + AllowUnverified: true, + UserClaim: "phone_number", + EmailClaim: "email", + GroupsClaim: "groups", + ExpectedSession: &sessions.SessionState{ + User: "+4798765432", + Email: "janed@me.com", + Groups: []string{"test:a", "test:b"}, + PreferredUsername: "Jane Dobbs", + }, + }, + "User Claim Invalid": { + IDToken: defaultIDToken, + AllowUnverified: true, + UserClaim: "groups", + EmailClaim: "email", + GroupsClaim: "groups", + ExpectedError: errors.New("unable to extract custom UserClaim (groups)"), + }, "Email Claim Switched": { IDToken: unverifiedIDToken, AllowUnverified: true, @@ -332,6 +354,7 @@ func TestProviderData_buildSessionFromClaims(t *testing.T) { ), } provider.AllowUnverifiedEmail = tc.AllowUnverified + provider.UserClaim = tc.UserClaim provider.EmailClaim = tc.EmailClaim provider.GroupsClaim = tc.GroupsClaim