summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/markbates/goth/providers
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/markbates/goth/providers')
-rw-r--r--vendor/github.com/markbates/goth/providers/bitbucket/bitbucket.go206
-rw-r--r--vendor/github.com/markbates/goth/providers/bitbucket/session.go61
-rw-r--r--vendor/github.com/markbates/goth/providers/dropbox/dropbox.go191
-rw-r--r--vendor/github.com/markbates/goth/providers/facebook/facebook.go195
-rw-r--r--vendor/github.com/markbates/goth/providers/facebook/session.go59
-rw-r--r--vendor/github.com/markbates/goth/providers/github/github.go31
-rw-r--r--vendor/github.com/markbates/goth/providers/gitlab/gitlab.go187
-rw-r--r--vendor/github.com/markbates/goth/providers/gitlab/session.go63
-rw-r--r--vendor/github.com/markbates/goth/providers/gplus/gplus.go195
-rw-r--r--vendor/github.com/markbates/goth/providers/gplus/session.go61
-rw-r--r--vendor/github.com/markbates/goth/providers/openidConnect/openidConnect.go384
-rw-r--r--vendor/github.com/markbates/goth/providers/openidConnect/session.go63
-rw-r--r--vendor/github.com/markbates/goth/providers/twitter/session.go54
-rw-r--r--vendor/github.com/markbates/goth/providers/twitter/twitter.go160
14 files changed, 1899 insertions, 11 deletions
diff --git a/vendor/github.com/markbates/goth/providers/bitbucket/bitbucket.go b/vendor/github.com/markbates/goth/providers/bitbucket/bitbucket.go
new file mode 100644
index 0000000000..06d9c923cb
--- /dev/null
+++ b/vendor/github.com/markbates/goth/providers/bitbucket/bitbucket.go
@@ -0,0 +1,206 @@
+// Package bitbucket implements the OAuth2 protocol for authenticating users through Bitbucket.
+package bitbucket
+
+import (
+ "bytes"
+ "encoding/json"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "net/url"
+
+ "github.com/markbates/goth"
+ "golang.org/x/oauth2"
+ "fmt"
+)
+
+const (
+ authURL string = "https://bitbucket.org/site/oauth2/authorize"
+ tokenURL string = "https://bitbucket.org/site/oauth2/access_token"
+ endpointProfile string = "https://api.bitbucket.org/2.0/user"
+ endpointEmail string = "https://api.bitbucket.org/2.0/user/emails"
+)
+
+// New creates a new Bitbucket provider, and sets up important connection details.
+// You should always call `bitbucket.New` to get a new Provider. Never try to create
+// one manually.
+func New(clientKey, secret, callbackURL string, scopes ...string) *Provider {
+ p := &Provider{
+ ClientKey: clientKey,
+ Secret: secret,
+ CallbackURL: callbackURL,
+ providerName: "bitbucket",
+ }
+ p.config = newConfig(p, scopes)
+ return p
+}
+
+// Provider is the implementation of `goth.Provider` for accessing Bitbucket.
+type Provider struct {
+ ClientKey string
+ Secret string
+ CallbackURL string
+ HTTPClient *http.Client
+ config *oauth2.Config
+ providerName string
+}
+
+// Name is the name used to retrieve this provider later.
+func (p *Provider) Name() string {
+ return p.providerName
+}
+
+// SetName is to update the name of the provider (needed in case of multiple providers of 1 type)
+func (p *Provider) SetName(name string) {
+ p.providerName = name
+}
+
+func (p *Provider) Client() *http.Client {
+ return goth.HTTPClientWithFallBack(p.HTTPClient)
+}
+
+// Debug is a no-op for the bitbucket package.
+func (p *Provider) Debug(debug bool) {}
+
+// BeginAuth asks Bitbucket for an authentication end-point.
+func (p *Provider) BeginAuth(state string) (goth.Session, error) {
+ url := p.config.AuthCodeURL(state)
+ session := &Session{
+ AuthURL: url,
+ }
+ return session, nil
+}
+
+// FetchUser will go to Bitbucket and access basic information about the user.
+func (p *Provider) FetchUser(session goth.Session) (goth.User, error) {
+ sess := session.(*Session)
+ user := goth.User{
+ AccessToken: sess.AccessToken,
+ Provider: p.Name(),
+ RefreshToken: sess.RefreshToken,
+ ExpiresAt: sess.ExpiresAt,
+ }
+
+ if user.AccessToken == "" {
+ // data is not yet retrieved since accessToken is still empty
+ return user, fmt.Errorf("%s cannot get user information without accessToken", p.providerName)
+ }
+
+ response, err := goth.HTTPClientWithFallBack(p.Client()).Get(endpointProfile + "?access_token=" + url.QueryEscape(sess.AccessToken))
+ if err != nil {
+ return user, err
+ }
+ defer response.Body.Close()
+
+ if response.StatusCode != http.StatusOK {
+ return user, fmt.Errorf("%s responded with a %d trying to fetch user information", p.providerName, response.StatusCode)
+ }
+
+ bits, err := ioutil.ReadAll(response.Body)
+ if err != nil {
+ return user, err
+ }
+
+ err = json.NewDecoder(bytes.NewReader(bits)).Decode(&user.RawData)
+ if err != nil {
+ return user, err
+ }
+
+ err = userFromReader(bytes.NewReader(bits), &user)
+
+ response, err = goth.HTTPClientWithFallBack(p.Client()).Get(endpointEmail + "?access_token=" + url.QueryEscape(sess.AccessToken))
+ if err != nil {
+ return user, err
+ }
+ defer response.Body.Close()
+
+ bits, err = ioutil.ReadAll(response.Body)
+ if err != nil {
+ return user, err
+ }
+
+ err = emailFromReader(bytes.NewReader(bits), &user)
+ return user, err
+}
+
+func userFromReader(reader io.Reader, user *goth.User) error {
+ u := struct {
+ ID string `json:"uuid"`
+ Links struct {
+ Avatar struct {
+ URL string `json:"href"`
+ } `json:"avatar"`
+ } `json:"links"`
+ Email string `json:"email"`
+ Username string `json:"username"`
+ Name string `json:"display_name"`
+ Location string `json:"location"`
+ }{}
+
+ err := json.NewDecoder(reader).Decode(&u)
+ if err != nil {
+ return err
+ }
+
+ user.Name = u.Name
+ user.NickName = u.Username
+ user.AvatarURL = u.Links.Avatar.URL
+ user.UserID = u.ID
+ user.Location = u.Location
+
+ return err
+}
+
+func emailFromReader(reader io.Reader, user *goth.User) error {
+ e := struct {
+ Values []struct {
+ Email string `json:"email"`
+ } `json:"values"`
+ }{}
+
+ err := json.NewDecoder(reader).Decode(&e)
+ if err != nil {
+ return err
+ }
+
+ if len(e.Values) > 0 {
+ user.Email = e.Values[0].Email
+ }
+
+ return err
+}
+
+func newConfig(provider *Provider, scopes []string) *oauth2.Config {
+ c := &oauth2.Config{
+ ClientID: provider.ClientKey,
+ ClientSecret: provider.Secret,
+ RedirectURL: provider.CallbackURL,
+ Endpoint: oauth2.Endpoint{
+ AuthURL: authURL,
+ TokenURL: tokenURL,
+ },
+ Scopes: []string{},
+ }
+
+ for _, scope := range scopes {
+ c.Scopes = append(c.Scopes, scope)
+ }
+
+ return c
+}
+
+//RefreshTokenAvailable refresh token is provided by auth provider or not
+func (p *Provider) RefreshTokenAvailable() bool {
+ return true
+}
+
+//RefreshToken get new access token based on the refresh token
+func (p *Provider) RefreshToken(refreshToken string) (*oauth2.Token, error) {
+ token := &oauth2.Token{RefreshToken: refreshToken}
+ ts := p.config.TokenSource(goth.ContextForClient(p.Client()), token)
+ newToken, err := ts.Token()
+ if err != nil {
+ return nil, err
+ }
+ return newToken, err
+}
diff --git a/vendor/github.com/markbates/goth/providers/bitbucket/session.go b/vendor/github.com/markbates/goth/providers/bitbucket/session.go
new file mode 100644
index 0000000000..a65242151b
--- /dev/null
+++ b/vendor/github.com/markbates/goth/providers/bitbucket/session.go
@@ -0,0 +1,61 @@
+package bitbucket
+
+import (
+ "encoding/json"
+ "errors"
+ "strings"
+ "time"
+
+ "github.com/markbates/goth"
+)
+
+// Session stores data during the auth process with Bitbucket.
+type Session struct {
+ AuthURL string
+ AccessToken string
+ RefreshToken string
+ ExpiresAt time.Time
+}
+
+// GetAuthURL will return the URL set by calling the `BeginAuth` function on the Bitbucket provider.
+func (s Session) GetAuthURL() (string, error) {
+ if s.AuthURL == "" {
+ return "", errors.New(goth.NoAuthUrlErrorMessage)
+ }
+ return s.AuthURL, nil
+}
+
+// Authorize the session with Bitbucket and return the access token to be stored for future use.
+func (s *Session) Authorize(provider goth.Provider, params goth.Params) (string, error) {
+ p := provider.(*Provider)
+ token, err := p.config.Exchange(goth.ContextForClient(p.Client()), params.Get("code"))
+ if err != nil {
+ return "", err
+ }
+
+ if !token.Valid() {
+ return "", errors.New("Invalid token received from provider")
+ }
+
+ s.AccessToken = token.AccessToken
+ s.RefreshToken = token.RefreshToken
+ s.ExpiresAt = token.Expiry
+ return token.AccessToken, err
+}
+
+// Marshal the session into a string
+func (s Session) Marshal() string {
+ b, _ := json.Marshal(s)
+ return string(b)
+}
+
+// UnmarshalSession will unmarshal a JSON string into a session.
+func (p *Provider) UnmarshalSession(data string) (goth.Session, error) {
+ sess := &Session{}
+ err := json.NewDecoder(strings.NewReader(data)).Decode(sess)
+ return sess, err
+}
+
+func (s Session) String() string {
+ return s.Marshal()
+}
diff --git a/vendor/github.com/markbates/goth/providers/dropbox/dropbox.go b/vendor/github.com/markbates/goth/providers/dropbox/dropbox.go
new file mode 100644
index 0000000000..61533d405e
--- /dev/null
+++ b/vendor/github.com/markbates/goth/providers/dropbox/dropbox.go
@@ -0,0 +1,191 @@
+// Package dropbox implements the OAuth2 protocol for authenticating users through Dropbox.
+package dropbox
+
+import (
+ "encoding/json"
+ "errors"
+ "io"
+ "net/http"
+ "strings"
+
+ "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"
+)
+
+// Provider is the implementation of `goth.Provider` for accessing Dropbox.
+type Provider struct {
+ ClientKey string
+ Secret string
+ CallbackURL string
+ HTTPClient *http.Client
+ config *oauth2.Config
+ providerName string
+}
+
+// Session stores data during the auth process with Dropbox.
+type Session struct {
+ AuthURL string
+ Token string
+}
+
+// New creates a new Dropbox provider and sets up important connection details.
+// You should always call `dropbox.New` to get a new provider. Never try to
+// create one manually.
+func New(clientKey, secret, callbackURL string, scopes ...string) *Provider {
+ p := &Provider{
+ ClientKey: clientKey,
+ Secret: secret,
+ CallbackURL: callbackURL,
+ providerName: "dropbox",
+ }
+ p.config = newConfig(p, scopes)
+ return p
+}
+
+// Name is the name used to retrieve this provider later.
+func (p *Provider) Name() string {
+ return p.providerName
+}
+
+// SetName is to update the name of the provider (needed in case of multiple providers of 1 type)
+func (p *Provider) SetName(name string) {
+ p.providerName = name
+}
+
+func (p *Provider) Client() *http.Client {
+ return goth.HTTPClientWithFallBack(p.HTTPClient)
+}
+
+// Debug is a no-op for the dropbox package.
+func (p *Provider) Debug(debug bool) {}
+
+// BeginAuth asks Dropbox for an authentication end-point.
+func (p *Provider) BeginAuth(state string) (goth.Session, error) {
+ return &Session{
+ AuthURL: p.config.AuthCodeURL(state),
+ }, nil
+}
+
+// FetchUser will go to Dropbox and access basic information about the user.
+func (p *Provider) FetchUser(session goth.Session) (goth.User, error) {
+ s := session.(*Session)
+ user := goth.User{
+ AccessToken: s.Token,
+ Provider: p.Name(),
+ }
+
+ if user.AccessToken == "" {
+ // data is not yet retrieved since accessToken is still empty
+ return user, fmt.Errorf("%s cannot get user information without accessToken", p.providerName)
+ }
+
+ req, err := http.NewRequest("GET", accountURL, nil)
+ if err != nil {
+ return user, err
+ }
+ req.Header.Set("Authorization", "Bearer "+s.Token)
+ resp, err := p.Client().Do(req)
+ if err != nil {
+ return user, err
+ }
+ defer resp.Body.Close()
+
+ if resp.StatusCode != http.StatusOK {
+ return user, fmt.Errorf("%s responded with a %d trying to fetch user information", p.providerName, resp.StatusCode)
+ }
+
+ err = userFromReader(resp.Body, &user)
+ return user, err
+}
+
+// UnmarshalSession wil unmarshal a JSON string into a session.
+func (p *Provider) UnmarshalSession(data string) (goth.Session, error) {
+ s := &Session{}
+ err := json.NewDecoder(strings.NewReader(data)).Decode(s)
+ return s, err
+}
+
+// GetAuthURL gets the URL set by calling the `BeginAuth` function on the Dropbox provider.
+func (s *Session) GetAuthURL() (string, error) {
+ if s.AuthURL == "" {
+ return "", errors.New("dropbox: missing AuthURL")
+ }
+ return s.AuthURL, nil
+}
+
+// Authorize the session with Dropbox and return the access token to be stored for future use.
+func (s *Session) Authorize(provider goth.Provider, params goth.Params) (string, error) {
+ p := provider.(*Provider)
+ token, err := p.config.Exchange(goth.ContextForClient(p.Client()), params.Get("code"))
+ if err != nil {
+ return "", err
+ }
+
+ if !token.Valid() {
+ return "", errors.New("Invalid token received from provider")
+ }
+
+ s.Token = token.AccessToken
+ return token.AccessToken, nil
+}
+
+// Marshal the session into a string
+func (s *Session) Marshal() string {
+ b, _ := json.Marshal(s)
+ return string(b)
+}
+
+func (s Session) String() string {
+ return s.Marshal()
+}
+
+func newConfig(p *Provider, scopes []string) *oauth2.Config {
+ c := &oauth2.Config{
+ ClientID: p.ClientKey,
+ ClientSecret: p.Secret,
+ RedirectURL: p.CallbackURL,
+ Endpoint: oauth2.Endpoint{
+ AuthURL: authURL,
+ TokenURL: tokenURL,
+ },
+ }
+ return c
+}
+
+func userFromReader(r io.Reader, user *goth.User) error {
+ u := struct {
+ Name string `json:"display_name"`
+ NameDetails struct {
+ NickName string `json:"familiar_name"`
+ } `json:"name_details"`
+ Location string `json:"country"`
+ Email string `json:"email"`
+ }{}
+ err := json.NewDecoder(r).Decode(&u)
+ if err != nil {
+ return err
+ }
+ user.Email = u.Email
+ user.Name = u.Name
+ user.NickName = u.NameDetails.NickName
+ user.UserID = u.Email // Dropbox doesn't provide a separate user ID
+ user.Location = u.Location
+ return nil
+}
+
+//RefreshToken refresh token is not provided by dropbox
+func (p *Provider) RefreshToken(refreshToken string) (*oauth2.Token, error) {
+ return nil, errors.New("Refresh token is not provided by dropbox")
+}
+
+//RefreshTokenAvailable refresh token is not provided by dropbox
+func (p *Provider) RefreshTokenAvailable() bool {
+ return false
+}
diff --git a/vendor/github.com/markbates/goth/providers/facebook/facebook.go b/vendor/github.com/markbates/goth/providers/facebook/facebook.go
new file mode 100644
index 0000000000..e0cfdf1e34
--- /dev/null
+++ b/vendor/github.com/markbates/goth/providers/facebook/facebook.go
@@ -0,0 +1,195 @@
+// Package facebook implements the OAuth2 protocol for authenticating users through Facebook.
+// This package can be used as a reference implementation of an OAuth2 provider for Goth.
+package facebook
+
+import (
+ "bytes"
+ "encoding/json"
+ "errors"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "net/url"
+
+ "github.com/markbates/goth"
+ "golang.org/x/oauth2"
+ "fmt"
+ "crypto/hmac"
+ "crypto/sha256"
+ "encoding/hex"
+)
+
+const (
+ authURL string = "https://www.facebook.com/dialog/oauth"
+ tokenURL string = "https://graph.facebook.com/oauth/access_token"
+ endpointProfile string = "https://graph.facebook.com/me?fields=email,first_name,last_name,link,about,id,name,picture,location"
+)
+
+// New creates a new Facebook provider, and sets up important connection details.
+// You should always call `facebook.New` to get a new Provider. Never try to create
+// one manually.
+func New(clientKey, secret, callbackURL string, scopes ...string) *Provider {
+ p := &Provider{
+ ClientKey: clientKey,
+ Secret: secret,
+ CallbackURL: callbackURL,
+ providerName: "facebook",
+ }
+ p.config = newConfig(p, scopes)
+ return p
+}
+
+// Provider is the implementation of `goth.Provider` for accessing Facebook.
+type Provider struct {
+ ClientKey string
+ Secret string
+ CallbackURL string
+ HTTPClient *http.Client
+ config *oauth2.Config
+ providerName string
+}
+
+// Name is the name used to retrieve this provider later.
+func (p *Provider) Name() string {
+ return p.providerName
+}
+
+// SetName is to update the name of the provider (needed in case of multiple providers of 1 type)
+func (p *Provider) SetName(name string) {
+ p.providerName = name
+}
+
+func (p *Provider) Client() *http.Client {
+ return goth.HTTPClientWithFallBack(p.HTTPClient)
+}
+
+// Debug is a no-op for the facebook package.
+func (p *Provider) Debug(debug bool) {}
+
+// BeginAuth asks Facebook for an authentication end-point.
+func (p *Provider) BeginAuth(state string) (goth.Session, error) {
+ url := p.config.AuthCodeURL(state)
+ session := &Session{
+ AuthURL: url,
+ }
+ return session, nil
+}
+
+// FetchUser will go to Facebook and access basic information about the user.
+func (p *Provider) FetchUser(session goth.Session) (goth.User, error) {
+ sess := session.(*Session)
+ user := goth.User{
+ AccessToken: sess.AccessToken,
+ Provider: p.Name(),
+ ExpiresAt: sess.ExpiresAt,
+ }
+
+ if user.AccessToken == "" {
+ // data is not yet retrieved since accessToken is still empty
+ return user, fmt.Errorf("%s cannot get user information without accessToken", p.providerName)
+ }
+
+ // always add appsecretProof to make calls more protected
+ // https://github.com/markbates/goth/issues/96
+ // https://developers.facebook.com/docs/graph-api/securing-requests
+ hash := hmac.New(sha256.New, []byte(p.Secret))
+ hash.Write([]byte(sess.AccessToken))
+ appsecretProof := hex.EncodeToString(hash.Sum(nil))
+
+ response, err := p.Client().Get(endpointProfile + "&access_token=" + url.QueryEscape(sess.AccessToken) + "&appsecret_proof=" + appsecretProof)
+ if err != nil {
+ return user, err
+ }
+ defer response.Body.Close()
+
+ if response.StatusCode != http.StatusOK {
+ return user, fmt.Errorf("%s responded with a %d trying to fetch user information", p.providerName, response.StatusCode)
+ }
+
+ bits, err := ioutil.ReadAll(response.Body)
+ if err != nil {
+ return user, err
+ }
+
+ err = json.NewDecoder(bytes.NewReader(bits)).Decode(&user.RawData)
+ if err != nil {
+ return user, err
+ }
+
+ err = userFromReader(bytes.NewReader(bits), &user)
+ return user, err
+}
+
+func userFromReader(reader io.Reader, user *goth.User) error {
+ u := struct {
+ ID string `json:"id"`
+ Email string `json:"email"`
+ About string `json:"about"`
+ Name string `json:"name"`
+ FirstName string `json:"first_name"`
+ LastName string `json:"last_name"`
+ Link string `json:"link"`
+ Picture struct {
+ Data struct {
+ URL string `json:"url"`
+ } `json:"data"`
+ } `json:"picture"`
+ Location struct {
+ Name string `json:"name"`
+ } `json:"location"`
+ }{}
+
+ err := json.NewDecoder(reader).Decode(&u)
+ if err != nil {
+ return err
+ }
+
+ user.Name = u.Name
+ user.FirstName = u.FirstName
+ user.LastName = u.LastName
+ user.NickName = u.Name
+ user.Email = u.Email
+ user.Description = u.About
+ user.AvatarURL = u.Picture.Data.URL
+ user.UserID = u.ID
+ user.Location = u.Location.Name
+
+ return err
+}
+
+func newConfig(provider *Provider, scopes []string) *oauth2.Config {
+ c := &oauth2.Config{
+ ClientID: provider.ClientKey,
+ ClientSecret: provider.Secret,
+ RedirectURL: provider.CallbackURL,
+ Endpoint: oauth2.Endpoint{
+ AuthURL: authURL,
+ TokenURL: tokenURL,
+ },
+ Scopes: []string{
+ "email",
+ },
+ }
+
+ defaultScopes := map[string]struct{}{
+ "email": {},
+ }
+
+ for _, scope := range scopes {
+ if _, exists := defaultScopes[scope]; !exists {
+ c.Scopes = append(c.Scopes, scope)
+ }
+ }
+
+ return c
+}
+
+//RefreshToken refresh token is not provided by facebook
+func (p *Provider) RefreshToken(refreshToken string) (*oauth2.Token, error) {
+ return nil, errors.New("Refresh token is not provided by facebook")
+}
+
+//RefreshTokenAvailable refresh token is not provided by facebook
+func (p *Provider) RefreshTokenAvailable() bool {
+ return false
+}
diff --git a/vendor/github.com/markbates/goth/providers/facebook/session.go b/vendor/github.com/markbates/goth/providers/facebook/session.go
new file mode 100644
index 0000000000..5cdcca443a
--- /dev/null
+++ b/vendor/github.com/markbates/goth/providers/facebook/session.go
@@ -0,0 +1,59 @@
+package facebook
+
+import (
+ "encoding/json"
+ "errors"
+ "strings"
+ "time"
+
+ "github.com/markbates/goth"
+)
+
+// Session stores data during the auth process with Facebook.
+type Session struct {
+ AuthURL string
+ AccessToken string
+ ExpiresAt time.Time
+}
+
+// GetAuthURL will return the URL set by calling the `BeginAuth` function on the Facebook provider.
+func (s Session) GetAuthURL() (string, error) {
+ if s.AuthURL == "" {
+ return "", errors.New(goth.NoAuthUrlErrorMessage)
+ }
+ return s.AuthURL, nil
+}
+
+// Authorize the session with Facebook and return the access token to be stored for future use.
+func (s *Session) Authorize(provider goth.Provider, params goth.Params) (string, error) {
+ p := provider.(*Provider)
+ token, err := p.config.Exchange(goth.ContextForClient(p.Client()), params.Get("code"))
+ if err != nil {
+ return "", err
+ }
+
+ if !token.Valid() {
+ return "", errors.New("Invalid token received from provider")
+ }
+
+ s.AccessToken = token.AccessToken
+ s.ExpiresAt = token.Expiry
+ return token.AccessToken, err
+}
+
+// Marshal the session into a string
+func (s Session) Marshal() string {
+ b, _ := json.Marshal(s)
+ return string(b)
+}
+
+func (s Session) String() string {
+ return s.Marshal()
+}
+
+// UnmarshalSession will unmarshal a JSON string into a session.
+func (p *Provider) UnmarshalSession(data string) (goth.Session, error) {
+ sess := &Session{}
+ err := json.NewDecoder(strings.NewReader(data)).Decode(sess)
+ return sess, err
+}
diff --git a/vendor/github.com/markbates/goth/providers/github/github.go b/vendor/github.com/markbates/goth/providers/github/github.go
index 866150e63a..b3c29b9670 100644
--- a/vendor/github.com/markbates/goth/providers/github/github.go
+++ b/vendor/github.com/markbates/goth/providers/github/github.go
@@ -37,13 +37,20 @@ var (
// You should always call `github.New` to get a new Provider. Never try to create
// one manually.
func New(clientKey, secret, callbackURL string, scopes ...string) *Provider {
+ return NewCustomisedURL(clientKey, secret, callbackURL, AuthURL, TokenURL, ProfileURL, EmailURL, scopes...)
+}
+
+// NewCustomisedURL is similar to New(...) but can be used to set custom URLs to connect to
+func NewCustomisedURL(clientKey, secret, callbackURL, authURL, tokenURL, profileURL, emailURL string, scopes ...string) *Provider {
p := &Provider{
- ClientKey: clientKey,
- Secret: secret,
- CallbackURL: callbackURL,
- providerName: "github",
- }
- p.config = newConfig(p, scopes)
+ ClientKey: clientKey,
+ Secret: secret,
+ CallbackURL: callbackURL,
+ providerName: "github",
+ profileURL: profileURL,
+ emailURL: emailURL,
+ }
+ p.config = newConfig(p, authURL, tokenURL, scopes)
return p
}
@@ -55,6 +62,8 @@ type Provider struct {
HTTPClient *http.Client
config *oauth2.Config
providerName string
+ profileURL string
+ emailURL string
}
// Name is the name used to retrieve this provider later.
@@ -96,7 +105,7 @@ func (p *Provider) FetchUser(session goth.Session) (goth.User, error) {
return user, fmt.Errorf("%s cannot get user information without accessToken", p.providerName)
}
- response, err := p.Client().Get(ProfileURL + "?access_token=" + url.QueryEscape(sess.AccessToken))
+ response, err := p.Client().Get(p.profileURL + "?access_token=" + url.QueryEscape(sess.AccessToken))
if err != nil {
return user, err
}
@@ -163,7 +172,7 @@ func userFromReader(reader io.Reader, user *goth.User) error {
}
func getPrivateMail(p *Provider, sess *Session) (email string, err error) {
- response, err := p.Client().Get(EmailURL + "?access_token=" + url.QueryEscape(sess.AccessToken))
+ response, err := p.Client().Get(p.emailURL + "?access_token=" + url.QueryEscape(sess.AccessToken))
if err != nil {
if response != nil {
response.Body.Close()
@@ -194,14 +203,14 @@ func getPrivateMail(p *Provider, sess *Session) (email string, err error) {
return
}
-func newConfig(provider *Provider, scopes []string) *oauth2.Config {
+func newConfig(provider *Provider, authURL, tokenURL string, scopes []string) *oauth2.Config {
c := &oauth2.Config{
ClientID: provider.ClientKey,
ClientSecret: provider.Secret,
RedirectURL: provider.CallbackURL,
Endpoint: oauth2.Endpoint{
- AuthURL: AuthURL,
- TokenURL: TokenURL,
+ AuthURL: authURL,
+ TokenURL: tokenURL,
},
Scopes: []string{},
}
diff --git a/vendor/github.com/markbates/goth/providers/gitlab/gitlab.go b/vendor/github.com/markbates/goth/providers/gitlab/gitlab.go
new file mode 100644
index 0000000000..fe188c01a9
--- /dev/null
+++ b/vendor/github.com/markbates/goth/providers/gitlab/gitlab.go
@@ -0,0 +1,187 @@
+// Package gitlab implements the OAuth2 protocol for authenticating users through gitlab.
+// This package can be used as a reference implementation of an OAuth2 provider for Goth.
+package gitlab
+
+import (
+ "bytes"
+ "encoding/json"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "net/url"
+ "strconv"
+
+ "github.com/markbates/goth"
+ "golang.org/x/oauth2"
+ "fmt"
+)
+
+// These vars define the Authentication, Token, and Profile URLS for Gitlab. If
+// using Gitlab CE or EE, you should change these values before calling New.
+//
+// Examples:
+// gitlab.AuthURL = "https://gitlab.acme.com/oauth/authorize
+// gitlab.TokenURL = "https://gitlab.acme.com/oauth/token
+// gitlab.ProfileURL = "https://gitlab.acme.com/api/v3/user
+var (
+ AuthURL = "https://gitlab.com/oauth/authorize"
+ TokenURL = "https://gitlab.com/oauth/token"
+ ProfileURL = "https://gitlab.com/api/v3/user"
+)
+
+// Provider is the implementation of `goth.Provider` for accessing Gitlab.
+type Provider struct {
+ ClientKey string
+ Secret string
+ CallbackURL string
+ HTTPClient *http.Client
+ config *oauth2.Config
+ providerName string
+ authURL string
+ tokenURL string
+ profileURL string
+}
+
+// New creates a new Gitlab provider and sets up important connection details.
+// You should always call `gitlab.New` to get a new provider. Never try to
+// create one manually.
+func New(clientKey, secret, callbackURL string, scopes ...string) *Provider {
+ return NewCustomisedURL(clientKey, secret, callbackURL, AuthURL, TokenURL, ProfileURL, scopes...)
+}
+
+// NewCustomisedURL is similar to New(...) but can be used to set custom URLs to connect to
+func NewCustomisedURL(clientKey, secret, callbackURL, authURL, tokenURL, profileURL string, scopes ...string) *Provider {
+ p := &Provider{
+ ClientKey: clientKey,
+ Secret: secret,
+ CallbackURL: callbackURL,
+ providerName: "gitlab",
+ profileURL: profileURL,
+ }
+ p.config = newConfig(p, authURL, tokenURL, scopes)
+ return p
+}
+
+// Name is the name used to retrieve this provider later.
+func (p *Provider) Name() string {
+ return p.providerName
+}
+
+// SetName is to update the name of the provider (needed in case of multiple providers of 1 type)
+func (p *Provider) SetName(name string) {
+ p.providerName = name
+}
+
+func (p *Provider) Client() *http.Client {
+ return goth.HTTPClientWithFallBack(p.HTTPClient)
+}
+
+// Debug is a no-op for the gitlab package.
+func (p *Provider) Debug(debug bool) {}
+
+// BeginAuth asks Gitlab for an authentication end-point.
+func (p *Provider) BeginAuth(state string) (goth.Session, error) {
+ return &Session{
+ AuthURL: p.config.AuthCodeURL(state),
+ }, nil
+}
+
+// FetchUser will go to Gitlab and access basic information about the user.
+func (p *Provider) FetchUser(session goth.Session) (goth.User, error) {
+ sess := session.(*Session)
+ user := goth.User{
+ AccessToken: sess.AccessToken,
+ Provider: p.Name(),
+ RefreshToken: sess.RefreshToken,
+ ExpiresAt: sess.ExpiresAt,
+ }
+
+ if user.AccessToken == "" {
+ // data is not yet retrieved since accessToken is still empty
+ return user, fmt.Errorf("%s cannot get user information without accessToken", p.providerName)
+ }
+
+ response, err := p.Client().Get(p.profileURL + "?access_token=" + url.QueryEscape(sess.AccessToken))
+ if err != nil {
+ if response != nil {
+ response.Body.Close()
+ }
+ return user, err
+ }
+
+ defer response.Body.Close()
+
+ if response.StatusCode != http.StatusOK {
+ return user, fmt.Errorf("%s responded with a %d trying to fetch user information", p.providerName, response.StatusCode)
+ }
+
+ bits, err := ioutil.ReadAll(response.Body)
+ if err != nil {
+ return user, err
+ }
+
+ err = json.NewDecoder(bytes.NewReader(bits)).Decode(&user.RawData)
+ if err != nil {
+ return user, err
+ }
+
+ err = userFromReader(bytes.NewReader(bits), &user)
+
+ return user, err
+}
+
+func newConfig(provider *Provider, authURL, tokenURL string, scopes []string) *oauth2.Config {
+ c := &oauth2.Config{
+ ClientID: provider.ClientKey,
+ ClientSecret: provider.Secret,
+ RedirectURL: provider.CallbackURL,
+ Endpoint: oauth2.Endpoint{
+ AuthURL: authURL,
+ TokenURL: tokenURL,
+ },
+ Scopes: []string{},
+ }
+
+ if len(scopes) > 0 {
+ for _, scope := range scopes {
+ c.Scopes = append(c.Scopes, scope)
+ }
+ }
+ return c
+}
+
+func userFromReader(r io.Reader, user *goth.User) error {
+ u := struct {
+ Name string `json:"name"`
+ Email string `json:"email"`
+ NickName string `json:"username"`
+ ID int `json:"id"`
+ AvatarURL string `json:"avatar_url"`
+ }{}
+ err := json.NewDecoder(r).Decode(&u)
+ if err != nil {
+ return err
+ }
+ user.Email = u.Email
+ user.Name = u.Name
+ user.NickName = u.NickName
+ user.UserID = strconv.Itoa(u.ID)
+ user.AvatarURL = u.AvatarURL
+ return nil
+}
+
+//RefreshTokenAvailable refresh token is provided by auth provider or not
+func (p *Provider) RefreshTokenAvailable() bool {
+ return true
+}
+
+//RefreshToken get new access token based on the refresh token
+func (p *Provider) RefreshToken(refreshToken string) (*oauth2.Token, error) {
+ token := &oauth2.Token{RefreshToken: refreshToken}
+ ts := p.config.TokenSource(goth.ContextForClient(p.Client()), token)
+ newToken, err := ts.Token()
+ if err != nil {
+ return nil, err
+ }
+ return newToken, err
+}
diff --git a/vendor/github.com/markbates/goth/providers/gitlab/session.go b/vendor/github.com/markbates/goth/providers/gitlab/session.go
new file mode 100644
index 0000000000..a2f90647c2
--- /dev/null
+++ b/vendor/github.com/markbates/goth/providers/gitlab/session.go
@@ -0,0 +1,63 @@
+package gitlab
+
+import (
+ "encoding/json"
+ "errors"
+ "strings"
+ "time"
+
+ "github.com/markbates/goth"
+)
+
+// Session stores data during the auth process with Gitlab.
+type Session struct {
+ AuthURL string
+ AccessToken string
+ RefreshToken string
+ ExpiresAt time.Time
+}
+
+var _ goth.Session = &Session{}
+
+// GetAuthURL will return the URL set by calling the `BeginAuth` function on the Gitlab provider.
+func (s Session) GetAuthURL() (string, error) {
+ if s.AuthURL == "" {
+ return "", errors.New(goth.NoAuthUrlErrorMessage)
+ }
+ return s.AuthURL, nil
+}
+
+// Authorize the session with Gitlab and return the access token to be stored for future use.
+func (s *Session) Authorize(provider goth.Provider, params goth.Params) (string, error) {
+ p := provider.(*Provider)
+ token, err := p.config.Exchange(goth.ContextForClient(p.Client()), params.Get("code"))
+ if err != nil {
+ return "", err
+ }
+
+ if !token.Valid() {
+ return "", errors.New("Invalid token received from provider")
+ }
+
+ s.AccessToken = token.AccessToken
+ s.RefreshToken = token.RefreshToken
+ s.ExpiresAt = token.Expiry
+ return token.AccessToken, err
+}
+
+// Marshal the session into a string
+func (s Session) Marshal() string {
+ b, _ := json.Marshal(s)
+ return string(b)
+}
+
+func (s Session) String() string {
+ return s.Marshal()
+}
+
+// UnmarshalSession wil unmarshal a JSON string into a session.
+func (p *Provider) UnmarshalSession(data string) (goth.Session, error) {
+ s := &Session{}
+ err := json.NewDecoder(strings.NewReader(data)).Decode(s)
+ return s, err
+}
diff --git a/vendor/github.com/markbates/goth/providers/gplus/gplus.go b/vendor/github.com/markbates/goth/providers/gplus/gplus.go
new file mode 100644
index 0000000000..06655c2f7f
--- /dev/null
+++ b/vendor/github.com/markbates/goth/providers/gplus/gplus.go
@@ -0,0 +1,195 @@
+// Package gplus implements the OAuth2 protocol for authenticating users through Google+.
+// This package can be used as a reference implementation of an OAuth2 provider for Goth.
+package gplus
+
+import (
+ "bytes"
+ "encoding/json"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "net/url"
+ "strings"
+
+ "github.com/markbates/goth"
+ "golang.org/x/oauth2"
+ "fmt"
+)
+
+const (
+ authURL string = "https://accounts.google.com/o/oauth2/auth?access_type=offline"
+ tokenURL string = "https://accounts.google.com/o/oauth2/token"
+ endpointProfile string = "https://www.googleapis.com/oauth2/v2/userinfo"
+)
+
+// New creates a new Google+ provider, and sets up important connection details.
+// You should always call `gplus.New` to get a new Provider. Never try to create
+// one manually.
+func New(clientKey, secret, callbackURL string, scopes ...string) *Provider {
+ p := &Provider{
+ ClientKey: clientKey,
+ Secret: secret,
+ CallbackURL: callbackURL,
+ providerName: "gplus",
+ }
+ p.config = newConfig(p, scopes)
+ return p
+}
+
+// Provider is the implementation of `goth.Provider` for accessing Google+.
+type Provider struct {
+ ClientKey string
+ Secret string
+ CallbackURL string
+ HTTPClient *http.Client
+ config *oauth2.Config
+ prompt oauth2.AuthCodeOption
+ providerName string
+}
+
+// Name is the name used to retrieve this provider later.
+func (p *Provider) Name() string {
+ return p.providerName
+}
+
+// SetName is to update the name of the provider (needed in case of multiple providers of 1 type)
+func (p *Provider) SetName(name string) {
+ p.providerName = name
+}
+
+func (p *Provider) Client() *http.Client {
+ return goth.HTTPClientWithFallBack(p.HTTPClient)
+}
+
+// Debug is a no-op for the gplus package.
+func (p *Provider) Debug(debug bool) {}
+
+// BeginAuth asks Google+ for an authentication end-point.
+func (p *Provider) BeginAuth(state string) (goth.Session, error) {
+ var opts []oauth2.AuthCodeOption
+ if p.prompt != nil {
+ opts = append(opts, p.prompt)
+ }
+ url := p.config.AuthCodeURL(state, opts...)
+ session := &Session{
+ AuthURL: url,
+ }
+ return session, nil
+}
+
+// FetchUser will go to Google+ and access basic information about the user.
+func (p *Provider) FetchUser(session goth.Session) (goth.User, error) {
+ sess := session.(*Session)
+ user := goth.User{
+ AccessToken: sess.AccessToken,
+ Provider: p.Name(),
+ RefreshToken: sess.RefreshToken,
+ ExpiresAt: sess.ExpiresAt,
+ }
+
+ if user.AccessToken == "" {
+ // data is not yet retrieved since accessToken is still empty
+ return user, fmt.Errorf("%s cannot get user information without accessToken", p.providerName)
+ }
+
+ response, err := p.Client().Get(endpointProfile + "?access_token=" + url.QueryEscape(sess.AccessToken))
+ if err != nil {
+ return user, err
+ }
+ defer response.Body.Close()
+
+ if response.StatusCode != http.StatusOK {
+ return user, fmt.Errorf("%s responded with a %d trying to fetch user information", p.providerName, response.StatusCode)
+ }
+
+ bits, err := ioutil.ReadAll(response.Body)
+ if err != nil {
+ return user, err
+ }
+
+ err = json.NewDecoder(bytes.NewReader(bits)).Decode(&user.RawData)
+ if err != nil {
+ return user, err
+ }
+
+ err = userFromReader(bytes.NewReader(bits), &user)
+ return user, err
+}
+
+func userFromReader(reader io.Reader, user *goth.User) error {
+ u := struct {
+ ID string `json:"id"`
+ Email string `json:"email"`
+ Name string `json:"name"`
+ FirstName string `json:"given_name"`
+ LastName string `json:"family_name"`
+ Link string `json:"link"`
+ Picture string `json:"picture"`
+ }{}
+
+ err := json.NewDecoder(reader).Decode(&u)
+ if err != nil {
+ return err
+ }
+
+ user.Name = u.Name
+ user.FirstName = u.FirstName
+ user.LastName = u.LastName
+ user.NickName = u.Name
+ user.Email = u.Email
+ //user.Description = u.Bio
+ user.AvatarURL = u.Picture
+ user.UserID = u.ID
+ //user.Location = u.Location.Name
+
+ return err
+}
+
+func newConfig(provider *Provider, scopes []string) *oauth2.Config {
+ c := &oauth2.Config{
+ ClientID: provider.ClientKey,
+ ClientSecret: provider.Secret,
+ RedirectURL: provider.CallbackURL,
+ Endpoint: oauth2.Endpoint{
+ AuthURL: authURL,
+ TokenURL: tokenURL,
+ },
+ Scopes: []string{},
+ }
+
+ if len(scopes) > 0 {
+ for _, scope := range scopes {
+ c.Scopes = append(c.Scopes, scope)
+ }
+ } else {
+ c.Scopes = []string{"profile", "email", "openid"}
+ }
+ return c
+}
+
+//RefreshTokenAvailable refresh token is provided by auth provider or not
+func (p *Provider) RefreshTokenAvailable() bool {
+ return true
+}
+
+//RefreshToken get new access token based on the refresh token
+func (p *Provider) RefreshToken(refreshToken string) (*oauth2.Token, error) {
+ token := &oauth2.Token{RefreshToken: refreshToken}
+ ts := p.config.TokenSource(goth.ContextForClient(p.Client()), token)
+ newToken, err := ts.Token()
+ if err != nil {
+ return nil, err
+ }
+ return newToken, err
+}
+
+// SetPrompt sets the prompt values for the GPlus OAuth call. Use this to
+// force users to choose and account every time by passing "select_account",
+// for example.
+// See https://developers.google.com/identity/protocols/OpenIDConnect#authenticationuriparameters
+func (p *Provider) SetPrompt(prompt ...string) {
+ if len(prompt) == 0 {
+ return
+ }
+ p.prompt = oauth2.SetAuthURLParam("prompt", strings.Join(prompt, " "))
+}
diff --git a/vendor/github.com/markbates/goth/providers/gplus/session.go b/vendor/github.com/markbates/goth/providers/gplus/session.go
new file mode 100644
index 0000000000..9710031f4d
--- /dev/null
+++ b/vendor/github.com/markbates/goth/providers/gplus/session.go
@@ -0,0 +1,61 @@
+package gplus
+
+import (
+ "encoding/json"
+ "errors"
+ "strings"
+ "time"
+
+ "github.com/markbates/goth"
+)
+
+// Session stores data during the auth process with Google+.
+type Session struct {
+ AuthURL string
+ AccessToken string
+ RefreshToken string
+ ExpiresAt time.Time
+}
+
+// GetAuthURL will return the URL set by calling the `BeginAuth` function on the Google+ provider.
+func (s Session) GetAuthURL() (string, error) {
+ if s.AuthURL == "" {
+ return "", errors.New(goth.NoAuthUrlErrorMessage)
+ }
+ return s.AuthURL, nil
+}
+
+// Authorize the session with Google+ and return the access token to be stored for future use.
+func (s *Session) Authorize(provider goth.Provider, params goth.Params) (string, error) {
+ p := provider.(*Provider)
+ token, err := p.config.Exchange(goth.ContextForClient(p.Client()), params.Get("code"))
+ if err != nil {
+ return "", err
+ }
+
+ if !token.Valid() {
+ return "", errors.New("Invalid token received from provider")
+ }
+
+ s.AccessToken = token.AccessToken
+ s.RefreshToken = token.RefreshToken
+ s.ExpiresAt = token.Expiry
+ return token.AccessToken, err
+}
+
+// Marshal the session into a string
+func (s Session) Marshal() string {
+ b, _ := json.Marshal(s)
+ return string(b)
+}
+
+func (s Session) String() string {
+ return s.Marshal()
+}
+
+// UnmarshalSession will unmarshal a JSON string into a session.
+func (p *Provider) UnmarshalSession(data string) (goth.Session, error) {
+ sess := &Session{}
+ err := json.NewDecoder(strings.NewReader(data)).Decode(sess)
+ return sess, err
+}
diff --git a/vendor/github.com/markbates/goth/providers/openidConnect/openidConnect.go b/vendor/github.com/markbates/goth/providers/openidConnect/openidConnect.go
new file mode 100644
index 0000000000..7ffd11c607
--- /dev/null
+++ b/vendor/github.com/markbates/goth/providers/openidConnect/openidConnect.go
@@ -0,0 +1,384 @@
+package openidConnect
+
+import (
+ "net/http"
+ "strings"
+ "fmt"
+ "encoding/json"
+ "encoding/base64"
+ "io/ioutil"
+ "errors"
+ "golang.org/x/oauth2"
+ "github.com/markbates/goth"
+ "time"
+ "bytes"
+)
+
+const (
+ // Standard Claims http://openid.net/specs/openid-connect-core-1_0.html#StandardClaims
+ // fixed, cannot be changed
+ subjectClaim = "sub"
+ expiryClaim = "exp"
+ audienceClaim = "aud"
+ issuerClaim = "iss"
+
+ PreferredUsernameClaim = "preferred_username"
+ EmailClaim = "email"
+ NameClaim = "name"
+ NicknameClaim = "nickname"
+ PictureClaim = "picture"
+ GivenNameClaim = "given_name"
+ FamilyNameClaim = "family_name"
+ AddressClaim = "address"
+
+ // Unused but available to set in Provider claims
+ MiddleNameClaim = "middle_name"
+ ProfileClaim = "profile"
+ WebsiteClaim = "website"
+ EmailVerifiedClaim = "email_verified"
+ GenderClaim = "gender"
+ BirthdateClaim = "birthdate"
+ ZoneinfoClaim = "zoneinfo"
+ LocaleClaim = "locale"
+ PhoneNumberClaim = "phone_number"
+ PhoneNumberVerifiedClaim = "phone_number_verified"
+ UpdatedAtClaim = "updated_at"
+
+ clockSkew = 10 * time.Second
+)
+
+// Provider is the implementation of `goth.Provider` for accessing OpenID Connect provider
+type Provider struct {
+ ClientKey string
+ Secret string
+ CallbackURL string
+ HTTPClient *http.Client
+ config *oauth2.Config
+ openIDConfig *OpenIDConfig
+ providerName string
+
+ UserIdClaims []string
+ NameClaims []string
+ NickNameClaims []string
+ EmailClaims []string
+ AvatarURLClaims []string
+ FirstNameClaims []string
+ LastNameClaims []string
+ LocationClaims []string
+
+ SkipUserInfoRequest bool
+}
+
+type OpenIDConfig struct {
+ AuthEndpoint string `json:"authorization_endpoint"`
+ TokenEndpoint string `json:"token_endpoint"`
+ UserInfoEndpoint string `json:"userinfo_endpoint"`
+ Issuer string `json:"issuer"`
+}
+
+// New creates a new OpenID Connect provider, and sets up important connection details.
+// You should always call `openidConnect.New` to get a new Provider. Never try to create
+// one manually.
+// New returns an implementation of an OpenID Connect Authorization Code Flow
+// See http://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth
+// ID Token decryption is not (yet) supported
+// UserInfo decryption is not (yet) supported
+func New(clientKey, secret, callbackURL, openIDAutoDiscoveryURL string, scopes ...string) (*Provider, error) {
+ p := &Provider{
+ ClientKey: clientKey,
+ 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},
+
+ providerName: "openid-connect",
+ }
+
+ openIDConfig, err := getOpenIDConfig(p, openIDAutoDiscoveryURL)
+ if err != nil {
+ return nil, err
+ }
+ p.openIDConfig = openIDConfig
+
+ p.config = newConfig(p, scopes, openIDConfig)
+ return p, nil
+}
+
+// Name is the name used to retrieve this provider later.
+func (p *Provider) Name() string {
+ return p.providerName
+}
+
+// SetName is to update the name of the provider (needed in case of multiple providers of 1 type)
+func (p *Provider) SetName(name string) {
+ p.providerName = name
+}
+
+func (p *Provider) Client() *http.Client {
+ return goth.HTTPClientWithFallBack(p.HTTPClient)
+}
+
+// Debug is a no-op for the openidConnect package.
+func (p *Provider) Debug(debug bool) {}
+
+// BeginAuth asks the OpenID Connect provider for an authentication end-point.
+func (p *Provider) BeginAuth(state string) (goth.Session, error) {
+ url := p.config.AuthCodeURL(state)
+ session := &Session{
+ AuthURL: url,
+ }
+ return session, nil
+}
+
+// FetchUser will use the the id_token and access requested information about the user.
+func (p *Provider) FetchUser(session goth.Session) (goth.User, error) {
+ sess := session.(*Session)
+
+ expiresAt := sess.ExpiresAt
+
+ if sess.IDToken == "" {
+ return goth.User{}, fmt.Errorf("%s cannot get user information without id_token", p.providerName)
+ }
+
+ // decode returned id token to get expiry
+ claims, err := decodeJWT(sess.IDToken)
+
+ if err != nil {
+ return goth.User{}, fmt.Errorf("oauth2: error decoding JWT token: %v", err)
+ }
+
+ expiry, err := p.validateClaims(claims)
+ if err != nil {
+ return goth.User{}, fmt.Errorf("oauth2: error validating JWT token: %v", err)
+ }
+
+ if expiry.Before(expiresAt) {
+ expiresAt = expiry
+ }
+
+ if err := p.getUserInfo(sess.AccessToken, claims); err != nil {
+ return goth.User{}, err
+ }
+
+ user := goth.User{
+ AccessToken: sess.AccessToken,
+ Provider: p.Name(),
+ RefreshToken: sess.RefreshToken,
+ ExpiresAt: expiresAt,
+ RawData: claims,
+ }
+
+ p.userFromClaims(claims, &user)
+ return user, err
+}
+
+//RefreshTokenAvailable refresh token is provided by auth provider or not
+func (p *Provider) RefreshTokenAvailable() bool {
+ return true
+}
+
+//RefreshToken get new access token based on the refresh token
+func (p *Provider) RefreshToken(refreshToken string) (*oauth2.Token, error) {
+ token := &oauth2.Token{RefreshToken: refreshToken}
+ ts := p.config.TokenSource(oauth2.NoContext, token)
+ newToken, err := ts.Token()
+ if err != nil {
+ return nil, err
+ }
+ return newToken, err
+}
+
+// validate according to standard, returns expiry
+// http://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation
+func (p *Provider) validateClaims(claims map[string]interface{}) (time.Time, error) {
+ audience := getClaimValue(claims, []string{audienceClaim})
+ if audience != p.ClientKey {
+ return time.Time{}, errors.New("audience in token does not match client key")
+ }
+
+ issuer := getClaimValue(claims, []string{issuerClaim})
+ if issuer != p.openIDConfig.Issuer {
+ return time.Time{}, errors.New("issuer in token does not match issuer in OpenIDConfig discovery")
+ }
+
+ // expiry is required for JWT, not for UserInfoResponse
+ // is actually a int64, so force it in to that type
+ expiryClaim := int64(claims[expiryClaim].(float64))
+ expiry := time.Unix(expiryClaim, 0)
+ if expiry.Add(clockSkew).Before(time.Now()) {
+ return time.Time{}, errors.New("user info JWT token is expired")
+ }
+ return expiry, nil
+}
+
+func (p *Provider) userFromClaims(claims map[string]interface{}, user *goth.User) {
+ // required
+ user.UserID = getClaimValue(claims, p.UserIdClaims)
+
+ user.Name = getClaimValue(claims, p.NameClaims)
+ user.NickName = getClaimValue(claims, p.NickNameClaims)
+ user.Email = getClaimValue(claims, p.EmailClaims)
+ user.AvatarURL = getClaimValue(claims, p.AvatarURLClaims)
+ user.FirstName = getClaimValue(claims, p.FirstNameClaims)
+ user.LastName = getClaimValue(claims, p.LastNameClaims)
+ user.Location = getClaimValue(claims, p.LocationClaims)
+}
+
+func (p *Provider) getUserInfo(accessToken string, claims map[string]interface{}) error {
+ // skip if there is no UserInfoEndpoint or is explicitly disabled
+ if p.openIDConfig.UserInfoEndpoint == "" || p.SkipUserInfoRequest {
+ return nil
+ }
+
+ userInfoClaims, err := p.fetchUserInfo(p.openIDConfig.UserInfoEndpoint, accessToken)
+ if err != nil {
+ return err
+ }
+
+ // The sub (subject) Claim MUST always be returned in the UserInfo Response.
+ // http://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse
+ userInfoSubject := getClaimValue(userInfoClaims, []string{subjectClaim})
+ if userInfoSubject == "" {
+ return fmt.Errorf("userinfo response did not contain a 'sub' claim: %#v", userInfoClaims)
+ }
+
+ // The sub Claim in the UserInfo Response MUST be verified to exactly match the sub Claim in the ID Token;
+ // if they do not match, the UserInfo Response values MUST NOT be used.
+ // http://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse
+ subject := getClaimValue(claims, []string{subjectClaim})
+ if userInfoSubject != subject {
+ return fmt.Errorf("userinfo 'sub' claim (%s) did not match id_token 'sub' claim (%s)", userInfoSubject, subject)
+ }
+
+ // Merge in userinfo claims in case id_token claims contained some that userinfo did not
+ for k, v := range userInfoClaims {
+ claims[k] = v
+ }
+
+ return nil
+}
+
+// fetch and decode JSON from the given UserInfo URL
+func (p *Provider) fetchUserInfo(url, accessToken string) (map[string]interface{}, error) {
+ req, _ := http.NewRequest("GET", url, nil)
+ req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", accessToken))
+
+ resp, err := p.Client().Do(req)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+
+ if resp.StatusCode != http.StatusOK {
+ return nil, fmt.Errorf("Non-200 response from UserInfo: %d, WWW-Authenticate=%s", resp.StatusCode, resp.Header.Get("WWW-Authenticate"))
+ }
+
+ // The UserInfo Claims MUST be returned as the members of a JSON object
+ // http://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse
+ data, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ return nil, err
+ }
+
+ return unMarshal(data)
+}
+
+func getOpenIDConfig(p *Provider, openIDAutoDiscoveryURL string) (*OpenIDConfig, error) {
+ res, err := p.Client().Get(openIDAutoDiscoveryURL)
+ if err != nil {
+ return nil, err
+ }
+ defer res.Body.Close()
+
+ body, err := ioutil.ReadAll(res.Body)
+ if err != nil {
+ return nil, err
+ }
+
+ openIDConfig := &OpenIDConfig{}
+ err = json.Unmarshal(body, openIDConfig)
+ if err != nil {
+ return nil, err
+ }
+
+ return openIDConfig, nil
+}
+
+func newConfig(provider *Provider, scopes []string, openIDConfig *OpenIDConfig) *oauth2.Config {
+ c := &oauth2.Config{
+ ClientID: provider.ClientKey,
+ ClientSecret: provider.Secret,
+ RedirectURL: provider.CallbackURL,
+ Endpoint: oauth2.Endpoint{
+ AuthURL: openIDConfig.AuthEndpoint,
+ TokenURL: openIDConfig.TokenEndpoint,
+ },
+ Scopes: []string{},
+ }
+
+ if len(scopes) > 0 {
+ foundOpenIDScope := false
+
+ for _, scope := range scopes {
+ if scope == "openid" {
+ foundOpenIDScope = true
+ }
+ c.Scopes = append(c.Scopes, scope)
+ }
+
+ if !foundOpenIDScope {
+ c.Scopes = append(c.Scopes, "openid")
+ }
+ } else {
+ c.Scopes = []string{"openid"}
+ }
+
+ return c
+}
+
+func getClaimValue(data map[string]interface{}, claims []string) string {
+ for _, claim := range claims {
+ if value, ok := data[claim]; ok {
+ if stringValue, ok := value.(string); ok && len(stringValue) > 0 {
+ return stringValue
+ }
+ }
+ }
+
+ return ""
+}
+
+// decodeJWT decodes a JSON Web Token into a simple map
+// http://openid.net/specs/draft-jones-json-web-token-07.html
+func decodeJWT(jwt string) (map[string]interface{}, error) {
+ jwtParts := strings.Split(jwt, ".")
+ if len(jwtParts) != 3 {
+ return nil, errors.New("jws: invalid token received, not all parts available")
+ }
+
+ // Re-pad, if needed
+ encodedPayload := jwtParts[1]
+ if l := len(encodedPayload) % 4; l != 0 {
+ encodedPayload += strings.Repeat("=", 4-l)
+ }
+
+ decodedPayload, err := base64.StdEncoding.DecodeString(encodedPayload)
+ if err != nil {
+ return nil, err
+ }
+
+ return unMarshal(decodedPayload)
+}
+
+func unMarshal(payload []byte) (map[string]interface{}, error) {
+ data := make(map[string]interface{})
+
+ return data, json.NewDecoder(bytes.NewBuffer(payload)).Decode(&data)
+}
diff --git a/vendor/github.com/markbates/goth/providers/openidConnect/session.go b/vendor/github.com/markbates/goth/providers/openidConnect/session.go
new file mode 100644
index 0000000000..a34584fdef
--- /dev/null
+++ b/vendor/github.com/markbates/goth/providers/openidConnect/session.go
@@ -0,0 +1,63 @@
+package openidConnect
+
+import (
+ "errors"
+ "github.com/markbates/goth"
+ "encoding/json"
+ "strings"
+ "time"
+ "golang.org/x/oauth2"
+)
+
+// Session stores data during the auth process with the OpenID Connect provider.
+type Session struct {
+ AuthURL string
+ AccessToken string
+ RefreshToken string
+ ExpiresAt time.Time
+ IDToken string
+}
+
+// GetAuthURL will return the URL set by calling the `BeginAuth` function on the OpenID Connect provider.
+func (s Session) GetAuthURL() (string, error) {
+ if s.AuthURL == "" {
+ return "", errors.New("an AuthURL has not be set")
+ }
+ return s.AuthURL, nil
+}
+
+// Authorize the session with the OpenID Connect provider and return the access token to be stored for future use.
+func (s *Session) Authorize(provider goth.Provider, params goth.Params) (string, error) {
+ p := provider.(*Provider)
+ token, err := p.config.Exchange(oauth2.NoContext, params.Get("code"))
+ if err != nil {
+ return "", err
+ }
+
+ if !token.Valid() {
+ return "", errors.New("Invalid token received from provider")
+ }
+
+ s.AccessToken = token.AccessToken
+ s.RefreshToken = token.RefreshToken
+ s.ExpiresAt = token.Expiry
+ s.IDToken = token.Extra("id_token").(string)
+ return token.AccessToken, err
+}
+
+// Marshal the session into a string
+func (s Session) Marshal() string {
+ b, _ := json.Marshal(s)
+ return string(b)
+}
+
+func (s Session) String() string {
+ return s.Marshal()
+}
+
+// UnmarshalSession will unmarshal a JSON string into a session.
+func (p *Provider) UnmarshalSession(data string) (goth.Session, error) {
+ sess := &Session{}
+ err := json.NewDecoder(strings.NewReader(data)).Decode(sess)
+ return sess, err
+}
diff --git a/vendor/github.com/markbates/goth/providers/twitter/session.go b/vendor/github.com/markbates/goth/providers/twitter/session.go
new file mode 100644
index 0000000000..049928ff26
--- /dev/null
+++ b/vendor/github.com/markbates/goth/providers/twitter/session.go
@@ -0,0 +1,54 @@
+package twitter
+
+import (
+ "encoding/json"
+ "errors"
+ "strings"
+
+ "github.com/markbates/goth"
+ "github.com/mrjones/oauth"
+)
+
+// Session stores data during the auth process with Twitter.
+type Session struct {
+ AuthURL string
+ AccessToken *oauth.AccessToken
+ RequestToken *oauth.RequestToken
+}
+
+// GetAuthURL will return the URL set by calling the `BeginAuth` function on the Twitter provider.
+func (s Session) GetAuthURL() (string, error) {
+ if s.AuthURL == "" {
+ return "", errors.New(goth.NoAuthUrlErrorMessage)
+ }
+ return s.AuthURL, nil
+}
+
+// Authorize the session with Twitter and return the access token to be stored for future use.
+func (s *Session) Authorize(provider goth.Provider, params goth.Params) (string, error) {
+ p := provider.(*Provider)
+ accessToken, err := p.consumer.AuthorizeToken(s.RequestToken, params.Get("oauth_verifier"))
+ if err != nil {
+ return "", err
+ }
+
+ s.AccessToken = accessToken
+ return accessToken.Token, err
+}
+
+// Marshal the session into a string
+func (s Session) Marshal() string {
+ b, _ := json.Marshal(s)
+ return string(b)
+}
+
+func (s Session) String() string {
+ return s.Marshal()
+}
+
+// UnmarshalSession will unmarshal a JSON string into a session.
+func (p *Provider) UnmarshalSession(data string) (goth.Session, error) {
+ sess := &Session{}
+ err := json.NewDecoder(strings.NewReader(data)).Decode(sess)
+ return sess, err
+}
diff --git a/vendor/github.com/markbates/goth/providers/twitter/twitter.go b/vendor/github.com/markbates/goth/providers/twitter/twitter.go
new file mode 100644
index 0000000000..3703f21974
--- /dev/null
+++ b/vendor/github.com/markbates/goth/providers/twitter/twitter.go
@@ -0,0 +1,160 @@
+// Package twitter implements the OAuth protocol for authenticating users through Twitter.
+// This package can be used as a reference implementation of an OAuth provider for Goth.
+package twitter
+
+import (
+ "bytes"
+ "encoding/json"
+ "errors"
+ "io/ioutil"
+ "net/http"
+
+ "github.com/markbates/goth"
+ "github.com/mrjones/oauth"
+ "golang.org/x/oauth2"
+ "fmt"
+)
+
+var (
+ requestURL = "https://api.twitter.com/oauth/request_token"
+ authorizeURL = "https://api.twitter.com/oauth/authorize"
+ authenticateURL = "https://api.twitter.com/oauth/authenticate"
+ tokenURL = "https://api.twitter.com/oauth/access_token"
+ endpointProfile = "https://api.twitter.com/1.1/account/verify_credentials.json"
+)
+
+// New creates a new Twitter provider, and sets up important connection details.
+// You should always call `twitter.New` to get a new Provider. Never try to create
+// one manually.
+//
+// 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",
+ }
+ p.consumer = newConsumer(p, authorizeURL)
+ return p
+}
+
+// NewAuthenticate is the almost same as New.
+// 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",
+ }
+ p.consumer = newConsumer(p, authenticateURL)
+ return p
+}
+
+// Provider is the implementation of `goth.Provider` for accessing Twitter.
+type Provider struct {
+ ClientKey string
+ Secret string
+ CallbackURL string
+ HTTPClient *http.Client
+ debug bool
+ consumer *oauth.Consumer
+ providerName string
+}
+
+// Name is the name used to retrieve this provider later.
+func (p *Provider) Name() string {
+ return p.providerName
+}
+
+// SetName is to update the name of the provider (needed in case of multiple providers of 1 type)
+func (p *Provider) SetName(name string) {
+ p.providerName = name
+}
+
+func (p *Provider) Client() *http.Client {
+ return goth.HTTPClientWithFallBack(p.HTTPClient)
+}
+
+// Debug sets the logging of the OAuth client to verbose.
+func (p *Provider) Debug(debug bool) {
+ p.debug = debug
+}
+
+// BeginAuth asks Twitter for an authentication end-point and a request token for a session.
+// Twitter does not support the "state" variable.
+func (p *Provider) BeginAuth(state string) (goth.Session, error) {
+ requestToken, url, err := p.consumer.GetRequestTokenAndUrl(p.CallbackURL)
+ session := &Session{
+ AuthURL: url,
+ RequestToken: requestToken,
+ }
+ return session, err
+}
+
+// FetchUser will go to Twitter and access basic information about the user.
+func (p *Provider) FetchUser(session goth.Session) (goth.User, error) {
+ sess := session.(*Session)
+ user := goth.User{
+ Provider: p.Name(),
+ }
+
+ if sess.AccessToken == nil {
+ // data is not yet retrieved since accessToken is still empty
+ return user, fmt.Errorf("%s cannot get user information without accessToken", p.providerName)
+ }
+
+ response, err := p.consumer.Get(
+ endpointProfile,
+ map[string]string{"include_entities": "false", "skip_status": "true"},
+ sess.AccessToken)
+ if err != nil {
+ return user, err
+ }
+ defer response.Body.Close()
+
+ if response.StatusCode != http.StatusOK {
+ return user, fmt.Errorf("%s responded with a %d trying to fetch user information", p.providerName, response.StatusCode)
+ }
+
+ bits, err := ioutil.ReadAll(response.Body)
+ err = json.NewDecoder(bytes.NewReader(bits)).Decode(&user.RawData)
+ if err != nil {
+ return user, err
+ }
+
+ user.Name = user.RawData["name"].(string)
+ user.NickName = user.RawData["screen_name"].(string)
+ user.Description = user.RawData["description"].(string)
+ user.AvatarURL = user.RawData["profile_image_url"].(string)
+ user.UserID = user.RawData["id_str"].(string)
+ user.Location = user.RawData["location"].(string)
+ user.AccessToken = sess.AccessToken.Token
+ user.AccessTokenSecret = sess.AccessToken.Secret
+ return user, err
+}
+
+func newConsumer(provider *Provider, authURL string) *oauth.Consumer {
+ c := oauth.NewConsumer(
+ provider.ClientKey,
+ provider.Secret,
+ oauth.ServiceProvider{
+ RequestTokenUrl: requestURL,
+ AuthorizeTokenUrl: authURL,
+ AccessTokenUrl: tokenURL,
+ })
+
+ c.Debug(provider.debug)
+ return c
+}
+
+//RefreshToken refresh token is not provided by twitter
+func (p *Provider) RefreshToken(refreshToken string) (*oauth2.Token, error) {
+ return nil, errors.New("Refresh token is not provided by twitter")
+}
+
+//RefreshTokenAvailable refresh token is not provided by twitter
+func (p *Provider) RefreshTokenAvailable() bool {
+ return false
+}