Repository: markbates/goth
Pull request #263 (2019-01-03 06:16:19)

add fields options
This PR is for issue #236, #252. 

PR #246 does not work correctly because of inappropriate scopes.

The following is a sample code to add custom fields.

```
func init() {
	gothic.Store = App().SessionStore
	query := []string{"email", "first_name", "last_name", "id", "name", "picture.type(large)", "location"}
	goth.UseProviders(
		facebook.New(os.Getenv("FACEBOOK_KEY"), os.Getenv("FACEBOOK_SECRET"), fmt.Sprintf("%s%s", "http://localhost:3000", "/auth/facebook/callback")).SetCustomFields(query),

	)
}
```


Patch:
diff --git a/providers/facebook/facebook.go b/providers/facebook/facebook.go
index 5c80ca7..dd13580 100644
--- a/providers/facebook/facebook.go
+++ b/providers/facebook/facebook.go
@@ -37,6 +37,7 @@ func New(clientKey, secret, callbackURL string, scopes ...string) *Provider {
 		providerName: "facebook",
 	}
 	p.config = newConfig(p, scopes)
+	p.Fields = "email,first_name,last_name,link,about,id,name,picture,location"
 	return p
 }
 
@@ -46,6 +47,7 @@ type Provider struct {
 	Secret       string
 	CallbackURL  string
 	HTTPClient   *http.Client
+	Fields       string
 	config       *oauth2.Config
 	providerName string
 }
@@ -60,6 +62,16 @@ func (p *Provider) SetName(name string) {
 	p.providerName = name
 }
 
+// SetCustomFields sets the fields used to return information
+// for a user.
+//
+// A list of available field values can be found at
+// https://developers.facebook.com/docs/graph-api/reference/user
+func (p *Provider) SetCustomFields(fields []string) *Provider {
+	p.Fields = strings.Join(fields, ",")
+	return p
+}
+
 func (p *Provider) Client() *http.Client {
 	return goth.HTTPClientWithFallBack(p.HTTPClient)
 }
@@ -99,7 +111,7 @@ func (p *Provider) FetchUser(session goth.Session) (goth.User, error) {
 
 	reqUrl := fmt.Sprint(
 		endpointProfile,
-		strings.Join(p.config.Scopes, ","),
+		p.Fields,
 		"&access_token=",
 		url.QueryEscape(sess.AccessToken),
 		"&appsecret_proof=",
@@ -177,31 +189,17 @@ func newConfig(provider *Provider, scopes []string) *oauth2.Config {
 		},
 		Scopes: []string{
 			"email",
-			"first_name",
-			"last_name",
-			"link",
-			"about",
-			"id",
-			"name",
-			"picture",
-			"location",
 		},
 	}
 
-	// creates possibility to invoke field method like 'picture.type(large)'
-	var found bool
-	for _, sc := range scopes {
-		sc := sc
-		for i, defScope := range c.Scopes {
-			if defScope == strings.Split(sc, ".")[0] {
-				c.Scopes[i] = sc
-				found = true
-			}
-		}
-		if !found {
-			c.Scopes = append(c.Scopes, sc)
+	defaultScopes := map[string]struct{}{
+		"email": {},
+	}
+
+	for _, scope := range scopes {
+		if _, exists := defaultScopes[scope]; !exists {
+			c.Scopes = append(c.Scopes, scope)
 		}
-		found = false
 	}
 
 	return c


Test patch:
diff --git a/providers/facebook/facebook_test.go b/providers/facebook/facebook_test.go
index 8a01c89..df9dd1c 100644
--- a/providers/facebook/facebook_test.go
+++ b/providers/facebook/facebook_test.go
@@ -3,6 +3,7 @@ package facebook_test
 import (
 	"fmt"
 	"os"
+	"strings"
 	"testing"
 
 	"github.com/markbates/goth"
@@ -54,12 +55,18 @@ func Test_SessionFromJSON(t *testing.T) {
 	a.Equal(session.AccessToken, "1234567890")
 }
 
+func Test_SetCustomFields(t *testing.T) {
+	t.Parallel()
+	defaultFields := "email,first_name,last_name,link,about,id,name,picture,location"
+	cf := []string{"email", "picture.type(large)"}
+	a := assert.New(t)
+
+	provider := facebookProvider()
+	a.Equal(provider.Fields, defaultFields)
+	provider.SetCustomFields(cf)
+	a.Equal(provider.Fields, strings.Join(cf, ","))
+}
+
 func facebookProvider() *facebook.Provider {
-	scopes := []string{"picture.type(large)", "email"}
-	return facebook.New(
-		os.Getenv("FACEBOOK_KEY"),
-		os.Getenv("FACEBOOK_SECRET"),
-		"/foo",
-		scopes...,
-	)
+	return facebook.New(os.Getenv("FACEBOOK_KEY"), os.Getenv("FACEBOOK_SECRET"), "/foo", "email")
 }