@@ -18,6 +18,7 @@ import (
1818 middlewareapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/middleware"
1919 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
2020 sessionsapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
21+ "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/app"
2122 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/authentication/basic"
2223 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/cookies"
2324 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/encryption"
@@ -116,7 +117,10 @@ func NewOAuthProxy(opts *options.Options, validator func(string) bool) (*OAuthPr
116117 return nil, fmt.Errorf("error initialising session store: %v", err)
117118 }
118119
119− templates := loadTemplates(opts.CustomTemplatesDir)
120+ templates, err := app.LoadTemplates(opts.Templates.Path)
121+ if err != nil {
122+ return nil, fmt.Errorf("error loading templates: %v", err)
123+ }
120124 proxyErrorHandler := upstream.NewProxyErrorHandler(templates.Lookup("error.html"), opts.ProxyPrefix)
121125 upstreamProxy, err := upstream.NewProxy(opts.UpstreamServers, opts.GetSignatureData(), proxyErrorHandler)
122126 if err != nil {
@@ -211,12 +215,12 @@ func NewOAuthProxy(opts *options.Options, validator func(string) bool) (*OAuthPr
211215 SkipProviderButton: opts.SkipProviderButton,
212216 templates: templates,
213217 trustedIPs: trustedIPs,
214− Banner: opts.Banner,
215− Footer: opts.Footer,
218+ Banner: opts.Templates.Banner,
219+ Footer: opts.Templates.Footer,
216220 SignInMessage: buildSignInMessage(opts),
217221
218222 basicAuthValidator: basicAuthValidator,
219− displayHtpasswdForm: basicAuthValidator != nil && opts.DisplayHtpasswdForm,
223+ displayHtpasswdForm: basicAuthValidator != nil && opts.Templates.DisplayLoginForm,
220224 sessionChain: sessionChain,
221225 headersChain: headersChain,
222226 preAuthChain: preAuthChain,
@@ -301,11 +305,11 @@ func buildHeadersChain(opts *options.Options) (alice.Chain, error) {
301305
302306func buildSignInMessage(opts *options.Options) string {
303307 var msg string
304− if len(opts.Banner) >= 1 {
305− if opts.Banner == "-" {
308+ if len(opts.Templates.Banner) >= 1 {
309+ if opts.Templates.Banner == "-" {
306310 msg = ""
307311 } else {
308− msg = opts.Banner
312+ msg = opts.Templates.Banner
309313 }
310314 } else if len(opts.EmailDomains) != 0 && opts.AuthenticatedEmailsFile == "" {
311315 if len(opts.EmailDomains) > 1 {
@@ -478,7 +482,7 @@ func (p *OAuthProxy) serveHTTP(rw http.ResponseWriter, req *http.Request) {
478482
479483 switch path := req.URL.Path; {
480484 case path == p.RobotsPath:
481− p.RobotsTxt(rw)
485+ p.RobotsTxt(rw, req)
482486 case p.IsAllowedRequest(req):
483487 p.SkipAuthProxy(rw, req)
484488 case path == p.SignInPath:
@@ -499,30 +503,49 @@ func (p *OAuthProxy) serveHTTP(rw http.ResponseWriter, req *http.Request) {
499503}
500504
501505// RobotsTxt disallows scraping pages from the OAuthProxy
502−func (p *OAuthProxy) RobotsTxt(rw http.ResponseWriter) {
506+func (p *OAuthProxy) RobotsTxt(rw http.ResponseWriter, req *http.Request) {
503507 _, err := fmt.Fprintf(rw, "User-agent: *\nDisallow: /")
504508 if err != nil {
505509 logger.Printf("Error writing robots.txt: %v", err)
506− p.ErrorPage(rw, http.StatusInternalServerError, "Internal Server Error", err.Error())
510+ p.ErrorPage(rw, req, http.StatusInternalServerError, "Internal Server Error", err.Error())
507511 return
508512 }
509513 rw.WriteHeader(http.StatusOK)
510514}
511515
512516// ErrorPage writes an error response
513−func (p *OAuthProxy) ErrorPage(rw http.ResponseWriter, code int, title string, message string) {
517+func (p *OAuthProxy) ErrorPage(rw http.ResponseWriter, req *http.Request, code int, title string, message string) {
518+ redirectURL, err := p.getAppRedirect(req)
519+ if err != nil {
520+ logger.Errorf("Error obtaining redirect: %v", err)
521+ }
522+ if redirectURL == p.SignInPath || redirectURL == "" {
523+ redirectURL = "/"
524+ }
525+
514526 rw.WriteHeader(code)
527+
528+ // We allow unescaped template.HTML since it is user configured options
529+ /* #nosec G203 */
515530 t := struct {
516531 Title string
517532 Message string
518533 ProxyPrefix string
534+ StatusCode int
535+ Redirect string
536+ Footer template.HTML
537+ Version string
519538 }{
520− Title: fmt.Sprintf("%d %s", code, title),
539+ Title: title,
521540 Message: message,
522541 ProxyPrefix: p.ProxyPrefix,
542+ StatusCode: code,
543+ Redirect: redirectURL,
544+ Footer: template.HTML(p.Footer),
545+ Version: VERSION,
523546 }
524− err := p.templates.ExecuteTemplate(rw, "error.html", t)
525− if err != nil {
547+
548+ if err := p.templates.ExecuteTemplate(rw, "error.html", t); err != nil {
526549 logger.Printf("Error rendering error.html template: %v", err)
527550 http.Error(rw, "Internal Server Error", http.StatusInternalServerError)
528551 }
@@ -570,7 +593,7 @@ func (p *OAuthProxy) SignInPage(rw http.ResponseWriter, req *http.Request, code
570593 err := p.ClearSessionCookie(rw, req)
571594 if err != nil {
572595 logger.Printf("Error clearing session cookie: %v", err)
573− p.ErrorPage(rw, http.StatusInternalServerError, "Internal Server Error", err.Error())
596+ p.ErrorPage(rw, req, http.StatusInternalServerError, "Internal Server Error", err.Error())
574597 return
575598 }
576599 rw.WriteHeader(code)
@@ -578,7 +601,7 @@ func (p *OAuthProxy) SignInPage(rw http.ResponseWriter, req *http.Request, code
578601 redirectURL, err := p.getAppRedirect(req)
579602 if err != nil {
580603 logger.Errorf("Error obtaining redirect: %v", err)
581− p.ErrorPage(rw, http.StatusInternalServerError, "Internal Server Error", err.Error())
604+ p.ErrorPage(rw, req, http.StatusInternalServerError, "Internal Server Error", err.Error())
582605 return
583606 }
584607
@@ -611,7 +634,7 @@ func (p *OAuthProxy) SignInPage(rw http.ResponseWriter, req *http.Request, code
611634 err = p.templates.ExecuteTemplate(rw, "sign_in.html", t)
612635 if err != nil {
613636 logger.Printf("Error rendering sign_in.html template: %v", err)
614− p.ErrorPage(rw, http.StatusInternalServerError, "Internal Server Error", err.Error())
637+ p.ErrorPage(rw, req, http.StatusInternalServerError, "Internal Server Error", err.Error())
615638 }
616639}
617640
@@ -639,7 +662,7 @@ func (p *OAuthProxy) SignIn(rw http.ResponseWriter, req *http.Request) {
639662 redirect, err := p.getAppRedirect(req)
640663 if err != nil {
641664 logger.Errorf("Error obtaining redirect: %v", err)
642− p.ErrorPage(rw, http.StatusInternalServerError, "Internal Server Error", err.Error())
665+ p.ErrorPage(rw, req, http.StatusInternalServerError, "Internal Server Error", err.Error())
643666 return
644667 }
645668
@@ -649,7 +672,7 @@ func (p *OAuthProxy) SignIn(rw http.ResponseWriter, req *http.Request) {
649672 err = p.SaveSession(rw, req, session)
650673 if err != nil {
651674 logger.Printf("Error saving session: %v", err)
652− p.ErrorPage(rw, http.StatusInternalServerError, "Internal Server Error", err.Error())
675+ p.ErrorPage(rw, req, http.StatusInternalServerError, "Internal Server Error", err.Error())
653676 return
654677 }
655678 http.Redirect(rw, req, redirect, http.StatusFound)
@@ -688,7 +711,7 @@ func (p *OAuthProxy) UserInfo(rw http.ResponseWriter, req *http.Request) {
688711 err = json.NewEncoder(rw).Encode(userInfo)
689712 if err != nil {
690713 logger.Printf("Error encoding user info: %v", err)
691− p.ErrorPage(rw, http.StatusInternalServerError, "Internal Server Error", err.Error())
714+ p.ErrorPage(rw, req, http.StatusInternalServerError, "Internal Server Error", err.Error())
692715 }
693716}
694717
@@ -697,13 +720,13 @@ func (p *OAuthProxy) SignOut(rw http.ResponseWriter, req *http.Request) {
697720 redirect, err := p.getAppRedirect(req)
698721 if err != nil {
699722 logger.Errorf("Error obtaining redirect: %v", err)
700− p.ErrorPage(rw, http.StatusInternalServerError, "Internal Server Error", err.Error())
723+ p.ErrorPage(rw, req, http.StatusInternalServerError, "Internal Server Error", err.Error())
701724 return
702725 }
703726 err = p.ClearSessionCookie(rw, req)
704727 if err != nil {
705728 logger.Errorf("Error clearing session cookie: %v", err)
706− p.ErrorPage(rw, http.StatusInternalServerError, "Internal Server Error", err.Error())
729+ p.ErrorPage(rw, req, http.StatusInternalServerError, "Internal Server Error", err.Error())
707730 return
708731 }
709732 http.Redirect(rw, req, redirect, http.StatusFound)
@@ -715,14 +738,14 @@ func (p *OAuthProxy) OAuthStart(rw http.ResponseWriter, req *http.Request) {
715738 nonce, err := encryption.Nonce()
716739 if err != nil {
717740 logger.Errorf("Error obtaining nonce: %v", err)
718− p.ErrorPage(rw, http.StatusInternalServerError, "Internal Server Error", err.Error())
741+ p.ErrorPage(rw, req, http.StatusInternalServerError, "Internal Server Error", err.Error())
719742 return
720743 }
721744 p.SetCSRFCookie(rw, req, nonce)
722745 redirect, err := p.getAppRedirect(req)
723746 if err != nil {
724747 logger.Errorf("Error obtaining redirect: %v", err)
725− p.ErrorPage(rw, http.StatusInternalServerError, "Internal Server Error", err.Error())
748+ p.ErrorPage(rw, req, http.StatusInternalServerError, "Internal Server Error", err.Error())
726749 return
727750 }
728751 redirectURI := p.getOAuthRedirectURI(req)
@@ -738,34 +761,34 @@ func (p *OAuthProxy) OAuthCallback(rw http.ResponseWriter, req *http.Request) {
738761 err := req.ParseForm()
739762 if err != nil {
740763 logger.Errorf("Error while parsing OAuth2 callback: %v", err)
741− p.ErrorPage(rw, http.StatusInternalServerError, "Internal Server Error", err.Error())
764+ p.ErrorPage(rw, req, http.StatusInternalServerError, "Internal Server Error", err.Error())
742765 return
743766 }
744767 errorString := req.Form.Get("error")
745768 if errorString != "" {
746769 logger.Errorf("Error while parsing OAuth2 callback: %s", errorString)
747− p.ErrorPage(rw, http.StatusForbidden, "Permission Denied", errorString)
770+ p.ErrorPage(rw, req, http.StatusForbidden, "Permission Denied", errorString)
748771 return
749772 }
750773
751774 session, err := p.redeemCode(req)
752775 if err != nil {
753776 logger.Errorf("Error redeeming code during OAuth2 callback: %v", err)
754− p.ErrorPage(rw, http.StatusInternalServerError, "Internal Server Error", "Internal Error")
777+ p.ErrorPage(rw, req, http.StatusInternalServerError, "Internal Server Error", "Internal Error")
755778 return
756779 }
757780
758781 err = p.enrichSessionState(req.Context(), session)
759782 if err != nil {
760783 logger.Errorf("Error creating session during OAuth2 callback: %v", err)
761− p.ErrorPage(rw, http.StatusInternalServerError, "Internal Server Error", "Internal Error")
784+ p.ErrorPage(rw, req, http.StatusInternalServerError, "Internal Server Error", "Internal Error")
762785 return
763786 }
764787
765788 state := strings.SplitN(req.Form.Get("state"), ":", 2)
766789 if len(state) != 2 {
767790 logger.Error("Error while parsing OAuth2 state: invalid length")
768− p.ErrorPage(rw, http.StatusInternalServerError, "Internal Server Error", "Invalid State")
791+ p.ErrorPage(rw, req, http.StatusInternalServerError, "Internal Server Error", "Invalid State")
769792 return
770793 }
771794 nonce := state[0]
@@ -773,13 +796,13 @@ func (p *OAuthProxy) OAuthCallback(rw http.ResponseWriter, req *http.Request) {
773796 c, err := req.Cookie(p.CSRFCookieName)
774797 if err != nil {
775798 logger.PrintAuthf(session.Email, req, logger.AuthFailure, "Invalid authentication via OAuth2: unable to obtain CSRF cookie")
776− p.ErrorPage(rw, http.StatusForbidden, "Permission Denied", err.Error())
799+ p.ErrorPage(rw, req, http.StatusForbidden, "Permission Denied", err.Error())
777800 return
778801 }
779802 p.ClearCSRFCookie(rw, req)
780803 if c.Value != nonce {
781804 logger.PrintAuthf(session.Email, req, logger.AuthFailure, "Invalid authentication via OAuth2: CSRF token mismatch, potential attack")
782− p.ErrorPage(rw, http.StatusForbidden, "Permission Denied", "CSRF Failed")
805+ p.ErrorPage(rw, req, http.StatusForbidden, "Permission Denied", "CSRF Failed")
783806 return
784807 }
785808
@@ -797,13 +820,13 @@ func (p *OAuthProxy) OAuthCallback(rw http.ResponseWriter, req *http.Request) {
797820 err := p.SaveSession(rw, req, session)
798821 if err != nil {
799822 logger.Errorf("Error saving session state for %s: %v", remoteAddr, err)
800− p.ErrorPage(rw, http.StatusInternalServerError, "Internal Server Error", err.Error())
823+ p.ErrorPage(rw, req, http.StatusInternalServerError, "Internal Server Error", err.Error())
801824 return
802825 }
803826 http.Redirect(rw, req, redirect, http.StatusFound)
804827 } else {
805828 logger.PrintAuthf(session.Email, req, logger.AuthFailure, "Invalid authentication via OAuth2: unauthorized")
806− p.ErrorPage(rw, http.StatusForbidden, "Permission Denied", "Invalid Account")
829+ p.ErrorPage(rw, req, http.StatusForbidden, "Permission Denied", "Invalid Account")
807830 }
808831}
809832
@@ -885,12 +908,12 @@ func (p *OAuthProxy) Proxy(rw http.ResponseWriter, req *http.Request) {
885908 }
886909
887910 case ErrAccessDenied:
888− p.ErrorPage(rw, http.StatusUnauthorized, "Permission Denied", "Unauthorized")
911+ p.ErrorPage(rw, req, http.StatusUnauthorized, "Permission Denied", "Unauthorized")
889912
890913 default:
891914 // unknown error
892915 logger.Errorf("Unexpected internal error: %v", err)
893− p.ErrorPage(rw, http.StatusInternalServerError,
916+ p.ErrorPage(rw, req, http.StatusInternalServerError,
894917 "Internal Error", "Internal Error")
895918 }
896919}