From 7b297808cee599f5ff58b9f6afa64d3ee980998e Mon Sep 17 00:00:00 2001
From: Lauris BH <lauris@nix.lv>
Date: Mon, 19 Feb 2018 07:10:51 +0200
Subject: [PATCH] Update markbates/goth library (#3533)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Lauris Bukšis-Haberkorns <lauris@nix.lv>
---
 vendor/github.com/markbates/goth/README.md    |  97 +++++-----
 .../markbates/goth/gothic/gothic.go           | 168 +++++++++++++++---
 .../goth/providers/bitbucket/bitbucket.go     |  12 +-
 .../goth/providers/dropbox/dropbox.go         |  21 +--
 .../goth/providers/facebook/facebook.go       |  16 +-
 .../markbates/goth/providers/gitlab/gitlab.go |   2 +-
 .../markbates/goth/providers/gplus/gplus.go   |  10 +-
 .../providers/openidConnect/openidConnect.go  |  32 ++--
 .../goth/providers/openidConnect/session.go   |   4 +-
 .../goth/providers/twitter/twitter.go         |  24 +--
 vendor/vendor.json                            |  58 +++---
 11 files changed, 284 insertions(+), 160 deletions(-)

diff --git a/vendor/github.com/markbates/goth/README.md b/vendor/github.com/markbates/goth/README.md
index b481683bcc..05b19fce5a 100644
--- a/vendor/github.com/markbates/goth/README.md
+++ b/vendor/github.com/markbates/goth/README.md
@@ -8,6 +8,10 @@ protocol providers, as long as they implement the `Provider` and `Session` inter
 
 This package was inspired by [https://github.com/intridea/omniauth](https://github.com/intridea/omniauth).
 
+## Goth Needs a New Maintainer
+
+[https://blog.gobuffalo.io/goth-needs-a-new-maintainer-626cd47ca37b](https://blog.gobuffalo.io/goth-needs-a-new-maintainer-626cd47ca37b) - TL;DR: I, @markbates, won't be responding to any more issues, PRs, etc... for this package. A new maintainer needs to be found ASAP. Is this you?
+
 ## Installation
 
 ```text
@@ -18,6 +22,8 @@ $ go get github.com/markbates/goth
 
 * Amazon
 * Auth0
+* Azure AD
+* Battle.net
 * Bitbucket
 * Box
 * Cloud Foundry
@@ -26,6 +32,7 @@ $ go get github.com/markbates/goth
 * Digital Ocean
 * Discord
 * Dropbox
+* Eve Online
 * Facebook
 * Fitbit
 * GitHub
@@ -38,6 +45,7 @@ $ go get github.com/markbates/goth
 * Lastfm
 * Linkedin
 * Meetup
+* MicrosoftOnline
 * OneDrive
 * OpenID Connect (auto discovery)
 * Paypal
@@ -50,7 +58,9 @@ $ go get github.com/markbates/goth
 * Twitch
 * Twitter
 * Uber
+* VK
 * Wepay
+* Xero
 * Yahoo
 * Yammer
 
@@ -71,17 +81,51 @@ $ go get github.com/markbates/goth
 ```text
 $ cd goth/examples
 $ go get -v
-$ go build 
+$ go build
 $ ./examples
 ```
 
 Now open up your browser and go to [http://localhost:3000](http://localhost:3000) to see the example.
 
-To actually use the different providers, please make sure you configure them given the system environments as defined in the examples/main.go file
+To actually use the different providers, please make sure you set environment variables. Example given in the examples/main.go file
+
+## Security Notes
+
+By default, gothic uses a `CookieStore` from the `gorilla/sessions` package to store session data.
+
+As configured, this default store (`gothic.Store`) will generate cookies with `Options`:
+
+```go
+&Options{
+   Path:   "/",
+   Domain: "",
+   MaxAge: 86400 * 30,
+   HttpOnly: true,
+   Secure: false,
+ }
+```
+
+To tailor these fields for your application, you can override the `gothic.Store` variable at startup.
+
+The follow snippet show one way to do this:
+
+```go
+key := ""             // Replace with your SESSION_SECRET or similar
+maxAge := 86400 * 30  // 30 days
+isProd := false       // Set to true when serving over https
+
+store := sessions.NewCookieStore([]byte(key))
+store.MaxAge(maxAge)
+store.Options.Path = "/"
+store.Options.HttpOnly = true   // HttpOnly should always be enabled
+store.Options.Secure = isProd
+
+gothic.Store = store
+```
 
 ## Issues
 
-Issues always stand a significantly better chance of getting fixed if the are accompanied by a
+Issues always stand a significantly better chance of getting fixed if they are accompanied by a
 pull request.
 
 ## Contributing
@@ -94,50 +138,3 @@ Would I love to see more providers? Certainly! Would you love to contribute one?
 4. Commit your changes (git commit -am 'Add some feature')
 5. Push to the branch (git push origin my-new-feature)
 6. Create new Pull Request
-
-## Contributors
-
-* Mark Bates
-* Tyler Bunnell
-* Corey McGrillis
-* willemvd
-* Rakesh Goyal
-* Andy Grunwald
-* Glenn Walker
-* Kevin Fitzpatrick
-* Ben Tranter
-* Sharad Ganapathy
-* Andrew Chilton
-* sharadgana
-* Aurorae
-* Craig P Jolicoeur
-* Zac Bergquist
-* Geoff Franks
-* Raphael Geronimi
-* Noah Shibley
-* lumost
-* oov
-* Felix Lamouroux
-* Rafael Quintela
-* Tyler
-* DenSm
-* Samy KACIMI
-* dante gray
-* Noah
-* Jacob Walker
-* Marin Martinic
-* Roy
-* Omni Adams
-* Sasa Brankovic
-* dkhamsing
-* Dante Swift
-* Attila Domokos
-* Albin Gilles
-* Syed Zubairuddin
-* Johnny Boursiquot
-* Jerome Touffe-Blin
-* bryanl
-* Masanobu YOSHIOKA
-* Jonathan Hall
-* HaiMing.Yin
-* Sairam Kunala
diff --git a/vendor/github.com/markbates/goth/gothic/gothic.go b/vendor/github.com/markbates/goth/gothic/gothic.go
index f6aaf2d117..8b8e114940 100644
--- a/vendor/github.com/markbates/goth/gothic/gothic.go
+++ b/vendor/github.com/markbates/goth/gothic/gothic.go
@@ -8,10 +8,18 @@ See https://github.com/markbates/goth/examples/main.go to see this in action.
 package gothic
 
 import (
+	"bytes"
+	"compress/gzip"
+	"encoding/base64"
 	"errors"
 	"fmt"
+	"io/ioutil"
+	"math/rand"
 	"net/http"
+	"net/url"
 	"os"
+	"strings"
+	"time"
 
 	"github.com/gorilla/mux"
 	"github.com/gorilla/sessions"
@@ -27,15 +35,21 @@ var defaultStore sessions.Store
 
 var keySet = false
 
+var gothicRand *rand.Rand
+
 func init() {
 	key := []byte(os.Getenv("SESSION_SECRET"))
 	keySet = len(key) != 0
-	Store = sessions.NewCookieStore([]byte(key))
+
+	cookieStore := sessions.NewCookieStore([]byte(key))
+	cookieStore.Options.HttpOnly = true
+	Store = cookieStore
 	defaultStore = Store
+	gothicRand = rand.New(rand.NewSource(time.Now().UnixNano()))
 }
 
 /*
-BeginAuthHandler is a convienence handler for starting the authentication process.
+BeginAuthHandler is a convenience handler for starting the authentication process.
 It expects to be able to get the name of the provider from the query parameters
 as either "provider" or ":provider".
 
@@ -65,8 +79,16 @@ var SetState = func(req *http.Request) string {
 		return state
 	}
 
-	return "state"
-
+	// If a state query param is not passed in, generate a random
+	// base64-encoded nonce so that the state on the auth URL
+	// is unguessable, preventing CSRF attacks, as described in
+	//
+	// https://auth0.com/docs/protocols/oauth2/oauth-state#keep-reading
+	nonceBytes := make([]byte, 64)
+	for i := 0; i < 64; i++ {
+		nonceBytes[i] = byte(gothicRand.Int63() % 256)
+	}
+	return base64.URLEncoding.EncodeToString(nonceBytes)
 }
 
 // GetState gets the state returned by the provider during the callback.
@@ -87,7 +109,6 @@ I would recommend using the BeginAuthHandler instead of doing all of these steps
 yourself, but that's entirely up to you.
 */
 func GetAuthURL(res http.ResponseWriter, req *http.Request) (string, error) {
-
 	if !keySet && defaultStore == Store {
 		fmt.Println("goth/gothic: no SESSION_SECRET environment variable is set. The default cookie store is not available and any calls will fail. Ignore this warning if you are using a different store.")
 	}
@@ -130,7 +151,7 @@ as either "provider" or ":provider".
 See https://github.com/markbates/goth/examples/main.go to see this in action.
 */
 var CompleteUserAuth = func(res http.ResponseWriter, req *http.Request) (goth.User, error) {
-
+	defer Logout(res, req)
 	if !keySet && defaultStore == Store {
 		fmt.Println("goth/gothic: no SESSION_SECRET environment variable is set. The default cookie store is not available and any calls will fail. Ignore this warning if you are using a different store.")
 	}
@@ -155,6 +176,11 @@ var CompleteUserAuth = func(res http.ResponseWriter, req *http.Request) (goth.Us
 		return goth.User{}, err
 	}
 
+	err = validateState(req, sess)
+	if err != nil {
+		return goth.User{}, err
+	}
+
 	user, err := provider.FetchUser(sess)
 	if err == nil {
 		// user can be found with existing session data
@@ -173,7 +199,43 @@ var CompleteUserAuth = func(res http.ResponseWriter, req *http.Request) (goth.Us
 		return goth.User{}, err
 	}
 
-	return provider.FetchUser(sess)
+	gu, err := provider.FetchUser(sess)
+	return gu, err
+}
+
+// validateState ensures that the state token param from the original
+// AuthURL matches the one included in the current (callback) request.
+func validateState(req *http.Request, sess goth.Session) error {
+	rawAuthURL, err := sess.GetAuthURL()
+	if err != nil {
+		return err
+	}
+
+	authURL, err := url.Parse(rawAuthURL)
+	if err != nil {
+		return err
+	}
+
+	originalState := authURL.Query().Get("state")
+	if originalState != "" && (originalState != req.URL.Query().Get("state")) {
+		return errors.New("state token mismatch")
+	}
+	return nil
+}
+
+// Logout invalidates a user session.
+func Logout(res http.ResponseWriter, req *http.Request) error {
+	session, err := Store.Get(req, SessionName)
+	if err != nil {
+		return err
+	}
+	session.Options.MaxAge = -1
+	session.Values = make(map[interface{}]interface{})
+	err = session.Save(req, res)
+	if err != nil {
+		return errors.New("Could not delete user session ")
+	}
+	return nil
 }
 
 // GetProviderName is a function used to get the name of a provider
@@ -184,36 +246,96 @@ var CompleteUserAuth = func(res http.ResponseWriter, req *http.Request) (goth.Us
 var GetProviderName = getProviderName
 
 func getProviderName(req *http.Request) (string, error) {
-	provider := req.URL.Query().Get("provider")
-	if provider == "" {
-		if p, ok := mux.Vars(req)["provider"]; ok {
+
+	// get all the used providers
+	providers := goth.GetProviders()
+
+	// loop over the used providers, if we already have a valid session for any provider (ie. user is already logged-in with a provider), then return that provider name
+	for _, provider := range providers {
+		p := provider.Name()
+		session, _ := Store.Get(req, p+SessionName)
+		value := session.Values[p]
+		if _, ok := value.(string); ok {
 			return p, nil
 		}
 	}
-	if provider == "" {
-		provider = req.URL.Query().Get(":provider")
+
+	// try to get it from the url param "provider"
+	if p := req.URL.Query().Get("provider"); p != "" {
+		return p, nil
 	}
-	if provider == "" {
-		return provider, errors.New("you must select a provider")
+
+	// try to get it from the url param ":provider"
+	if p := req.URL.Query().Get(":provider"); p != "" {
+		return p, nil
 	}
-	return provider, nil
+
+	// try to get it from the context's value of "provider" key
+	if p, ok := mux.Vars(req)["provider"]; ok {
+		return p, nil
+	}
+
+	//  try to get it from the go-context's value of "provider" key
+	if p, ok := req.Context().Value("provider").(string); ok {
+		return p, nil
+	}
+
+	// if not found then return an empty string with the corresponding error
+	return "", errors.New("you must select a provider")
 }
 
 func storeInSession(key string, value string, req *http.Request, res http.ResponseWriter) error {
-	session, _ := Store.Get(req, key + SessionName)
+	session, _ := Store.Get(req, SessionName)
 
-	session.Values[key] = value
+	if err := updateSessionValue(session, key, value); err != nil {
+		return err
+	}
 
 	return session.Save(req, res)
 }
 
 func getFromSession(key string, req *http.Request) (string, error) {
-	session, _ := Store.Get(req, key + SessionName)
-
-	value := session.Values[key]
-	if value == nil {
+	session, _ := Store.Get(req, SessionName)
+	value, err := getSessionValue(session, key)
+	if err != nil {
 		return "", errors.New("could not find a matching session for this request")
 	}
 
-	return value.(string), nil
-}
\ No newline at end of file
+	return value, nil
+}
+
+func getSessionValue(session *sessions.Session, key string) (string, error) {
+	value := session.Values[key]
+	if value == nil {
+		return "", fmt.Errorf("could not find a matching session for this request")
+	}
+
+	rdata := strings.NewReader(value.(string))
+	r, err := gzip.NewReader(rdata)
+	if err != nil {
+		return "", err
+	}
+	s, err := ioutil.ReadAll(r)
+	if err != nil {
+		return "", err
+	}
+
+	return string(s), nil
+}
+
+func updateSessionValue(session *sessions.Session, key, value string) error {
+	var b bytes.Buffer
+	gz := gzip.NewWriter(&b)
+	if _, err := gz.Write([]byte(value)); err != nil {
+		return err
+	}
+	if err := gz.Flush(); err != nil {
+		return err
+	}
+	if err := gz.Close(); err != nil {
+		return err
+	}
+
+	session.Values[key] = b.String()
+	return nil
+}
diff --git a/vendor/github.com/markbates/goth/providers/bitbucket/bitbucket.go b/vendor/github.com/markbates/goth/providers/bitbucket/bitbucket.go
index 06d9c923cb..c19734770d 100644
--- a/vendor/github.com/markbates/goth/providers/bitbucket/bitbucket.go
+++ b/vendor/github.com/markbates/goth/providers/bitbucket/bitbucket.go
@@ -9,9 +9,9 @@ import (
 	"net/http"
 	"net/url"
 
+	"fmt"
 	"github.com/markbates/goth"
 	"golang.org/x/oauth2"
-	"fmt"
 )
 
 const (
@@ -26,10 +26,10 @@ const (
 // one manually.
 func New(clientKey, secret, callbackURL string, scopes ...string) *Provider {
 	p := &Provider{
-		ClientKey:           clientKey,
-		Secret:              secret,
-		CallbackURL:         callbackURL,
-		providerName:        "bitbucket",
+		ClientKey:    clientKey,
+		Secret:       secret,
+		CallbackURL:  callbackURL,
+		providerName: "bitbucket",
 	}
 	p.config = newConfig(p, scopes)
 	return p
@@ -125,7 +125,7 @@ func (p *Provider) FetchUser(session goth.Session) (goth.User, error) {
 
 func userFromReader(reader io.Reader, user *goth.User) error {
 	u := struct {
-		ID string `json:"uuid"`
+		ID    string `json:"uuid"`
 		Links struct {
 			Avatar struct {
 				URL string `json:"href"`
diff --git a/vendor/github.com/markbates/goth/providers/dropbox/dropbox.go b/vendor/github.com/markbates/goth/providers/dropbox/dropbox.go
index 61533d405e..262905806b 100644
--- a/vendor/github.com/markbates/goth/providers/dropbox/dropbox.go
+++ b/vendor/github.com/markbates/goth/providers/dropbox/dropbox.go
@@ -8,15 +8,16 @@ import (
 	"net/http"
 	"strings"
 
+	"fmt"
+
 	"github.com/markbates/goth"
 	"golang.org/x/oauth2"
-	"fmt"
 )
 
 const (
-	authURL    = "https://www.dropbox.com/1/oauth2/authorize"
-	tokenURL   = "https://api.dropbox.com/1/oauth2/token"
-	accountURL = "https://api.dropbox.com/1/account/info"
+	authURL    = "https://www.dropbox.com/oauth2/authorize"
+	tokenURL   = "https://api.dropbox.com/oauth2/token"
+	accountURL = "https://api.dropbox.com/2/users/get_current_account"
 )
 
 // Provider is the implementation of `goth.Provider` for accessing Dropbox.
@@ -40,10 +41,10 @@ type Session struct {
 // create one manually.
 func New(clientKey, secret, callbackURL string, scopes ...string) *Provider {
 	p := &Provider{
-		ClientKey:           clientKey,
-		Secret:              secret,
-		CallbackURL:         callbackURL,
-		providerName:        "dropbox",
+		ClientKey:    clientKey,
+		Secret:       secret,
+		CallbackURL:  callbackURL,
+		providerName: "dropbox",
 	}
 	p.config = newConfig(p, scopes)
 	return p
@@ -86,7 +87,7 @@ func (p *Provider) FetchUser(session goth.Session) (goth.User, error) {
 		return user, fmt.Errorf("%s cannot get user information without accessToken", p.providerName)
 	}
 
-	req, err := http.NewRequest("GET", accountURL, nil)
+	req, err := http.NewRequest("POST", accountURL, nil)
 	if err != nil {
 		return user, err
 	}
@@ -161,7 +162,7 @@ func newConfig(p *Provider, scopes []string) *oauth2.Config {
 
 func userFromReader(r io.Reader, user *goth.User) error {
 	u := struct {
-		Name string `json:"display_name"`
+		Name        string `json:"display_name"`
 		NameDetails struct {
 			NickName string `json:"familiar_name"`
 		} `json:"name_details"`
diff --git a/vendor/github.com/markbates/goth/providers/facebook/facebook.go b/vendor/github.com/markbates/goth/providers/facebook/facebook.go
index e0cfdf1e34..266bbe2208 100644
--- a/vendor/github.com/markbates/goth/providers/facebook/facebook.go
+++ b/vendor/github.com/markbates/goth/providers/facebook/facebook.go
@@ -11,12 +11,12 @@ import (
 	"net/http"
 	"net/url"
 
-	"github.com/markbates/goth"
-	"golang.org/x/oauth2"
-	"fmt"
 	"crypto/hmac"
 	"crypto/sha256"
 	"encoding/hex"
+	"fmt"
+	"github.com/markbates/goth"
+	"golang.org/x/oauth2"
 )
 
 const (
@@ -30,10 +30,10 @@ const (
 // one manually.
 func New(clientKey, secret, callbackURL string, scopes ...string) *Provider {
 	p := &Provider{
-		ClientKey:           clientKey,
-		Secret:              secret,
-		CallbackURL:         callbackURL,
-		providerName:        "facebook",
+		ClientKey:    clientKey,
+		Secret:       secret,
+		CallbackURL:  callbackURL,
+		providerName: "facebook",
 	}
 	p.config = newConfig(p, scopes)
 	return p
@@ -129,7 +129,7 @@ func userFromReader(reader io.Reader, user *goth.User) error {
 		FirstName string `json:"first_name"`
 		LastName  string `json:"last_name"`
 		Link      string `json:"link"`
-		Picture struct {
+		Picture   struct {
 			Data struct {
 				URL string `json:"url"`
 			} `json:"data"`
diff --git a/vendor/github.com/markbates/goth/providers/gitlab/gitlab.go b/vendor/github.com/markbates/goth/providers/gitlab/gitlab.go
index fe188c01a9..533632e7fd 100644
--- a/vendor/github.com/markbates/goth/providers/gitlab/gitlab.go
+++ b/vendor/github.com/markbates/goth/providers/gitlab/gitlab.go
@@ -11,9 +11,9 @@ import (
 	"net/url"
 	"strconv"
 
+	"fmt"
 	"github.com/markbates/goth"
 	"golang.org/x/oauth2"
-	"fmt"
 )
 
 // These vars define the Authentication, Token, and Profile URLS for Gitlab. If
diff --git a/vendor/github.com/markbates/goth/providers/gplus/gplus.go b/vendor/github.com/markbates/goth/providers/gplus/gplus.go
index 06655c2f7f..4726aa349e 100644
--- a/vendor/github.com/markbates/goth/providers/gplus/gplus.go
+++ b/vendor/github.com/markbates/goth/providers/gplus/gplus.go
@@ -11,9 +11,9 @@ import (
 	"net/url"
 	"strings"
 
+	"fmt"
 	"github.com/markbates/goth"
 	"golang.org/x/oauth2"
-	"fmt"
 )
 
 const (
@@ -27,10 +27,10 @@ const (
 // one manually.
 func New(clientKey, secret, callbackURL string, scopes ...string) *Provider {
 	p := &Provider{
-		ClientKey:           clientKey,
-		Secret:              secret,
-		CallbackURL:         callbackURL,
-		providerName:        "gplus",
+		ClientKey:    clientKey,
+		Secret:       secret,
+		CallbackURL:  callbackURL,
+		providerName: "gplus",
 	}
 	p.config = newConfig(p, scopes)
 	return p
diff --git a/vendor/github.com/markbates/goth/providers/openidConnect/openidConnect.go b/vendor/github.com/markbates/goth/providers/openidConnect/openidConnect.go
index 7ffd11c607..44419ba15f 100644
--- a/vendor/github.com/markbates/goth/providers/openidConnect/openidConnect.go
+++ b/vendor/github.com/markbates/goth/providers/openidConnect/openidConnect.go
@@ -1,17 +1,17 @@
 package openidConnect
 
 import (
+	"bytes"
+	"encoding/base64"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"github.com/markbates/goth"
+	"golang.org/x/oauth2"
+	"io/ioutil"
 	"net/http"
 	"strings"
-	"fmt"
-	"encoding/json"
-	"encoding/base64"
-	"io/ioutil"
-	"errors"
-	"golang.org/x/oauth2"
-	"github.com/markbates/goth"
 	"time"
-	"bytes"
 )
 
 const (
@@ -89,14 +89,14 @@ func New(clientKey, secret, callbackURL, openIDAutoDiscoveryURL string, scopes .
 		Secret:      secret,
 		CallbackURL: callbackURL,
 
-		UserIdClaims:   []string{subjectClaim},
-		NameClaims:     []string{NameClaim},
-		NickNameClaims: []string{NicknameClaim, PreferredUsernameClaim},
-		EmailClaims:    []string{EmailClaim},
-		AvatarURLClaims:[]string{PictureClaim},
-		FirstNameClaims:[]string{GivenNameClaim},
-		LastNameClaims: []string{FamilyNameClaim},
-		LocationClaims: []string{AddressClaim},
+		UserIdClaims:    []string{subjectClaim},
+		NameClaims:      []string{NameClaim},
+		NickNameClaims:  []string{NicknameClaim, PreferredUsernameClaim},
+		EmailClaims:     []string{EmailClaim},
+		AvatarURLClaims: []string{PictureClaim},
+		FirstNameClaims: []string{GivenNameClaim},
+		LastNameClaims:  []string{FamilyNameClaim},
+		LocationClaims:  []string{AddressClaim},
 
 		providerName: "openid-connect",
 	}
diff --git a/vendor/github.com/markbates/goth/providers/openidConnect/session.go b/vendor/github.com/markbates/goth/providers/openidConnect/session.go
index a34584fdef..d223cf8752 100644
--- a/vendor/github.com/markbates/goth/providers/openidConnect/session.go
+++ b/vendor/github.com/markbates/goth/providers/openidConnect/session.go
@@ -1,12 +1,12 @@
 package openidConnect
 
 import (
+	"encoding/json"
 	"errors"
 	"github.com/markbates/goth"
-	"encoding/json"
+	"golang.org/x/oauth2"
 	"strings"
 	"time"
-	"golang.org/x/oauth2"
 )
 
 // Session stores data during the auth process with the OpenID Connect provider.
diff --git a/vendor/github.com/markbates/goth/providers/twitter/twitter.go b/vendor/github.com/markbates/goth/providers/twitter/twitter.go
index 3703f21974..2ce20e780e 100644
--- a/vendor/github.com/markbates/goth/providers/twitter/twitter.go
+++ b/vendor/github.com/markbates/goth/providers/twitter/twitter.go
@@ -9,10 +9,11 @@ import (
 	"io/ioutil"
 	"net/http"
 
+	"fmt"
+
 	"github.com/markbates/goth"
 	"github.com/mrjones/oauth"
 	"golang.org/x/oauth2"
-	"fmt"
 )
 
 var (
@@ -30,10 +31,10 @@ var (
 // If you'd like to use authenticate instead of authorize, use NewAuthenticate instead.
 func New(clientKey, secret, callbackURL string) *Provider {
 	p := &Provider{
-		ClientKey:           clientKey,
-		Secret:              secret,
-		CallbackURL:         callbackURL,
-		providerName:        "twitter",
+		ClientKey:    clientKey,
+		Secret:       secret,
+		CallbackURL:  callbackURL,
+		providerName: "twitter",
 	}
 	p.consumer = newConsumer(p, authorizeURL)
 	return p
@@ -43,10 +44,10 @@ func New(clientKey, secret, callbackURL string) *Provider {
 // NewAuthenticate uses the authenticate URL instead of the authorize URL.
 func NewAuthenticate(clientKey, secret, callbackURL string) *Provider {
 	p := &Provider{
-		ClientKey:           clientKey,
-		Secret:              secret,
-		CallbackURL:         callbackURL,
-		providerName:        "twitter",
+		ClientKey:    clientKey,
+		Secret:       secret,
+		CallbackURL:  callbackURL,
+		providerName: "twitter",
 	}
 	p.consumer = newConsumer(p, authenticateURL)
 	return p
@@ -107,7 +108,7 @@ func (p *Provider) FetchUser(session goth.Session) (goth.User, error) {
 
 	response, err := p.consumer.Get(
 		endpointProfile,
-		map[string]string{"include_entities": "false", "skip_status": "true"},
+		map[string]string{"include_entities": "false", "skip_status": "true", "include_email": "true"},
 		sess.AccessToken)
 	if err != nil {
 		return user, err
@@ -126,6 +127,9 @@ func (p *Provider) FetchUser(session goth.Session) (goth.User, error) {
 
 	user.Name = user.RawData["name"].(string)
 	user.NickName = user.RawData["screen_name"].(string)
+	if user.RawData["email"] != nil {
+		user.Email = user.RawData["email"].(string)
+	}
 	user.Description = user.RawData["description"].(string)
 	user.AvatarURL = user.RawData["profile_image_url"].(string)
 	user.UserID = user.RawData["id_str"].(string)
diff --git a/vendor/vendor.json b/vendor/vendor.json
index 7397cac40a..c6a10a585b 100644
--- a/vendor/vendor.json
+++ b/vendor/vendor.json
@@ -666,64 +666,64 @@
 			"revisionTime": "2017-10-25T03:15:54Z"
 		},
 		{
-			"checksumSHA1": "O3KUfEXQPfdQ+tCMpP2RAIRJJqY=",
+			"checksumSHA1": "q9MD1ienC+kmKq5i51oAktQEV1E=",
 			"path": "github.com/markbates/goth",
-			"revision": "90362394a367f9d77730911973462a53d69662ba",
-			"revisionTime": "2017-02-23T14:12:10Z"
+			"revision": "bc7deaf077a50416cf3a23aa5903d2a7b5a30ada",
+			"revisionTime": "2018-02-15T02:27:40Z"
 		},
 		{
-			"checksumSHA1": "MkFKwLV3icyUo4oP0BgEs+7+R1Y=",
+			"checksumSHA1": "+nosptSgGb2qCAR6CSHV2avwmNg=",
 			"path": "github.com/markbates/goth/gothic",
-			"revision": "90362394a367f9d77730911973462a53d69662ba",
-			"revisionTime": "2017-02-23T14:12:10Z"
+			"revision": "bc7deaf077a50416cf3a23aa5903d2a7b5a30ada",
+			"revisionTime": "2018-02-15T02:27:40Z"
 		},
 		{
-			"checksumSHA1": "crNSlQADjX6hcxykON2tFCqY4iw=",
+			"checksumSHA1": "pJ+Cws/TU22K6tZ/ALFOvvH1K5U=",
 			"path": "github.com/markbates/goth/providers/bitbucket",
-			"revision": "90362394a367f9d77730911973462a53d69662ba",
-			"revisionTime": "2017-02-23T14:12:10Z"
+			"revision": "bc7deaf077a50416cf3a23aa5903d2a7b5a30ada",
+			"revisionTime": "2018-02-15T02:27:40Z"
 		},
 		{
-			"checksumSHA1": "1Kp4DKkJNVn135Xg8H4a6CFBNy8=",
+			"checksumSHA1": "bKokLof0Pkk5nEhW8NdbfcVzuqk=",
 			"path": "github.com/markbates/goth/providers/dropbox",
-			"revision": "90362394a367f9d77730911973462a53d69662ba",
-			"revisionTime": "2017-02-23T14:12:10Z"
+			"revision": "bc7deaf077a50416cf3a23aa5903d2a7b5a30ada",
+			"revisionTime": "2018-02-15T02:27:40Z"
 		},
 		{
-			"checksumSHA1": "cGs1da29iOBJh5EAH0icKDbN8CA=",
+			"checksumSHA1": "VzbroIA9R00Ig3iGnOlZLU7d4ls=",
 			"path": "github.com/markbates/goth/providers/facebook",
-			"revision": "90362394a367f9d77730911973462a53d69662ba",
-			"revisionTime": "2017-02-23T14:12:10Z"
+			"revision": "bc7deaf077a50416cf3a23aa5903d2a7b5a30ada",
+			"revisionTime": "2018-02-15T02:27:40Z"
 		},
 		{
 			"checksumSHA1": "P6nBZ850aaekpOcoXNdRhK86bH8=",
 			"path": "github.com/markbates/goth/providers/github",
-			"revision": "90362394a367f9d77730911973462a53d69662ba",
-			"revisionTime": "2017-02-23T14:12:10Z"
+			"revision": "bc7deaf077a50416cf3a23aa5903d2a7b5a30ada",
+			"revisionTime": "2018-02-15T02:27:40Z"
 		},
 		{
-			"checksumSHA1": "o/109paSRy9HqV87gR4zUZMMSzs=",
+			"checksumSHA1": "ld488t+yGoTwtmiCSSggEX4fxVk=",
 			"path": "github.com/markbates/goth/providers/gitlab",
-			"revision": "90362394a367f9d77730911973462a53d69662ba",
-			"revisionTime": "2017-02-23T14:12:10Z"
+			"revision": "bc7deaf077a50416cf3a23aa5903d2a7b5a30ada",
+			"revisionTime": "2018-02-15T02:27:40Z"
 		},
 		{
-			"checksumSHA1": "cX6kR9y94BWFZvI/7UFrsFsP3FQ=",
+			"checksumSHA1": "qXEulD7vnwY9hFrxh91Pm5YrvTM=",
 			"path": "github.com/markbates/goth/providers/gplus",
-			"revision": "90362394a367f9d77730911973462a53d69662ba",
-			"revisionTime": "2017-02-23T14:12:10Z"
+			"revision": "bc7deaf077a50416cf3a23aa5903d2a7b5a30ada",
+			"revisionTime": "2018-02-15T02:27:40Z"
 		},
 		{
-			"checksumSHA1": "sMYKhqAUZXM1+T/TjlMhWh8Vveo=",
+			"checksumSHA1": "wsOBzyp4LKDhfCPmX1LLP7T0S3U=",
 			"path": "github.com/markbates/goth/providers/openidConnect",
-			"revision": "90362394a367f9d77730911973462a53d69662ba",
-			"revisionTime": "2017-02-23T14:12:10Z"
+			"revision": "bc7deaf077a50416cf3a23aa5903d2a7b5a30ada",
+			"revisionTime": "2018-02-15T02:27:40Z"
 		},
 		{
-			"checksumSHA1": "1w0V6jYXaGlEtZcMeYTOAAucvgw=",
+			"checksumSHA1": "o6RqMbbE8QNZhNT9TsAIRMPI8tg=",
 			"path": "github.com/markbates/goth/providers/twitter",
-			"revision": "90362394a367f9d77730911973462a53d69662ba",
-			"revisionTime": "2017-02-23T14:12:10Z"
+			"revision": "bc7deaf077a50416cf3a23aa5903d2a7b5a30ada",
+			"revisionTime": "2018-02-15T02:27:40Z"
 		},
 		{
 			"checksumSHA1": "61HNjGetaBoMp8HBOpuEZRSim8g=",