github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/pkg/server/oauth_google.go (about)

     1  package server
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  	"net/http"
     8  	"net/url"
     9  	"strings"
    10  
    11  	"github.com/pyroscope-io/pyroscope/pkg/config"
    12  	"github.com/sirupsen/logrus"
    13  	"golang.org/x/oauth2"
    14  )
    15  
    16  type oauthHandlerGoogle struct {
    17  	oauthBase
    18  	allowedDomains []string
    19  }
    20  
    21  func newOauthGoogleHandler(cfg config.GoogleOauth, baseURL string, log *logrus.Logger) (*oauthHandlerGoogle, error) {
    22  	authURL, err := url.Parse(cfg.AuthURL)
    23  	if err != nil {
    24  		return nil, err
    25  	}
    26  
    27  	h := &oauthHandlerGoogle{
    28  		oauthBase: oauthBase{
    29  			config: &oauth2.Config{
    30  				ClientID:     cfg.ClientID,
    31  				ClientSecret: cfg.ClientSecret,
    32  				Scopes:       []string{"https://www.googleapis.com/auth/userinfo.email"},
    33  				Endpoint:     oauth2.Endpoint{AuthURL: cfg.AuthURL, TokenURL: cfg.TokenURL},
    34  			},
    35  			authURL:       authURL,
    36  			log:           log,
    37  			callbackRoute: "/auth/google/callback",
    38  			redirectRoute: "/auth/google/redirect",
    39  			apiURL:        "https://www.googleapis.com/oauth2/v2",
    40  			baseURL:       baseURL,
    41  		},
    42  		allowedDomains: cfg.AllowedDomains,
    43  	}
    44  
    45  	if cfg.RedirectURL != "" {
    46  		h.config.RedirectURL = cfg.RedirectURL
    47  	}
    48  
    49  	return h, nil
    50  }
    51  
    52  func (o oauthHandlerGoogle) userAuth(client *http.Client) (extUserInfo, error) {
    53  	type userProfileResponse struct {
    54  		ID            string
    55  		Email         string
    56  		VerifiedEmail bool
    57  		Picture       string
    58  	}
    59  
    60  	resp, err := client.Get(o.apiURL + "/userinfo")
    61  	if err != nil {
    62  		return extUserInfo{}, fmt.Errorf("failed to get oauth user info: %w", err)
    63  	}
    64  	defer resp.Body.Close()
    65  
    66  	var userProfile userProfileResponse
    67  	err = json.NewDecoder(resp.Body).Decode(&userProfile)
    68  	if err != nil {
    69  		return extUserInfo{}, fmt.Errorf("failed to decode user profile response: %w", err)
    70  	}
    71  	u := extUserInfo{
    72  		Name:  userProfile.Email,
    73  		Email: userProfile.Email,
    74  	}
    75  	if userProfile.Email == "" {
    76  		return extUserInfo{}, errors.New("user email is empty")
    77  	}
    78  
    79  	if len(o.allowedDomains) == 0 || (len(o.allowedDomains) > 0 && isAllowedDomain(o.allowedDomains, userProfile.Email)) {
    80  		return u, nil
    81  	}
    82  
    83  	return extUserInfo{}, errForbidden
    84  }
    85  
    86  func isAllowedDomain(allowedDomains []string, email string) bool {
    87  	for _, domain := range allowedDomains {
    88  		if strings.HasSuffix(email, fmt.Sprintf("@%s", domain)) {
    89  			return true
    90  		}
    91  	}
    92  
    93  	return false
    94  }
    95  
    96  func (o oauthHandlerGoogle) getOauthBase() oauthBase {
    97  	return o.oauthBase
    98  }