diff options
author | Lauris BH <lauris@nix.lv> | 2018-02-19 07:10:51 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-02-19 07:10:51 +0200 |
commit | 7b297808cee599f5ff58b9f6afa64d3ee980998e (patch) | |
tree | b66509fc7f42094c836c6206d1e4aca12df6cfd6 /vendor/github.com/markbates/goth/gothic | |
parent | 6f751409b4f996af83e88064877271efa90534f9 (diff) | |
download | gitea-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.go | 164 |
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 +} |