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