prow/githuboauth/githuboauth.go
func (ga *Agent) HandleRedirect(client OAuthClient, getter GitHubClientGetter) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		state := r.FormValue("state")
		stateTokenRaw, err := hex.DecodeString(state)
		if err != nil {
			ga.serverError(w, "Decode state", fmt.Errorf("error with decoding state"))
		}
		stateToken := string(stateTokenRaw)
		// Check if the state token is still valid or not.
		if !xsrftoken.Valid(stateToken, ga.gc.ClientSecret, "", "") {
			ga.serverError(w, "Validate state", fmt.Errorf("state token has expired"))
			return
		}

		oauthSession, err := ga.gc.CookieStore.Get(r, oauthSessionCookie)
		if err != nil {
			ga.serverError(w, "Get cookie", err)
			return
		}
		secretState, ok := oauthSession.Values[stateKey].(string)
		if !ok {
			ga.serverError(w, "Get secret state", fmt.Errorf("empty string or cannot convert to string"))
			return
		}
		// Validate the state parameter to prevent cross-site attack.
		if state == "" || subtle.ConstantTimeCompare([]byte(state), []byte(secretState)) != 1 {
			ga.serverError(w, "Validate state", fmt.Errorf("invalid state"))
			return
		}

		// Exchanges the code for user access token.
		code := r.FormValue("code")
		token, err := client.Exchange(context.Background(), code)
		if err != nil {
			if gherror := r.FormValue("error"); len(gherror) > 0 {
				gherrorDescription := r.FormValue("error_description")
				gherrorURI := r.FormValue("error_uri")
				fields := logrus.Fields{
					"gh_error":             gherror,
					"gh_error_description": gherrorDescription,
					"gh_error_uri":         gherrorURI,
				}
				ga.logger.WithFields(fields).Error("GitHub passed errors in callback, token is not present")
				ga.serverError(w, "OAuth authentication with GitHub", fmt.Errorf(gherror))
			} else {
				ga.serverError(w, "Exchange code for token", err)
			}
			return
		}

		// New session that stores the token.
		session, err := ga.gc.CookieStore.New(r, tokenSession)
		session.Options.Secure = true
		session.Options.HttpOnly = true
		if err != nil {
			ga.serverError(w, "Create new session", err)
			return
		}

		session.Values[tokenKey] = token
		if err := session.Save(r, w); err != nil {
			ga.serverError(w, "Save session", err)
			return
		}
		ghc := getter.GetGitHubClient(token.AccessToken, false)
		user, err := ghc.GetUser("")
		if err != nil {
			ga.serverError(w, "Get user login", err)
			return
		}
		http.SetCookie(w, &http.Cookie{
			Name:    loginSession,
			Value:   *user.Login,
			Path:    "/",
			Expires: time.Now().Add(time.Hour * 24 * 30),
			Secure:  true,
		})
		http.Redirect(w, r, ga.gc.FinalRedirectURL, http.StatusFound)
	}
}
func (ga *Agent) HandleRedirect(client OAuthClient, getter GitHubClientGetter) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		state := r.FormValue("state")
		stateTokenRaw, err := hex.DecodeString(state)
		if err != nil {
			ga.serverError(w, "Decode state", fmt.Errorf("error with decoding state"))
		}
		stateToken := string(stateTokenRaw)
		// Check if the state token is still valid or not.
		if !xsrftoken.Valid(stateToken, ga.gc.ClientSecret, "", "") {
			ga.serverError(w, "Validate state", fmt.Errorf("state token has expired"))
			return
		}

		oauthSession, err := ga.gc.CookieStore.Get(r, oauthSessionCookie)
		if err != nil {
			ga.serverError(w, "Get cookie", err)
			return
		}
		secretState, ok := oauthSession.Values[stateKey].(string)
		if !ok {
			ga.serverError(w, "Get secret state", fmt.Errorf("empty string or cannot convert to string"))
			return
		}
		// Validate the state parameter to prevent cross-site attack.
		if state == "" || subtle.ConstantTimeCompare([]byte(state), []byte(secretState)) != 1 {
			ga.serverError(w, "Validate state", fmt.Errorf("invalid state"))
			return
		}

		// Exchanges the code for user access token.
		code := r.FormValue("code")
		token, err := client.Exchange(context.Background(), code)
		if err != nil {
			if gherror := r.FormValue("error"); len(gherror) > 0 {
				gherrorDescription := r.FormValue("error_description")
				gherrorURI := r.FormValue("error_uri")
				fields := logrus.Fields{
					"gh_error":             gherror,
					"gh_error_description": gherrorDescription,
					"gh_error_uri":         gherrorURI,
				}
				ga.logger.WithFields(fields).Error("GitHub passed errors in callback, token is not present")
				ga.serverError(w, "OAuth authentication with GitHub", fmt.Errorf(gherror))
			} else {
				ga.serverError(w, "Exchange code for token", err)
			}
			return
		}

		// New session that stores the token.
		session, err := ga.gc.CookieStore.New(r, tokenSession)
		session.Options.Secure = true
		session.Options.HttpOnly = true
		if err != nil {
			ga.serverError(w, "Create new session", err)
			return
		}

		session.Values[tokenKey] = token
		if err := session.Save(r, w); err != nil {
			ga.serverError(w, "Save session", err)
			return
		}
		ghc := getter.GetGitHubClient(token.AccessToken, false)
		user, err := ghc.GetUser("")
		if err != nil {
			ga.serverError(w, "Get user login", err)
			return
		}
		http.SetCookie(w, &http.Cookie{
			Name:    loginSession,
			Value:   *user.Login,
			Path:    "/",
			Expires: time.Now().Add(time.Hour * 24 * 30),
			Secure:  true,
		})
		http.Redirect(w, r, ga.gc.FinalRedirectURL, http.StatusFound)
	}
}
// HandleRedirect handles the redirection from GitHub. It exchanges the code from redirect URL for
// user access token. The access token is then saved to the cookie and the page is redirected to
// the final destination in the config, which should be the front-end.