9 oidc "github.com/coreos/go-oidc"
10 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
11 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/requests"
15// GitLabProvider represents a GitLab based Identity Provider
16type GitLabProvider struct {
22 Verifier *oidc.IDTokenVerifier
23 AllowUnverifiedEmail bool
26var _ Provider = (*GitLabProvider)(nil)
29 gitlabProviderName = "GitLab"
30 gitlabDefaultScope = "openid email"
33// NewGitLabProvider initiates a new GitLabProvider
34func NewGitLabProvider(p *ProviderData) *GitLabProvider {
35 p.ProviderName = gitlabProviderName
38 p.Scope = gitlabDefaultScope
41 return &GitLabProvider{ProviderData: p}
44// Redeem exchanges the OAuth2 authentication token for an ID token
45func (p *GitLabProvider) Redeem(ctx context.Context, redirectURL, code string) (s *sessions.SessionState, err error) {
46 clientSecret, err := p.GetClientSecret()
53 ClientSecret: clientSecret,
54 Endpoint: oauth2.Endpoint{
55 TokenURL: p.RedeemURL.String(),
57 RedirectURL: redirectURL,
59 token, err := c.Exchange(ctx, code)
61 return nil, fmt.Errorf("token exchange: %v", err)
63 s, err = p.createSessionState(ctx, token)
65 return nil, fmt.Errorf("unable to update session: %v", err)
70// RefreshSessionIfNeeded checks if the session has expired and uses the
71// RefreshToken to fetch a new ID token if required
72func (p *GitLabProvider) RefreshSessionIfNeeded(ctx context.Context, s *sessions.SessionState) (bool, error) {
73 if s == nil || (s.ExpiresOn != nil && s.ExpiresOn.After(time.Now())) || s.RefreshToken == "" {
77 origExpiration := s.ExpiresOn
79 err := p.redeemRefreshToken(ctx, s)
81 return false, fmt.Errorf("unable to redeem refresh token: %v", err)
84 fmt.Printf("refreshed id token %s (expired on %s)\n", s, origExpiration)
88func (p *GitLabProvider) redeemRefreshToken(ctx context.Context, s *sessions.SessionState) (err error) {
89 clientSecret, err := p.GetClientSecret()
96 ClientSecret: clientSecret,
97 Endpoint: oauth2.Endpoint{
98 TokenURL: p.RedeemURL.String(),
102 RefreshToken: s.RefreshToken,
103 Expiry: time.Now().Add(-time.Hour),
105 token, err := c.TokenSource(ctx, t).Token()
107 return fmt.Errorf("failed to get token: %v", err)
109 newSession, err := p.createSessionState(ctx, token)
111 return fmt.Errorf("unable to update session: %v", err)
113 s.AccessToken = newSession.AccessToken
114 s.IDToken = newSession.IDToken
115 s.RefreshToken = newSession.RefreshToken
116 s.CreatedAt = newSession.CreatedAt
117 s.ExpiresOn = newSession.ExpiresOn
118 s.Email = newSession.Email
122type gitlabUserInfo struct {
123 Username string `json:"nickname"`
124 Email string `json:"email"`
125 EmailVerified bool `json:"email_verified"`
126 Groups []string `json:"groups"`
129func (p *GitLabProvider) getUserInfo(ctx context.Context, s *sessions.SessionState) (*gitlabUserInfo, error) {
130 // Retrieve user info JSON
131 // https://docs.gitlab.com/ee/integration/openid_connect_provider.html#shared-information
133 // Build user info url from login url of GitLab instance
134 userInfoURL := *p.LoginURL
135 userInfoURL.Path = "/oauth/userinfo"
137 var userInfo gitlabUserInfo
138 err := requests.New(userInfoURL.String()).
140 SetHeader("Authorization", "Bearer "+s.AccessToken).
142 UnmarshalInto(&userInfo)
144 return nil, fmt.Errorf("error getting user info: %v", err)
147 return &userInfo, nil
150func (p *GitLabProvider) verifyGroupMembership(userInfo *gitlabUserInfo) error {
151 if len(p.Groups) == 0 {
155 // Collect user group memberships
156 membershipSet := make(map[string]bool)
157 for _, group := range userInfo.Groups {
158 membershipSet[group] = true
161 // Find a valid group that they are a member of
162 for _, validGroup := range p.Groups {
163 if _, ok := membershipSet[validGroup]; ok {
168 return fmt.Errorf("user is not a member of '%s'", p.Groups)
171func (p *GitLabProvider) verifyEmailDomain(userInfo *gitlabUserInfo) error {
172 if len(p.EmailDomains) == 0 || p.EmailDomains[0] == "*" {
176 for _, domain := range p.EmailDomains {
177 if strings.HasSuffix(userInfo.Email, domain) {
182 return fmt.Errorf("user email is not one of the valid domains '%v'", p.EmailDomains)
185func (p *GitLabProvider) createSessionState(ctx context.Context, token *oauth2.Token) (*sessions.SessionState, error) {
186 rawIDToken, ok := token.Extra("id_token").(string)
188 return nil, fmt.Errorf("token response did not contain an id_token")
191 // Parse and verify ID Token payload.
192 idToken, err := p.Verifier.Verify(ctx, rawIDToken)
194 return nil, fmt.Errorf("could not verify id_token: %v", err)
197 created := time.Now()
198 return &sessions.SessionState{
199 AccessToken: token.AccessToken,
201 RefreshToken: token.RefreshToken,
203 ExpiresOn: &idToken.Expiry,
207// ValidateSessionState checks that the session's IDToken is still valid
208func (p *GitLabProvider) ValidateSessionState(ctx context.Context, s *sessions.SessionState) bool {
209 _, err := p.Verifier.Verify(ctx, s.IDToken)
213// GetEmailAddress returns the Account email address
214func (p *GitLabProvider) GetEmailAddress(ctx context.Context, s *sessions.SessionState) (string, error) {
215 // Retrieve user info
216 userInfo, err := p.getUserInfo(ctx, s)
218 return "", fmt.Errorf("failed to retrieve user info: %v", err)
221 // Check if email is verified
222 if !p.AllowUnverifiedEmail && !userInfo.EmailVerified {
223 return "", fmt.Errorf("user email is not verified")
226 // Check if email has valid domain
227 err = p.verifyEmailDomain(userInfo)
229 return "", fmt.Errorf("email domain check failed: %v", err)
232 // Check group membership
233 err = p.verifyGroupMembership(userInfo)
235 return "", fmt.Errorf("group membership check failed: %v", err)
238 return userInfo.Email, nil
241// GetUserName returns the Account user name
242func (p *GitLabProvider) GetUserName(ctx context.Context, s *sessions.SessionState) (string, error) {
243 userInfo, err := p.getUserInfo(ctx, s)
245 return "", fmt.Errorf("failed to retrieve user info: %v", err)
248 return userInfo.Username, nil
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list