chatdesk-ui/auth_v2.169.0/internal/api/provider/google.go

145 lines
3.5 KiB
Go

package provider
import (
"context"
"strings"
"github.com/coreos/go-oidc/v3/oidc"
"github.com/sirupsen/logrus"
"github.com/supabase/auth/internal/conf"
"golang.org/x/oauth2"
)
type googleUser struct {
ID string `json:"id"`
Subject string `json:"sub"`
Issuer string `json:"iss"`
Name string `json:"name"`
AvatarURL string `json:"picture"`
Email string `json:"email"`
VerifiedEmail bool `json:"verified_email"`
EmailVerified bool `json:"email_verified"`
HostedDomain string `json:"hd"`
}
func (u googleUser) IsEmailVerified() bool {
return u.VerifiedEmail || u.EmailVerified
}
const IssuerGoogle = "https://accounts.google.com"
var internalIssuerGoogle = IssuerGoogle
type googleProvider struct {
*oauth2.Config
oidc *oidc.Provider
}
// NewGoogleProvider creates a Google OAuth2 identity provider.
func NewGoogleProvider(ctx context.Context, ext conf.OAuthProviderConfiguration, scopes string) (OAuthProvider, error) {
if err := ext.ValidateOAuth(); err != nil {
return nil, err
}
if ext.URL != "" {
logrus.Warn("Google OAuth provider has URL config set which is ignored (check GOTRUE_EXTERNAL_GOOGLE_URL)")
}
oauthScopes := []string{
"email",
"profile",
}
if scopes != "" {
oauthScopes = append(oauthScopes, strings.Split(scopes, ",")...)
}
oidcProvider, err := oidc.NewProvider(ctx, internalIssuerGoogle)
if err != nil {
return nil, err
}
return &googleProvider{
Config: &oauth2.Config{
ClientID: ext.ClientID[0],
ClientSecret: ext.Secret,
Endpoint: oidcProvider.Endpoint(),
Scopes: oauthScopes,
RedirectURL: ext.RedirectURI,
},
oidc: oidcProvider,
}, nil
}
func (g googleProvider) GetOAuthToken(code string) (*oauth2.Token, error) {
return g.Exchange(context.Background(), code)
}
const UserInfoEndpointGoogle = "https://www.googleapis.com/userinfo/v2/me"
var internalUserInfoEndpointGoogle = UserInfoEndpointGoogle
func (g googleProvider) GetUserData(ctx context.Context, tok *oauth2.Token) (*UserProvidedData, error) {
if idToken := tok.Extra("id_token"); idToken != nil {
_, data, err := ParseIDToken(ctx, g.oidc, &oidc.Config{
ClientID: g.Config.ClientID,
}, idToken.(string), ParseIDTokenOptions{
AccessToken: tok.AccessToken,
})
if err != nil {
return nil, err
}
return data, nil
}
// This whole section offers legacy support in case the Google OAuth2
// flow does not return an ID Token for the user, which appears to
// always be the case.
logrus.Info("Using Google OAuth2 user info endpoint, an ID token was not returned by Google")
var u googleUser
if err := makeRequest(ctx, tok, g.Config, internalUserInfoEndpointGoogle, &u); err != nil {
return nil, err
}
var data UserProvidedData
if u.Email != "" {
data.Emails = append(data.Emails, Email{
Email: u.Email,
Verified: u.IsEmailVerified(),
Primary: true,
})
}
data.Metadata = &Claims{
Issuer: internalUserInfoEndpointGoogle,
Subject: u.ID,
Name: u.Name,
Picture: u.AvatarURL,
Email: u.Email,
EmailVerified: u.IsEmailVerified(),
// To be deprecated
AvatarURL: u.AvatarURL,
FullName: u.Name,
ProviderId: u.ID,
}
return &data, nil
}
// ResetGoogleProvider should only be used in tests!
func ResetGoogleProvider() {
internalIssuerGoogle = IssuerGoogle
internalUserInfoEndpointGoogle = UserInfoEndpointGoogle
}
// OverrideGoogleProvider should only be used in tests!
func OverrideGoogleProvider(issuer, userInfo string) {
internalIssuerGoogle = issuer
internalUserInfoEndpointGoogle = userInfo
}