@@ -3,9 +3,13 @@ package providers
33
44import (
55 "context"
6+ "crypto/rsa"
67 "encoding/json"
8+ "errors"
9+ "fmt"
710 "net/http"
811
12+ "github.com/golang-jwt/jwt/v4"
913 "golang.org/x/oauth2"
1014 "golang.org/x/oauth2/google"
1115)
@@ -20,7 +24,7 @@ func NewGoogleProvider(clientID, clientSecret, redirectURL string) *GoogleProvid
2024 ClientID : clientID ,
2125 ClientSecret : clientSecret ,
2226 RedirectURL : redirectURL ,
23- Scopes : []string {"https://www.googleapis.com/auth/userinfo.email " , "https://www.googleapis.com/auth/userinfo. profile" },
27+ Scopes : []string {"openid " , "profile" , "email " },
2428 Endpoint : google .Endpoint ,
2529 },
2630 }
@@ -37,9 +41,14 @@ func (g *GoogleProvider) ExchangeCode(ctx context.Context, code string) (TokenRe
3741 if err != nil {
3842 return TokenResponse {}, err
3943 }
44+ idToken , ok := token .Extra ("id_token" ).(string )
45+ if ! ok {
46+ return TokenResponse {}, fmt .Errorf ("ID Token not found in response" )
47+ }
4048 return TokenResponse {
4149 AccessToken : token .AccessToken ,
4250 RefreshToken : token .RefreshToken ,
51+ IDToken : idToken ,
4352 ExpiresIn : int (token .Expiry .Sub (token .Expiry ).Seconds ()),
4453 }, nil
4554}
@@ -64,3 +73,70 @@ func (g *GoogleProvider) GetUserInfo(accessToken string) (UserInfo, error) {
6473 }
6574 return userInfo , nil
6675}
76+
77+ func (g * GoogleProvider ) GetUserInfoFromIDToken (idToken string ) (UserInfo , error ) {
78+ var userInfo UserInfo
79+
80+ // Parse and validate the ID token
81+ parsedToken , err := jwt .Parse (idToken , func (token * jwt.Token ) (interface {}, error ) {
82+ return getGooglePublicKey ()
83+ })
84+ if err != nil {
85+ return userInfo , err
86+ }
87+
88+ // Check if the token is valid and has the expected claims
89+ claims , ok := parsedToken .Claims .(jwt.MapClaims )
90+ if ! ok {
91+ return userInfo , errors .New ("invalid claims in ID Token" )
92+ }
93+
94+ // Extract user info from the claims
95+ userInfo .Email = claims ["email" ].(string )
96+ userInfo .Name = claims ["name" ].(string )
97+ userInfo .PictureURL = claims ["picture" ].(string )
98+
99+ return userInfo , nil
100+ }
101+
102+ func getGooglePublicKey () (* rsa.PublicKey , error ) {
103+ const certsURL = "https://www.googleapis.com/oauth2/v3/certs"
104+ resp , err := http .Get (certsURL )
105+ if err != nil {
106+ return nil , err
107+ }
108+ defer resp .Body .Close ()
109+
110+ var certs map [string ]interface {}
111+ if err := json .NewDecoder (resp .Body ).Decode (& certs ); err != nil {
112+ return nil , err
113+ }
114+
115+ keyID := "some-key-id"
116+ if keyData , ok := certs [keyID ]; ok {
117+ keyDataMap , ok := keyData .(map [string ]interface {})
118+ if ! ok {
119+ return nil , fmt .Errorf ("invalid key data" )
120+ }
121+
122+ publicKeyData , ok := keyDataMap ["x5c" ].([]interface {})
123+ if ! ok || len (publicKeyData ) == 0 {
124+ return nil , fmt .Errorf ("x5c field missing or invalid" )
125+ }
126+
127+ certData := publicKeyData [0 ].(string )
128+ certBytes , err := json .Marshal (certData )
129+ if err != nil {
130+ return nil , err
131+ }
132+
133+ publicKey , err := jwt .ParseRSAPublicKeyFromPEM (certBytes )
134+ if err != nil {
135+ return nil , fmt .Errorf ("unable to parse public key: %w" , err )
136+ }
137+
138+ return publicKey , nil
139+ }
140+
141+ return nil , fmt .Errorf ("key not found" )
142+ }
0 commit comments