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:
parent
2368bbb672
commit
950f2e2074
44 changed files with 4164 additions and 159 deletions
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
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue