summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/markbates/goth/gothic
diff options
context:
space:
mode:
authorLauris BH <lauris@nix.lv>2018-02-19 07:10:51 +0200
committerGitHub <noreply@github.com>2018-02-19 07:10:51 +0200
commit7b297808cee599f5ff58b9f6afa64d3ee980998e (patch)
treeb66509fc7f42094c836c6206d1e4aca12df6cfd6 /vendor/github.com/markbates/goth/gothic
parent6f751409b4f996af83e88064877271efa90534f9 (diff)
downloadgitea-7b297808cee599f5ff58b9f6afa64d3ee980998e.tar.gz
gitea-7b297808cee599f5ff58b9f6afa64d3ee980998e.zip
Update markbates/goth library (#3533)
Signed-off-by: Lauris Bukšis-Haberkorns <lauris@nix.lv>
Diffstat (limited to 'vendor/github.com/markbates/goth/gothic')
-rw-r--r--vendor/github.com/markbates/goth/gothic/gothic.go164
1 files changed, 143 insertions, 21 deletions
diff --git a/vendor/github.com/markbates/goth/gothic/gothic.go b/vendor/github.com/markbates/goth/gothic/gothic.go
index f6aaf2d117..8b8e114940 100644
--- a/vendor/github.com/markbates/goth/gothic/gothic.go
+++ b/vendor/github.com/markbates/goth/gothic/gothic.go
@@ -8,10 +8,18 @@ See https://github.com/markbates/goth/examples/main.go to see this in action.
package gothic
import (
+ "bytes"
+ "compress/gzip"
+ "encoding/base64"
"errors"
"fmt"
+ "io/ioutil"
+ "math/rand"
"net/http"
+ "net/url"
"os"
+ "strings"
+ "time"
"github.com/gorilla/mux"
"github.com/gorilla/sessions"
@@ -27,15 +35,21 @@ var defaultStore sessions.Store
var keySet = false
+var gothicRand *rand.Rand
+
func init() {
key := []byte(os.Getenv("SESSION_SECRET"))
keySet = len(key) != 0
- Store = sessions.NewCookieStore([]byte(key))
+
+ cookieStore := sessions.NewCookieStore([]byte(key))
+ cookieStore.Options.HttpOnly = true
+ Store = cookieStore
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
as either "provider" or ":provider".
@@ -65,8 +79,16 @@ var SetState = func(req *http.Request) string {
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.
@@ -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.
*/
func GetAuthURL(res http.ResponseWriter, req *http.Request) (string, error) {
-
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.")
}
@@ -130,7 +151,7 @@ as either "provider" or ":provider".
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) {
-
+ defer Logout(res, req)
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.")
}
@@ -155,6 +176,11 @@ var CompleteUserAuth = func(res http.ResponseWriter, req *http.Request) (goth.Us
return goth.User{}, err
}
+ err = validateState(req, sess)
+ if err != nil {
+ return goth.User{}, err
+ }
+
user, err := provider.FetchUser(sess)
if err == nil {
// 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 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
@@ -184,36 +246,96 @@ var CompleteUserAuth = func(res http.ResponseWriter, req *http.Request) (goth.Us
var GetProviderName = getProviderName
func getProviderName(req *http.Request) (string, error) {
- provider := req.URL.Query().Get("provider")
- if provider == "" {
- if p, ok := mux.Vars(req)["provider"]; ok {
+
+ // get all the used providers
+ 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
}
}
- 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
+ }
+
+ // try to get it from the context's value of "provider" key
+ if p, ok := mux.Vars(req)["provider"]; ok {
+ return p, nil
}
- return provider, 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 {
- 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)
}
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)
+ if err != nil {
+ return "", errors.New("could not find a matching session for this request")
+ }
+ return value, nil
+}
+
+func getSessionValue(session *sessions.Session, key string) (string, error) {
value := session.Values[key]
if value == nil {
- return "", errors.New("could not find a matching session for this request")
+ return "", fmt.Errorf("could not find a matching session for this request")
}
- return value.(string), nil
-} \ No newline at end of file
+ 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
+}