github.com/ngocphuongnb/tetua@v0.0.7-alpha/packages/auth/google.go (about)

     1  package auth
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"net/http"
     9  	"strings"
    10  
    11  	"github.com/ngocphuongnb/tetua/app/auth"
    12  	"github.com/ngocphuongnb/tetua/app/config"
    13  	"github.com/ngocphuongnb/tetua/app/entities"
    14  	"github.com/ngocphuongnb/tetua/app/server"
    15  	"github.com/ngocphuongnb/tetua/app/utils"
    16  	"golang.org/x/oauth2"
    17  	"golang.org/x/oauth2/google"
    18  )
    19  
    20  const oauthGoogleUrlAPI = "https://www.googleapis.com/oauth2/v2/userinfo?access_token="
    21  
    22  type GoogleUserResponse struct {
    23  	ID      string `json:"id"`
    24  	Email   string `json:"email"`
    25  	Name    string `json:"name"`
    26  	Picture string `json:"picture"`
    27  }
    28  
    29  type GoogleAuthProvider struct {
    30  	config *oauth2.Config
    31  }
    32  
    33  func NewGoogle(cfg map[string]string) server.AuthProvider {
    34  	if cfg["client_id"] == "" || cfg["client_secret"] == "" {
    35  		panic("Github client id or secret is not set")
    36  	}
    37  
    38  	return &GoogleAuthProvider{
    39  		config: &oauth2.Config{
    40  			ClientID:     cfg["client_id"],
    41  			ClientSecret: cfg["client_secret"],
    42  			Endpoint:     google.Endpoint,
    43  			RedirectURL:  utils.Url("/auth/google/callback"),
    44  			Scopes: []string{
    45  				"https://www.googleapis.com/auth/userinfo.email",
    46  				"https://www.googleapis.com/auth/userinfo.profile",
    47  			},
    48  		},
    49  	}
    50  }
    51  
    52  func (g *GoogleAuthProvider) Name() string {
    53  	return "google"
    54  }
    55  
    56  func (g *GoogleAuthProvider) GetGoogleUserFromAccessCode(code string) (*GoogleUserResponse, error) {
    57  	token, err := g.config.Exchange(context.Background(), code)
    58  	if err != nil {
    59  		return nil, fmt.Errorf("code exchange wrong: %s", err.Error())
    60  	}
    61  	response, err := http.Get(oauthGoogleUrlAPI + token.AccessToken)
    62  	if err != nil {
    63  		return nil, fmt.Errorf("failed getting user info: %s", err.Error())
    64  	}
    65  	defer response.Body.Close()
    66  	body, err := ioutil.ReadAll(response.Body)
    67  
    68  	if err != nil {
    69  		return nil, fmt.Errorf("failed read response: %s", err.Error())
    70  	}
    71  
    72  	userResponse := &GoogleUserResponse{}
    73  	if err := json.Unmarshal(body, userResponse); err != nil {
    74  		return nil, err
    75  	}
    76  
    77  	return userResponse, nil
    78  }
    79  
    80  func (g *GoogleAuthProvider) Login(c server.Context) error {
    81  	url := g.config.AuthCodeURL(c.Cookies(config.COOKIE_UUID))
    82  
    83  	return c.Redirect(url)
    84  }
    85  
    86  func (g *GoogleAuthProvider) Callback(c server.Context) (u *entities.User, err error) {
    87  	if c.Query("state") != c.Cookies(config.COOKIE_UUID) {
    88  		return nil, fmt.Errorf("invalid oauth google state")
    89  	}
    90  
    91  	if c.Query("code") == "" {
    92  		return nil, fmt.Errorf("code is empty")
    93  	}
    94  
    95  	googleUser, err := g.GetGoogleUserFromAccessCode(c.Query("code"))
    96  
    97  	if err != nil {
    98  		return nil, err
    99  	}
   100  
   101  	return &entities.User{
   102  		Provider:         "google",
   103  		ProviderID:       utils.SanitizePlainText(googleUser.ID),
   104  		Username:         utils.SanitizePlainText(strings.Split(googleUser.Email, "@gmail")[0]),
   105  		Email:            utils.SanitizePlainText(googleUser.Email),
   106  		ProviderAvatar:   utils.SanitizePlainText(googleUser.Picture),
   107  		DisplayName:      utils.SanitizePlainText(googleUser.Name),
   108  		URL:              utils.SanitizePlainText(""),
   109  		ProviderUsername: utils.SanitizePlainText(googleUser.Email),
   110  		RoleIDs:          []int{auth.ROLE_USER.ID},
   111  		Active:           config.Setting("auto_approve_user") == "yes",
   112  	}, nil
   113  }