aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/markbates
diff options
context:
space:
mode:
authortechknowlogick <hello@techknowlogick.com>2019-01-13 14:06:22 -0500
committerGitHub <noreply@github.com>2019-01-13 14:06:22 -0500
commit5c44f751a30517914ad232607a1202111cf4f0fa (patch)
tree5c2efcfa35e80d092bfd246f1c3a966792f9bb5f /vendor/github.com/markbates
parentbeab2df1227f9b7e556aa5716d94feb3a3e2088e (diff)
downloadgitea-5c44f751a30517914ad232607a1202111cf4f0fa.tar.gz
gitea-5c44f751a30517914ad232607a1202111cf4f0fa.zip
Discord Oauth2 support (#4476)
* add discord auth * add vendor for discord * fix syntax error * make fmt * update version of goth in use * update markbates/goth
Diffstat (limited to 'vendor/github.com/markbates')
-rw-r--r--vendor/github.com/markbates/goth/gothic/gothic.go2
-rw-r--r--vendor/github.com/markbates/goth/providers/discord/discord.go210
-rw-r--r--vendor/github.com/markbates/goth/providers/discord/session.go65
-rw-r--r--vendor/github.com/markbates/goth/providers/facebook/facebook.go42
4 files changed, 296 insertions, 23 deletions
diff --git a/vendor/github.com/markbates/goth/gothic/gothic.go b/vendor/github.com/markbates/goth/gothic/gothic.go
index 19dacb44b0..bea87d963d 100644
--- a/vendor/github.com/markbates/goth/gothic/gothic.go
+++ b/vendor/github.com/markbates/goth/gothic/gothic.go
@@ -3,7 +3,7 @@ Package gothic wraps common behaviour when using Goth. This makes it quick, and
and running with Goth. Of course, if you want complete control over how things flow, in regards
to the authentication process, feel free and use Goth directly.
-See https://github.com/markbates/goth/examples/main.go to see this in action.
+See https://github.com/markbates/goth/blob/master/examples/main.go to see this in action.
*/
package gothic
diff --git a/vendor/github.com/markbates/goth/providers/discord/discord.go b/vendor/github.com/markbates/goth/providers/discord/discord.go
new file mode 100644
index 0000000000..e93ec60cd8
--- /dev/null
+++ b/vendor/github.com/markbates/goth/providers/discord/discord.go
@@ -0,0 +1,210 @@
+// Package discord implements the OAuth2 protocol for authenticating users through Discord.
+// This package can be used as a reference implementation of an OAuth2 provider for Discord.
+package discord
+
+import (
+ "bytes"
+ "encoding/json"
+ "io"
+ "io/ioutil"
+
+ "github.com/markbates/goth"
+ "golang.org/x/oauth2"
+
+ "fmt"
+ "net/http"
+)
+
+const (
+ authURL string = "https://discordapp.com/api/oauth2/authorize"
+ tokenURL string = "https://discordapp.com/api/oauth2/token"
+ userEndpoint string = "https://discordapp.com/api/users/@me"
+)
+
+const (
+ // allows /users/@me without email
+ ScopeIdentify string = "identify"
+ // enables /users/@me to return an email
+ ScopeEmail string = "email"
+ // allows /users/@me/connections to return linked Twitch and YouTube accounts
+ ScopeConnections string = "connections"
+ // allows /users/@me/guilds to return basic information about all of a user's guilds
+ ScopeGuilds string = "guilds"
+ // allows /invites/{invite.id} to be used for joining a user's guild
+ ScopeJoinGuild string = "guilds.join"
+ // allows your app to join users to a group dm
+ ScopeGroupDMjoin string = "gdm.join"
+ // for oauth2 bots, this puts the bot in the user's selected guild by default
+ ScopeBot string = "bot"
+ // this generates a webhook that is returned in the oauth token response for authorization code grants
+ ScopeWebhook string = "webhook.incoming"
+)
+
+// New creates a new Discord provider, and sets up important connection details.
+// You should always call `discord.New` to get a new Provider. Never try to create
+// one manually.
+func New(clientKey string, secret string, callbackURL string, scopes ...string) *Provider {
+ p := &Provider{
+ ClientKey: clientKey,
+ Secret: secret,
+ CallbackURL: callbackURL,
+ providerName: "discord",
+ }
+ p.config = newConfig(p, scopes)
+ return p
+}
+
+// Provider is the implementation of `goth.Provider` for accessing Discord
+type Provider struct {
+ ClientKey string
+ Secret string
+ CallbackURL string
+ HTTPClient *http.Client
+ config *oauth2.Config
+ providerName string
+}
+
+// Name gets the name used to retrieve this provider.
+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 no-op for the Discord package.
+func (p *Provider) Debug(debug bool) {}
+
+// BeginAuth asks Discord for an authentication end-point.
+func (p *Provider) BeginAuth(state string) (goth.Session, error) {
+
+ url := p.config.AuthCodeURL(state, oauth2.AccessTypeOnline)
+
+ s := &Session{
+ AuthURL: url,
+ }
+ return s, nil
+}
+
+// FetchUser will go to Discord and access basic info about the user.
+func (p *Provider) FetchUser(session goth.Session) (goth.User, error) {
+
+ s := session.(*Session)
+
+ user := goth.User{
+ AccessToken: s.AccessToken,
+ Provider: p.Name(),
+ RefreshToken: s.RefreshToken,
+ ExpiresAt: s.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)
+ }
+
+ req, err := http.NewRequest("GET", userEndpoint, nil)
+ if err != nil {
+ return user, err
+ }
+ req.Header.Set("Accept", "application/json")
+ req.Header.Set("Authorization", "Bearer "+s.AccessToken)
+ resp, err := p.Client().Do(req)
+ if err != nil {
+ if resp != nil {
+ resp.Body.Close()
+ }
+ 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)
+ }
+
+ bits, err := ioutil.ReadAll(resp.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)
+ if err != nil {
+ return user, err
+ }
+
+ return user, err
+}
+
+func userFromReader(r io.Reader, user *goth.User) error {
+ u := struct {
+ Name string `json:"username"`
+ Email string `json:"email"`
+ AvatarID string `json:"avatar"`
+ MFAEnabled bool `json:"mfa_enabled"`
+ Discriminator string `json:"discriminator"`
+ Verified bool `json:"verified"`
+ ID string `json:"id"`
+ }{}
+
+ err := json.NewDecoder(r).Decode(&u)
+ if err != nil {
+ return err
+ }
+
+ user.Name = u.Name
+ user.Email = u.Email
+ user.AvatarURL = "https://media.discordapp.net/avatars/" + u.ID + "/" + u.AvatarID + ".jpg"
+ user.UserID = u.ID
+
+ return nil
+}
+
+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,
+ },
+ Scopes: []string{},
+ }
+
+ if len(scopes) > 0 {
+ for _, scope := range scopes {
+ c.Scopes = append(c.Scopes, scope)
+ }
+ } else {
+ c.Scopes = []string{ScopeIdentify}
+ }
+
+ 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(oauth2.NoContext, token)
+ newToken, err := ts.Token()
+ if err != nil {
+ return nil, err
+ }
+ return newToken, err
+}
diff --git a/vendor/github.com/markbates/goth/providers/discord/session.go b/vendor/github.com/markbates/goth/providers/discord/session.go
new file mode 100644
index 0000000000..b3078f09a2
--- /dev/null
+++ b/vendor/github.com/markbates/goth/providers/discord/session.go
@@ -0,0 +1,65 @@
+package discord
+
+import (
+ "encoding/json"
+ "errors"
+ "github.com/markbates/goth"
+ "golang.org/x/oauth2"
+ "strings"
+ "time"
+)
+
+// Session stores data during the auth process with Discord
+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 Discord provider.
+func (s Session) GetAuthURL() (string, error) {
+ if s.AuthURL == "" {
+ return "", errors.New(goth.NoAuthUrlErrorMessage)
+ }
+ return s.AuthURL, nil
+}
+
+// Authorize completes the authorization with Discord and returns 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
+ return token.AccessToken, err
+}
+
+// Marshal marshals a session into a JSON string.
+func (s Session) Marshal() string {
+ j, _ := json.Marshal(s)
+ return string(j)
+}
+
+// String is equivalent to Marshal. It returns a JSON representation of the
+// of the session.
+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) {
+ s := &Session{}
+ err := json.NewDecoder(strings.NewReader(data)).Decode(s)
+ return s, err
+}
diff --git a/vendor/github.com/markbates/goth/providers/facebook/facebook.go b/vendor/github.com/markbates/goth/providers/facebook/facebook.go
index 5c80ca747b..dd13580a34 100644
--- a/vendor/github.com/markbates/goth/providers/facebook/facebook.go
+++ b/vendor/github.com/markbates/goth/providers/facebook/facebook.go
@@ -37,6 +37,7 @@ func New(clientKey, secret, callbackURL string, scopes ...string) *Provider {
providerName: "facebook",
}
p.config = newConfig(p, scopes)
+ p.Fields = "email,first_name,last_name,link,about,id,name,picture,location"
return p
}
@@ -46,6 +47,7 @@ type Provider struct {
Secret string
CallbackURL string
HTTPClient *http.Client
+ Fields string
config *oauth2.Config
providerName string
}
@@ -60,6 +62,16 @@ func (p *Provider) SetName(name string) {
p.providerName = name
}
+// SetCustomFields sets the fields used to return information
+// for a user.
+//
+// A list of available field values can be found at
+// https://developers.facebook.com/docs/graph-api/reference/user
+func (p *Provider) SetCustomFields(fields []string) *Provider {
+ p.Fields = strings.Join(fields, ",")
+ return p
+}
+
func (p *Provider) Client() *http.Client {
return goth.HTTPClientWithFallBack(p.HTTPClient)
}
@@ -99,7 +111,7 @@ func (p *Provider) FetchUser(session goth.Session) (goth.User, error) {
reqUrl := fmt.Sprint(
endpointProfile,
- strings.Join(p.config.Scopes, ","),
+ p.Fields,
"&access_token=",
url.QueryEscape(sess.AccessToken),
"&appsecret_proof=",
@@ -177,31 +189,17 @@ func newConfig(provider *Provider, scopes []string) *oauth2.Config {
},
Scopes: []string{
"email",
- "first_name",
- "last_name",
- "link",
- "about",
- "id",
- "name",
- "picture",
- "location",
},
}
- // creates possibility to invoke field method like 'picture.type(large)'
- var found bool
- for _, sc := range scopes {
- sc := sc
- for i, defScope := range c.Scopes {
- if defScope == strings.Split(sc, ".")[0] {
- c.Scopes[i] = sc
- found = true
- }
- }
- if !found {
- c.Scopes = append(c.Scopes, sc)
+ defaultScopes := map[string]struct{}{
+ "email": {},
+ }
+
+ for _, scope := range scopes {
+ if _, exists := defaultScopes[scope]; !exists {
+ c.Scopes = append(c.Scopes, scope)
}
- found = false
}
return c