Skip to content

Commit 5cda81b

Browse files
Use updated Google user info endpoint (#621)
1 parent 6c7fc31 commit 5cda81b

File tree

8 files changed

+53
-393
lines changed

8 files changed

+53
-393
lines changed

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ $ go get github.com/markbates/goth
3737
* GitHub
3838
* Gitlab
3939
* Google
40-
* Google+ (deprecated)
4140
* Heroku
4241
* InfluxCloud
4342
* Instagram

examples/main.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ import (
3030
"github.com/markbates/goth/providers/github"
3131
"github.com/markbates/goth/providers/gitlab"
3232
"github.com/markbates/goth/providers/google"
33-
"github.com/markbates/goth/providers/gplus"
3433
"github.com/markbates/goth/providers/heroku"
3534
"github.com/markbates/goth/providers/instagram"
3635
"github.com/markbates/goth/providers/intercom"
@@ -89,7 +88,6 @@ func main() {
8988
facebook.New(os.Getenv("FACEBOOK_KEY"), os.Getenv("FACEBOOK_SECRET"), "http://localhost:3000/auth/facebook/callback"),
9089
fitbit.New(os.Getenv("FITBIT_KEY"), os.Getenv("FITBIT_SECRET"), "http://localhost:3000/auth/fitbit/callback"),
9190
google.New(os.Getenv("GOOGLE_KEY"), os.Getenv("GOOGLE_SECRET"), "http://localhost:3000/auth/google/callback"),
92-
gplus.New(os.Getenv("GPLUS_KEY"), os.Getenv("GPLUS_SECRET"), "http://localhost:3000/auth/gplus/callback"),
9391
github.New(os.Getenv("GITHUB_KEY"), os.Getenv("GITHUB_SECRET"), "http://localhost:3000/auth/github/callback"),
9492
spotify.New(os.Getenv("SPOTIFY_KEY"), os.Getenv("SPOTIFY_SECRET"), "http://localhost:3000/auth/spotify/callback"),
9593
linkedin.New(os.Getenv("LINKEDIN_KEY"), os.Getenv("LINKEDIN_SECRET"), "http://localhost:3000/auth/linkedin/callback"),
@@ -179,7 +177,6 @@ func main() {
179177
"github": "Github",
180178
"gitlab": "Gitlab",
181179
"google": "Google",
182-
"gplus": "Google Plus",
183180
"heroku": "Heroku",
184181
"instagram": "Instagram",
185182
"intercom": "Intercom",

providers/google/google.go

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,13 @@ import (
77
"fmt"
88
"io"
99
"net/http"
10-
"net/url"
1110
"strings"
1211

1312
"github.com/markbates/goth"
1413
"golang.org/x/oauth2"
1514
)
1615

17-
const endpointProfile string = "https://www.googleapis.com/oauth2/v2/userinfo"
16+
const endpointProfile string = "https://openidconnect.googleapis.com/v1/userinfo"
1817

1918
// New creates a new Google provider, and sets up important connection details.
2019
// You should always call `google.New` to get a new Provider. Never try to create
@@ -76,6 +75,7 @@ func (p *Provider) BeginAuth(state string) (goth.Session, error) {
7675

7776
type googleUser struct {
7877
ID string `json:"id"`
78+
Sub string `json:"sub"`
7979
Email string `json:"email"`
8080
Name string `json:"name"`
8181
FirstName string `json:"given_name"`
@@ -100,7 +100,13 @@ func (p *Provider) FetchUser(session goth.Session) (goth.User, error) {
100100
return user, fmt.Errorf("%s cannot get user information without accessToken", p.providerName)
101101
}
102102

103-
response, err := p.Client().Get(endpointProfile + "?access_token=" + url.QueryEscape(sess.AccessToken))
103+
req, err := http.NewRequest("GET", endpointProfile, nil)
104+
if err != nil {
105+
return user, err
106+
}
107+
req.Header.Set("Authorization", "Bearer "+sess.AccessToken)
108+
109+
response, err := p.Client().Do(req)
104110
if err != nil {
105111
return user, err
106112
}
@@ -127,7 +133,13 @@ func (p *Provider) FetchUser(session goth.Session) (goth.User, error) {
127133
user.NickName = u.Name
128134
user.Email = u.Email
129135
user.AvatarURL = u.Picture
130-
user.UserID = u.ID
136+
137+
if u.ID != "" {
138+
user.UserID = u.ID
139+
} else {
140+
user.UserID = u.Sub
141+
}
142+
131143
// Google provides other useful fields such as 'hd'; get them from RawData
132144
if err := json.Unmarshal(responseBytes, &user.RawData); err != nil {
133145
return user, err
@@ -148,7 +160,7 @@ func newConfig(provider *Provider, scopes []string) *oauth2.Config {
148160
if len(scopes) > 0 {
149161
c.Scopes = append(c.Scopes, scopes...)
150162
} else {
151-
c.Scopes = []string{"email"}
163+
c.Scopes = []string{"openid", "email", "profile"}
152164
}
153165
return c
154166
}

providers/google/google_test.go

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package google_test
22

33
import (
4+
"encoding/json"
45
"fmt"
56
"os"
67
"testing"
@@ -31,7 +32,7 @@ func Test_BeginAuth(t *testing.T) {
3132
a.Contains(s.AuthURL, "accounts.google.com/o/oauth2/auth")
3233
a.Contains(s.AuthURL, fmt.Sprintf("client_id=%s", os.Getenv("GOOGLE_KEY")))
3334
a.Contains(s.AuthURL, "state=test_state")
34-
a.Contains(s.AuthURL, "scope=email")
35+
a.Contains(s.AuthURL, "scope=openid+email+profile")
3536
a.Contains(s.AuthURL, "access_type=offline")
3637
}
3738

@@ -50,7 +51,7 @@ func Test_BeginAuthWithPrompt(t *testing.T) {
5051
a.Contains(s.AuthURL, "accounts.google.com/o/oauth2/auth")
5152
a.Contains(s.AuthURL, fmt.Sprintf("client_id=%s", os.Getenv("GOOGLE_KEY")))
5253
a.Contains(s.AuthURL, "state=test_state")
53-
a.Contains(s.AuthURL, "scope=email")
54+
a.Contains(s.AuthURL, "scope=openid+email+profile")
5455
a.Contains(s.AuthURL, "access_type=offline")
5556
a.Contains(s.AuthURL, "prompt=test+prompts")
5657
}
@@ -70,7 +71,7 @@ func Test_BeginAuthWithHostedDomain(t *testing.T) {
7071
a.Contains(s.AuthURL, "accounts.google.com/o/oauth2/auth")
7172
a.Contains(s.AuthURL, fmt.Sprintf("client_id=%s", os.Getenv("GOOGLE_KEY")))
7273
a.Contains(s.AuthURL, "state=test_state")
73-
a.Contains(s.AuthURL, "scope=email")
74+
a.Contains(s.AuthURL, "scope=openid+email+profile")
7475
a.Contains(s.AuthURL, "access_type=offline")
7576
a.Contains(s.AuthURL, "hd=example.com")
7677
}
@@ -90,7 +91,7 @@ func Test_BeginAuthWithLoginHint(t *testing.T) {
9091
a.Contains(s.AuthURL, "accounts.google.com/o/oauth2/auth")
9192
a.Contains(s.AuthURL, fmt.Sprintf("client_id=%s", os.Getenv("GOOGLE_KEY")))
9293
a.Contains(s.AuthURL, "state=test_state")
93-
a.Contains(s.AuthURL, "scope=email")
94+
a.Contains(s.AuthURL, "scope=openid+email+profile")
9495
a.Contains(s.AuthURL, "access_type=offline")
9596
a.Contains(s.AuthURL, "login_hint=john%40example.com")
9697
}
@@ -115,6 +116,37 @@ func Test_SessionFromJSON(t *testing.T) {
115116
a.Equal(session.AccessToken, "1234567890")
116117
}
117118

119+
func Test_UserIDHandling(t *testing.T) {
120+
t.Parallel()
121+
a := assert.New(t)
122+
123+
// Test v2 endpoint response format (uses 'id' field)
124+
v2Response := `{"id":"123456789","email":"[email protected]","name":"Test User"}`
125+
var userV2 struct {
126+
ID string `json:"id"`
127+
Sub string `json:"sub"`
128+
Email string `json:"email"`
129+
Name string `json:"name"`
130+
}
131+
err := json.Unmarshal([]byte(v2Response), &userV2)
132+
a.NoError(err)
133+
a.Equal("123456789", userV2.ID)
134+
a.Equal("", userV2.Sub)
135+
136+
// Test OpenID Connect response format (uses 'sub' field)
137+
oidcResponse := `{"sub":"123456789","email":"[email protected]","name":"Test User"}`
138+
var userOIDC struct {
139+
ID string `json:"id"`
140+
Sub string `json:"sub"`
141+
Email string `json:"email"`
142+
Name string `json:"name"`
143+
}
144+
err = json.Unmarshal([]byte(oidcResponse), &userOIDC)
145+
a.NoError(err)
146+
a.Equal("", userOIDC.ID)
147+
a.Equal("123456789", userOIDC.Sub)
148+
}
149+
118150
func googleProvider() *google.Provider {
119151
return google.New(os.Getenv("GOOGLE_KEY"), os.Getenv("GOOGEL_SECRET"), "/foo")
120152
}

providers/gplus/gplus.go

Lines changed: 0 additions & 194 deletions
This file was deleted.

0 commit comments

Comments
 (0)