mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-26 12:27:06 +00:00 
			
		
		
		
	Additional OAuth2 providers (#1010)
* add google+ * sort signin oauth2 providers based on the name so order is always the same * update auth tip for google+ * add gitlab provider * add bitbucket provider (and some go fmt) * add twitter provider * add facebook provider * add dropbox provider * add openid connect provider incl. new format of tips section in "Add New Source" * lower the amount of disk storage for each session to prevent issues while building cross platform (and disk overflow) * imports according to goimport and code style * make it possible to set custom urls to gitlab and github provider (only these could have a different host) * split up oauth2 into multiple files * small typo in comment * fix indention * fix indentation * fix new line before external import * fix layout of signin part * update "broken" dependency
This commit is contained in:
		 Willem van Dreumel
					Willem van Dreumel
				
			
				
					committed by
					
						 Lunny Xiao
						Lunny Xiao
					
				
			
			
				
	
			
			
			 Lunny Xiao
						Lunny Xiao
					
				
			
						parent
						
							2368bbb672
						
					
				
				
					commit
					950f2e2074
				
			
							
								
								
									
										206
									
								
								vendor/github.com/markbates/goth/providers/bitbucket/bitbucket.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										206
									
								
								vendor/github.com/markbates/goth/providers/bitbucket/bitbucket.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -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 | ||||
| } | ||||
							
								
								
									
										61
									
								
								vendor/github.com/markbates/goth/providers/bitbucket/session.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								vendor/github.com/markbates/goth/providers/bitbucket/session.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -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() | ||||
| } | ||||
							
								
								
									
										191
									
								
								vendor/github.com/markbates/goth/providers/dropbox/dropbox.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										191
									
								
								vendor/github.com/markbates/goth/providers/dropbox/dropbox.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -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 | ||||
| } | ||||
							
								
								
									
										195
									
								
								vendor/github.com/markbates/goth/providers/facebook/facebook.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										195
									
								
								vendor/github.com/markbates/goth/providers/facebook/facebook.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -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 | ||||
| } | ||||
							
								
								
									
										59
									
								
								vendor/github.com/markbates/goth/providers/facebook/session.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								vendor/github.com/markbates/goth/providers/facebook/session.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -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 | ||||
| } | ||||
							
								
								
									
										29
									
								
								vendor/github.com/markbates/goth/providers/github/github.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										29
									
								
								vendor/github.com/markbates/goth/providers/github/github.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -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", | ||||
| 		ClientKey:    clientKey, | ||||
| 		Secret:       secret, | ||||
| 		CallbackURL:  callbackURL, | ||||
| 		providerName: "github", | ||||
| 		profileURL:   profileURL, | ||||
| 		emailURL:     emailURL, | ||||
| 	} | ||||
| 	p.config = newConfig(p, scopes) | ||||
| 	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{}, | ||||
| 	} | ||||
|   | ||||
							
								
								
									
										187
									
								
								vendor/github.com/markbates/goth/providers/gitlab/gitlab.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										187
									
								
								vendor/github.com/markbates/goth/providers/gitlab/gitlab.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -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 | ||||
| } | ||||
							
								
								
									
										63
									
								
								vendor/github.com/markbates/goth/providers/gitlab/session.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								vendor/github.com/markbates/goth/providers/gitlab/session.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -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 | ||||
| } | ||||
							
								
								
									
										195
									
								
								vendor/github.com/markbates/goth/providers/gplus/gplus.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										195
									
								
								vendor/github.com/markbates/goth/providers/gplus/gplus.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -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, " ")) | ||||
| } | ||||
							
								
								
									
										61
									
								
								vendor/github.com/markbates/goth/providers/gplus/session.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								vendor/github.com/markbates/goth/providers/gplus/session.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -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 | ||||
| } | ||||
							
								
								
									
										384
									
								
								vendor/github.com/markbates/goth/providers/openidConnect/openidConnect.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										384
									
								
								vendor/github.com/markbates/goth/providers/openidConnect/openidConnect.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -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) | ||||
| } | ||||
							
								
								
									
										63
									
								
								vendor/github.com/markbates/goth/providers/openidConnect/session.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								vendor/github.com/markbates/goth/providers/openidConnect/session.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -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 | ||||
| } | ||||
							
								
								
									
										54
									
								
								vendor/github.com/markbates/goth/providers/twitter/session.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								vendor/github.com/markbates/goth/providers/twitter/session.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -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 | ||||
| } | ||||
							
								
								
									
										160
									
								
								vendor/github.com/markbates/goth/providers/twitter/twitter.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								vendor/github.com/markbates/goth/providers/twitter/twitter.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -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 | ||||
| } | ||||
		Reference in New Issue
	
	Block a user