summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/markbates/goth/gothic
diff options
context:
space:
mode:
authorWillem van Dreumel <willemvd@users.noreply.github.com>2017-02-22 08:14:37 +0100
committerKim "BKC" Carlbäcker <kim.carlbacker@gmail.com>2017-02-22 08:14:37 +0100
commit01d957677f160e4b5e43ce043b05e246493b34ea (patch)
treee06e3849d874ce37f02b29666ada6069b78decd9 /vendor/github.com/markbates/goth/gothic
parentfd941db246e66244ec81f43d74b8358c06173fd6 (diff)
downloadgitea-01d957677f160e4b5e43ce043b05e246493b34ea.tar.gz
gitea-01d957677f160e4b5e43ce043b05e246493b34ea.zip
Oauth2 consumer (#679)
* initial stuff for oauth2 login, fails on: * login button on the signIn page to start the OAuth2 flow and a callback for each provider Only GitHub is implemented for now * show login button only when the OAuth2 consumer is configured (and activated) * create macaron group for oauth2 urls * prevent net/http in modules (other then oauth2) * use a new data sessions oauth2 folder for storing the oauth2 session data * add missing 2FA when this is enabled on the user * add password option for OAuth2 user , for use with git over http and login to the GUI * add tip for registering a GitHub OAuth application * at startup of Gitea register all configured providers and also on adding/deleting of new providers * custom handling of errors in oauth2 request init + show better tip * add ExternalLoginUser model and migration script to add it to database * link a external account to an existing account (still need to handle wrong login and signup) and remove if user is removed * remove the linked external account from the user his settings * if user is unknown we allow him to register a new account or link it to some existing account * sign up with button on signin page (als change OAuth2Provider structure so we can store basic stuff about providers) * from gorilla/sessions docs: "Important Note: If you aren't using gorilla/mux, you need to wrap your handlers with context.ClearHandler as or else you will leak memory!" (we're using gorilla/sessions for storing oauth2 sessions) * use updated goth lib that now supports getting the OAuth2 user if the AccessToken is still valid instead of re-authenticating (prevent flooding the OAuth2 provider)
Diffstat (limited to 'vendor/github.com/markbates/goth/gothic')
-rw-r--r--vendor/github.com/markbates/goth/gothic/gothic.go219
1 files changed, 219 insertions, 0 deletions
diff --git a/vendor/github.com/markbates/goth/gothic/gothic.go b/vendor/github.com/markbates/goth/gothic/gothic.go
new file mode 100644
index 0000000000..f6aaf2d117
--- /dev/null
+++ b/vendor/github.com/markbates/goth/gothic/gothic.go
@@ -0,0 +1,219 @@
+/*
+Package gothic wraps common behaviour when using Goth. This makes it quick, and easy, to get up
+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.
+*/
+package gothic
+
+import (
+ "errors"
+ "fmt"
+ "net/http"
+ "os"
+
+ "github.com/gorilla/mux"
+ "github.com/gorilla/sessions"
+ "github.com/markbates/goth"
+)
+
+// SessionName is the key used to access the session store.
+const SessionName = "_gothic_session"
+
+// Store can/should be set by applications using gothic. The default is a cookie store.
+var Store sessions.Store
+var defaultStore sessions.Store
+
+var keySet = false
+
+func init() {
+ key := []byte(os.Getenv("SESSION_SECRET"))
+ keySet = len(key) != 0
+ Store = sessions.NewCookieStore([]byte(key))
+ defaultStore = Store
+}
+
+/*
+BeginAuthHandler is a convienence 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".
+
+BeginAuthHandler will redirect the user to the appropriate authentication end-point
+for the requested provider.
+
+See https://github.com/markbates/goth/examples/main.go to see this in action.
+*/
+func BeginAuthHandler(res http.ResponseWriter, req *http.Request) {
+ url, err := GetAuthURL(res, req)
+ if err != nil {
+ res.WriteHeader(http.StatusBadRequest)
+ fmt.Fprintln(res, err)
+ return
+ }
+
+ http.Redirect(res, req, url, http.StatusTemporaryRedirect)
+}
+
+// SetState sets the state string associated with the given request.
+// If no state string is associated with the request, one will be generated.
+// This state is sent to the provider and can be retrieved during the
+// callback.
+var SetState = func(req *http.Request) string {
+ state := req.URL.Query().Get("state")
+ if len(state) > 0 {
+ return state
+ }
+
+ return "state"
+
+}
+
+// GetState gets the state returned by the provider during the callback.
+// This is used to prevent CSRF attacks, see
+// http://tools.ietf.org/html/rfc6749#section-10.12
+var GetState = func(req *http.Request) string {
+ return req.URL.Query().Get("state")
+}
+
+/*
+GetAuthURL starts the authentication process with the requested provided.
+It will return a URL that should be used to send users to.
+
+It expects to be able to get the name of the provider from the query parameters
+as either "provider" or ":provider".
+
+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.")
+ }
+
+ providerName, err := GetProviderName(req)
+ if err != nil {
+ return "", err
+ }
+
+ provider, err := goth.GetProvider(providerName)
+ if err != nil {
+ return "", err
+ }
+ sess, err := provider.BeginAuth(SetState(req))
+ if err != nil {
+ return "", err
+ }
+
+ url, err := sess.GetAuthURL()
+ if err != nil {
+ return "", err
+ }
+
+ err = storeInSession(providerName, sess.Marshal(), req, res)
+
+ if err != nil {
+ return "", err
+ }
+
+ return url, err
+}
+
+/*
+CompleteUserAuth does what it says on the tin. It completes the authentication
+process and fetches all of the basic information about the user from the provider.
+
+It expects to be able to get the name of the provider from the query parameters
+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) {
+
+ 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.")
+ }
+
+ providerName, err := GetProviderName(req)
+ if err != nil {
+ return goth.User{}, err
+ }
+
+ provider, err := goth.GetProvider(providerName)
+ if err != nil {
+ return goth.User{}, err
+ }
+
+ value, err := getFromSession(providerName, req)
+ if err != nil {
+ return goth.User{}, err
+ }
+
+ sess, err := provider.UnmarshalSession(value)
+ if err != nil {
+ return goth.User{}, err
+ }
+
+ user, err := provider.FetchUser(sess)
+ if err == nil {
+ // user can be found with existing session data
+ return user, err
+ }
+
+ // get new token and retry fetch
+ _, err = sess.Authorize(provider, req.URL.Query())
+ if err != nil {
+ return goth.User{}, err
+ }
+
+ err = storeInSession(providerName, sess.Marshal(), req, res)
+
+ if err != nil {
+ return goth.User{}, err
+ }
+
+ return provider.FetchUser(sess)
+}
+
+// GetProviderName is a function used to get the name of a provider
+// for a given request. By default, this provider is fetched from
+// the URL query string. If you provide it in a different way,
+// assign your own function to this variable that returns the provider
+// name for your request.
+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 {
+ return p, nil
+ }
+ }
+ if provider == "" {
+ provider = req.URL.Query().Get(":provider")
+ }
+ if provider == "" {
+ return provider, errors.New("you must select a provider")
+ }
+ return provider, nil
+}
+
+func storeInSession(key string, value string, req *http.Request, res http.ResponseWriter) error {
+ session, _ := Store.Get(req, key + SessionName)
+
+ session.Values[key] = value
+
+ return session.Save(req, res)
+}
+
+func getFromSession(key string, req *http.Request) (string, error) {
+ session, _ := Store.Get(req, key + SessionName)
+
+ value := session.Values[key]
+ if value == nil {
+ return "", errors.New("could not find a matching session for this request")
+ }
+
+ return value.(string), nil
+} \ No newline at end of file