@@ -108,6 +108,7 @@ type OAuthProxy struct {
108108 sessionChain alice.Chain
109109 headersChain alice.Chain
110110 preAuthChain alice.Chain
111+ errorPage *app.ErrorPage
111112}
112113
113114// NewOAuthProxy creates a new instance of OAuthProxy from the options provided
@@ -121,8 +122,16 @@ func NewOAuthProxy(opts *options.Options, validator func(string) bool) (*OAuthPr
121122 if err != nil {
122123 return nil, fmt.Errorf("error loading templates: %v", err)
123124 }
124− proxyErrorHandler := upstream.NewProxyErrorHandler(templates.Lookup("error.html"), opts.ProxyPrefix)
125− upstreamProxy, err := upstream.NewProxy(opts.UpstreamServers, opts.GetSignatureData(), proxyErrorHandler)
125+
126+ errorPage := &app.ErrorPage{
127+ Template: templates.Lookup("error.html"),
128+ ProxyPrefix: opts.ProxyPrefix,
129+ Footer: opts.Templates.Footer,
130+ Version: VERSION,
131+ Debug: opts.Templates.Debug,
132+ }
133+
134+ upstreamProxy, err := upstream.NewProxy(opts.UpstreamServers, opts.GetSignatureData(), errorPage.ProxyErrorHandler)
126135 if err != nil {
127136 return nil, fmt.Errorf("error initialising upstream proxy: %v", err)
128137 }
@@ -224,6 +233,7 @@ func NewOAuthProxy(opts *options.Options, validator func(string) bool) (*OAuthPr
224233 sessionChain: sessionChain,
225234 headersChain: headersChain,
226235 preAuthChain: preAuthChain,
236+ errorPage: errorPage,
227237 }, nil
228238}
229239
@@ -507,14 +517,14 @@ func (p *OAuthProxy) RobotsTxt(rw http.ResponseWriter, req *http.Request) {
507517 _, err := fmt.Fprintf(rw, "User-agent: *\nDisallow: /")
508518 if err != nil {
509519 logger.Printf("Error writing robots.txt: %v", err)
510− p.ErrorPage(rw, req, http.StatusInternalServerError, "Internal Server Error", err.Error())
520+ p.ErrorPage(rw, req, http.StatusInternalServerError, err.Error())
511521 return
512522 }
513523 rw.WriteHeader(http.StatusOK)
514524}
515525
516526// ErrorPage writes an error response
517−func (p *OAuthProxy) ErrorPage(rw http.ResponseWriter, req *http.Request, code int, title string, message string) {
527+func (p *OAuthProxy) ErrorPage(rw http.ResponseWriter, req *http.Request, code int, appError string, messages ...interface{}) {
518528 redirectURL, err := p.getAppRedirect(req)
519529 if err != nil {
520530 logger.Errorf("Error obtaining redirect: %v", err)
@@ -523,32 +533,7 @@ func (p *OAuthProxy) ErrorPage(rw http.ResponseWriter, req *http.Request, code i
523533 redirectURL = "/"
524534 }
525535
526− rw.WriteHeader(code)
527−
528− // We allow unescaped template.HTML since it is user configured options
529− /* #nosec G203 */
530− t := struct {
531− Title string
532− Message string
533− ProxyPrefix string
534− StatusCode int
535− Redirect string
536− Footer template.HTML
537− Version string
538− }{
539− Title: title,
540− Message: message,
541− ProxyPrefix: p.ProxyPrefix,
542− StatusCode: code,
543− Redirect: redirectURL,
544− Footer: template.HTML(p.Footer),
545− Version: VERSION,
546− }
547−
548− if err := p.templates.ExecuteTemplate(rw, "error.html", t); err != nil {
549− logger.Printf("Error rendering error.html template: %v", err)
550− http.Error(rw, "Internal Server Error", http.StatusInternalServerError)
551− }
536+ p.errorPage.Render(rw, code, redirectURL, appError, messages...)
552537}
553538
554539// IsAllowedRequest is used to check if auth should be skipped for this request
@@ -593,7 +578,7 @@ func (p *OAuthProxy) SignInPage(rw http.ResponseWriter, req *http.Request, code
593578 err := p.ClearSessionCookie(rw, req)
594579 if err != nil {
595580 logger.Printf("Error clearing session cookie: %v", err)
596− p.ErrorPage(rw, req, http.StatusInternalServerError, "Internal Server Error", err.Error())
581+ p.ErrorPage(rw, req, http.StatusInternalServerError, err.Error())
597582 return
598583 }
599584 rw.WriteHeader(code)
@@ -601,7 +586,7 @@ func (p *OAuthProxy) SignInPage(rw http.ResponseWriter, req *http.Request, code
601586 redirectURL, err := p.getAppRedirect(req)
602587 if err != nil {
603588 logger.Errorf("Error obtaining redirect: %v", err)
604− p.ErrorPage(rw, req, http.StatusInternalServerError, "Internal Server Error", err.Error())
589+ p.ErrorPage(rw, req, http.StatusInternalServerError, err.Error())
605590 return
606591 }
607592
@@ -634,7 +619,7 @@ func (p *OAuthProxy) SignInPage(rw http.ResponseWriter, req *http.Request, code
634619 err = p.templates.ExecuteTemplate(rw, "sign_in.html", t)
635620 if err != nil {
636621 logger.Printf("Error rendering sign_in.html template: %v", err)
637− p.ErrorPage(rw, req, http.StatusInternalServerError, "Internal Server Error", err.Error())
622+ p.ErrorPage(rw, req, http.StatusInternalServerError, err.Error())
638623 }
639624}
640625
@@ -662,7 +647,7 @@ func (p *OAuthProxy) SignIn(rw http.ResponseWriter, req *http.Request) {
662647 redirect, err := p.getAppRedirect(req)
663648 if err != nil {
664649 logger.Errorf("Error obtaining redirect: %v", err)
665− p.ErrorPage(rw, req, http.StatusInternalServerError, "Internal Server Error", err.Error())
650+ p.ErrorPage(rw, req, http.StatusInternalServerError, err.Error())
666651 return
667652 }
668653
@@ -672,7 +657,7 @@ func (p *OAuthProxy) SignIn(rw http.ResponseWriter, req *http.Request) {
672657 err = p.SaveSession(rw, req, session)
673658 if err != nil {
674659 logger.Printf("Error saving session: %v", err)
675− p.ErrorPage(rw, req, http.StatusInternalServerError, "Internal Server Error", err.Error())
660+ p.ErrorPage(rw, req, http.StatusInternalServerError, err.Error())
676661 return
677662 }
678663 http.Redirect(rw, req, redirect, http.StatusFound)
@@ -711,7 +696,7 @@ func (p *OAuthProxy) UserInfo(rw http.ResponseWriter, req *http.Request) {
711696 err = json.NewEncoder(rw).Encode(userInfo)
712697 if err != nil {
713698 logger.Printf("Error encoding user info: %v", err)
714− p.ErrorPage(rw, req, http.StatusInternalServerError, "Internal Server Error", err.Error())
699+ p.ErrorPage(rw, req, http.StatusInternalServerError, err.Error())
715700 }
716701}
717702
@@ -720,13 +705,13 @@ func (p *OAuthProxy) SignOut(rw http.ResponseWriter, req *http.Request) {
720705 redirect, err := p.getAppRedirect(req)
721706 if err != nil {
722707 logger.Errorf("Error obtaining redirect: %v", err)
723− p.ErrorPage(rw, req, http.StatusInternalServerError, "Internal Server Error", err.Error())
708+ p.ErrorPage(rw, req, http.StatusInternalServerError, err.Error())
724709 return
725710 }
726711 err = p.ClearSessionCookie(rw, req)
727712 if err != nil {
728713 logger.Errorf("Error clearing session cookie: %v", err)
729− p.ErrorPage(rw, req, http.StatusInternalServerError, "Internal Server Error", err.Error())
714+ p.ErrorPage(rw, req, http.StatusInternalServerError, err.Error())
730715 return
731716 }
732717 http.Redirect(rw, req, redirect, http.StatusFound)
@@ -738,14 +723,14 @@ func (p *OAuthProxy) OAuthStart(rw http.ResponseWriter, req *http.Request) {
738723 nonce, err := encryption.Nonce()
739724 if err != nil {
740725 logger.Errorf("Error obtaining nonce: %v", err)
741− p.ErrorPage(rw, req, http.StatusInternalServerError, "Internal Server Error", err.Error())
726+ p.ErrorPage(rw, req, http.StatusInternalServerError, err.Error())
742727 return
743728 }
744729 p.SetCSRFCookie(rw, req, nonce)
745730 redirect, err := p.getAppRedirect(req)
746731 if err != nil {
747732 logger.Errorf("Error obtaining redirect: %v", err)
748− p.ErrorPage(rw, req, http.StatusInternalServerError, "Internal Server Error", err.Error())
733+ p.ErrorPage(rw, req, http.StatusInternalServerError, err.Error())
749734 return
750735 }
751736 redirectURI := p.getOAuthRedirectURI(req)
@@ -761,34 +746,36 @@ func (p *OAuthProxy) OAuthCallback(rw http.ResponseWriter, req *http.Request) {
761746 err := req.ParseForm()
762747 if err != nil {
763748 logger.Errorf("Error while parsing OAuth2 callback: %v", err)
764− p.ErrorPage(rw, req, http.StatusInternalServerError, "Internal Server Error", err.Error())
749+ p.ErrorPage(rw, req, http.StatusInternalServerError, err.Error())
765750 return
766751 }
767752 errorString := req.Form.Get("error")
768753 if errorString != "" {
769754 logger.Errorf("Error while parsing OAuth2 callback: %s", errorString)
770− p.ErrorPage(rw, req, http.StatusForbidden, "Permission Denied", errorString)
755+ message := fmt.Sprintf("Login Failed: The upstream identity provider returned an error: %s", errorString)
756+ // Set the debug message and override the non debug message to be the same for this case
757+ p.ErrorPage(rw, req, http.StatusForbidden, message, message)
771758 return
772759 }
773760
774761 session, err := p.redeemCode(req)
775762 if err != nil {
776763 logger.Errorf("Error redeeming code during OAuth2 callback: %v", err)
777− p.ErrorPage(rw, req, http.StatusInternalServerError, "Internal Server Error", "Internal Error")
764+ p.ErrorPage(rw, req, http.StatusInternalServerError, err.Error())
778765 return
779766 }
780767
781768 err = p.enrichSessionState(req.Context(), session)
782769 if err != nil {
783770 logger.Errorf("Error creating session during OAuth2 callback: %v", err)
784− p.ErrorPage(rw, req, http.StatusInternalServerError, "Internal Server Error", "Internal Error")
771+ p.ErrorPage(rw, req, http.StatusInternalServerError, err.Error())
785772 return
786773 }
787774
788775 state := strings.SplitN(req.Form.Get("state"), ":", 2)
789776 if len(state) != 2 {
790777 logger.Error("Error while parsing OAuth2 state: invalid length")
791− p.ErrorPage(rw, req, http.StatusInternalServerError, "Internal Server Error", "Invalid State")
778+ p.ErrorPage(rw, req, http.StatusInternalServerError, "State paremeter did not have expected length", "Login Failed: Invalid State after login.")
792779 return
793780 }
794781 nonce := state[0]
@@ -796,13 +783,13 @@ func (p *OAuthProxy) OAuthCallback(rw http.ResponseWriter, req *http.Request) {
796783 c, err := req.Cookie(p.CSRFCookieName)
797784 if err != nil {
798785 logger.PrintAuthf(session.Email, req, logger.AuthFailure, "Invalid authentication via OAuth2: unable to obtain CSRF cookie")
799− p.ErrorPage(rw, req, http.StatusForbidden, "Permission Denied", err.Error())
786+ p.ErrorPage(rw, req, http.StatusForbidden, err.Error(), "Login Failed: Unable to find a valid CSRF token. Please try again.")
800787 return
801788 }
802789 p.ClearCSRFCookie(rw, req)
803790 if c.Value != nonce {
804791 logger.PrintAuthf(session.Email, req, logger.AuthFailure, "Invalid authentication via OAuth2: CSRF token mismatch, potential attack")
805− p.ErrorPage(rw, req, http.StatusForbidden, "Permission Denied", "CSRF Failed")
792+ p.ErrorPage(rw, req, http.StatusForbidden, "CSRF token mismatch, potential attack", "Login Failed: Unable to find a valid CSRF token. Please try again.")
806793 return
807794 }
808795
@@ -820,13 +807,13 @@ func (p *OAuthProxy) OAuthCallback(rw http.ResponseWriter, req *http.Request) {
820807 err := p.SaveSession(rw, req, session)
821808 if err != nil {
822809 logger.Errorf("Error saving session state for %s: %v", remoteAddr, err)
823− p.ErrorPage(rw, req, http.StatusInternalServerError, "Internal Server Error", err.Error())
810+ p.ErrorPage(rw, req, http.StatusInternalServerError, err.Error())
824811 return
825812 }
826813 http.Redirect(rw, req, redirect, http.StatusFound)
827814 } else {
828815 logger.PrintAuthf(session.Email, req, logger.AuthFailure, "Invalid authentication via OAuth2: unauthorized")
829− p.ErrorPage(rw, req, http.StatusForbidden, "Permission Denied", "Invalid Account")
816+ p.ErrorPage(rw, req, http.StatusForbidden, "Invalid session: unauthorized")
830817 }
831818}
832819
@@ -908,13 +895,12 @@ func (p *OAuthProxy) Proxy(rw http.ResponseWriter, req *http.Request) {
908895 }
909896
910897 case ErrAccessDenied:
911− p.ErrorPage(rw, req, http.StatusUnauthorized, "Permission Denied", "Unauthorized")
898+ p.ErrorPage(rw, req, http.StatusForbidden, "The session failed authorization checks")
912899
913900 default:
914901 // unknown error
915902 logger.Errorf("Unexpected internal error: %v", err)
916− p.ErrorPage(rw, req, http.StatusInternalServerError,
917− "Internal Error", "Internal Error")
903+ p.ErrorPage(rw, req, http.StatusInternalServerError, err.Error())
918904 }
919905}
920906