@@ -18,6 +18,7 @@ import (
18 middlewareapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/middleware"
18 middlewareapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/middleware"
19 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
19 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options"
20 sessionsapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
20 sessionsapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
21+ "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/app"
21 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/authentication/basic"
22 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/authentication/basic"
22 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/cookies"
23 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/cookies"
23 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/encryption"
24 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/encryption"
@@ -116,7 +117,10 @@ func NewOAuthProxy(opts *options.Options, validator func(string) bool) (*OAuthPr
116 return nil, fmt.Errorf("error initialising session store: %v", err)
117 return nil, fmt.Errorf("error initialising session store: %v", err)
119− templates := loadTemplates(opts.CustomTemplatesDir)
120+ templates, err := app.LoadTemplates(opts.Templates.Path)
122+ return nil, fmt.Errorf("error loading templates: %v", err)
120 proxyErrorHandler := upstream.NewProxyErrorHandler(templates.Lookup("error.html"), opts.ProxyPrefix)
124 proxyErrorHandler := upstream.NewProxyErrorHandler(templates.Lookup("error.html"), opts.ProxyPrefix)
121 upstreamProxy, err := upstream.NewProxy(opts.UpstreamServers, opts.GetSignatureData(), proxyErrorHandler)
125 upstreamProxy, err := upstream.NewProxy(opts.UpstreamServers, opts.GetSignatureData(), proxyErrorHandler)
122 if err != nil {
126 if err != nil {
@@ -211,12 +215,12 @@ func NewOAuthProxy(opts *options.Options, validator func(string) bool) (*OAuthPr
211 SkipProviderButton: opts.SkipProviderButton,
215 SkipProviderButton: opts.SkipProviderButton,
212 templates: templates,
216 templates: templates,
213 trustedIPs: trustedIPs,
217 trustedIPs: trustedIPs,
214− Banner: opts.Banner,
218+ Banner: opts.Templates.Banner,
215− Footer: opts.Footer,
219+ Footer: opts.Templates.Footer,
216 SignInMessage: buildSignInMessage(opts),
220 SignInMessage: buildSignInMessage(opts),
218 basicAuthValidator: basicAuthValidator,
222 basicAuthValidator: basicAuthValidator,
219− displayHtpasswdForm: basicAuthValidator != nil && opts.DisplayHtpasswdForm,
223+ displayHtpasswdForm: basicAuthValidator != nil && opts.Templates.DisplayLoginForm,
220 sessionChain: sessionChain,
224 sessionChain: sessionChain,
221 headersChain: headersChain,
225 headersChain: headersChain,
222 preAuthChain: preAuthChain,
226 preAuthChain: preAuthChain,
@@ -301,11 +305,11 @@ func buildHeadersChain(opts *options.Options) (alice.Chain, error) {
302func buildSignInMessage(opts *options.Options) string {
306func buildSignInMessage(opts *options.Options) string {
303 var msg string
307 var msg string
304− if len(opts.Banner) >= 1 {
308+ if len(opts.Templates.Banner) >= 1 {
305− if opts.Banner == "-" {
309+ if opts.Templates.Banner == "-" {
308− msg = opts.Banner
312+ msg = opts.Templates.Banner
310 } else if len(opts.EmailDomains) != 0 && opts.AuthenticatedEmailsFile == "" {
314 } else if len(opts.EmailDomains) != 0 && opts.AuthenticatedEmailsFile == "" {
311 if len(opts.EmailDomains) > 1 {
315 if len(opts.EmailDomains) > 1 {
@@ -478,7 +482,7 @@ func (p *OAuthProxy) serveHTTP(rw http.ResponseWriter, req *http.Request) {
479 switch path := req.URL.Path; {
483 switch path := req.URL.Path; {
480 case path == p.RobotsPath:
484 case path == p.RobotsPath:
481− p.RobotsTxt(rw)
485+ p.RobotsTxt(rw, req)
482 case p.IsAllowedRequest(req):
486 case p.IsAllowedRequest(req):
483 p.SkipAuthProxy(rw, req)
487 p.SkipAuthProxy(rw, req)
484 case path == p.SignInPath:
488 case path == p.SignInPath:
@@ -499,30 +503,49 @@ func (p *OAuthProxy) serveHTTP(rw http.ResponseWriter, req *http.Request) {
501// RobotsTxt disallows scraping pages from the OAuthProxy
505// 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) {
503 _, err := fmt.Fprintf(rw, "User-agent: *\nDisallow: /")
507 _, err := fmt.Fprintf(rw, "User-agent: *\nDisallow: /")
504 if err != nil {
508 if err != nil {
505 logger.Printf("Error writing robots.txt: %v", err)
509 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())
509 rw.WriteHeader(http.StatusOK)
513 rw.WriteHeader(http.StatusOK)
512// ErrorPage writes an error response
516// 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)
520+ logger.Errorf("Error obtaining redirect: %v", err)
522+ if redirectURL == p.SignInPath || redirectURL == "" {
514 rw.WriteHeader(code)
526 rw.WriteHeader(code)
528+ // We allow unescaped template.HTML since it is user configured options
515 t := struct {
530 t := struct {
516 Title string
531 Title string
517 Message string
532 Message string
518 ProxyPrefix string
533 ProxyPrefix string
536+ Footer template.HTML
520− Title: fmt.Sprintf("%d %s", code, title),
539+ Title: title,
521 Message: message,
540 Message: message,
522 ProxyPrefix: p.ProxyPrefix,
541 ProxyPrefix: p.ProxyPrefix,
543+ Redirect: redirectURL,
544+ Footer: template.HTML(p.Footer),
524− err := p.templates.ExecuteTemplate(rw, "error.html", t)
548+ if err := p.templates.ExecuteTemplate(rw, "error.html", t); err != nil {
526 logger.Printf("Error rendering error.html template: %v", err)
549 logger.Printf("Error rendering error.html template: %v", err)
527 http.Error(rw, "Internal Server Error", http.StatusInternalServerError)
550 http.Error(rw, "Internal Server Error", http.StatusInternalServerError)
@@ -570,7 +593,7 @@ func (p *OAuthProxy) SignInPage(rw http.ResponseWriter, req *http.Request, code
570 err := p.ClearSessionCookie(rw, req)
593 err := p.ClearSessionCookie(rw, req)
571 if err != nil {
594 if err != nil {
572 logger.Printf("Error clearing session cookie: %v", err)
595 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())
576 rw.WriteHeader(code)
599 rw.WriteHeader(code)
@@ -578,7 +601,7 @@ func (p *OAuthProxy) SignInPage(rw http.ResponseWriter, req *http.Request, code
578 redirectURL, err := p.getAppRedirect(req)
601 redirectURL, err := p.getAppRedirect(req)
579 if err != nil {
602 if err != nil {
580 logger.Errorf("Error obtaining redirect: %v", err)
603 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())
@@ -611,7 +634,7 @@ func (p *OAuthProxy) SignInPage(rw http.ResponseWriter, req *http.Request, code
611 err = p.templates.ExecuteTemplate(rw, "sign_in.html", t)
634 err = p.templates.ExecuteTemplate(rw, "sign_in.html", t)
612 if err != nil {
635 if err != nil {
613 logger.Printf("Error rendering sign_in.html template: %v", err)
636 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())
@@ -639,7 +662,7 @@ func (p *OAuthProxy) SignIn(rw http.ResponseWriter, req *http.Request) {
639 redirect, err := p.getAppRedirect(req)
662 redirect, err := p.getAppRedirect(req)
640 if err != nil {
663 if err != nil {
641 logger.Errorf("Error obtaining redirect: %v", err)
664 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())
@@ -649,7 +672,7 @@ func (p *OAuthProxy) SignIn(rw http.ResponseWriter, req *http.Request) {
649 err = p.SaveSession(rw, req, session)
672 err = p.SaveSession(rw, req, session)
650 if err != nil {
673 if err != nil {
651 logger.Printf("Error saving session: %v", err)
674 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())
655 http.Redirect(rw, req, redirect, http.StatusFound)
678 http.Redirect(rw, req, redirect, http.StatusFound)
@@ -688,7 +711,7 @@ func (p *OAuthProxy) UserInfo(rw http.ResponseWriter, req *http.Request) {
688 err = json.NewEncoder(rw).Encode(userInfo)
711 err = json.NewEncoder(rw).Encode(userInfo)
689 if err != nil {
712 if err != nil {
690 logger.Printf("Error encoding user info: %v", err)
713 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())
@@ -697,13 +720,13 @@ func (p *OAuthProxy) SignOut(rw http.ResponseWriter, req *http.Request) {
697 redirect, err := p.getAppRedirect(req)
720 redirect, err := p.getAppRedirect(req)
698 if err != nil {
721 if err != nil {
699 logger.Errorf("Error obtaining redirect: %v", err)
722 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())
703 err = p.ClearSessionCookie(rw, req)
726 err = p.ClearSessionCookie(rw, req)
704 if err != nil {
727 if err != nil {
705 logger.Errorf("Error clearing session cookie: %v", err)
728 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())
709 http.Redirect(rw, req, redirect, http.StatusFound)
732 http.Redirect(rw, req, redirect, http.StatusFound)
@@ -715,14 +738,14 @@ func (p *OAuthProxy) OAuthStart(rw http.ResponseWriter, req *http.Request) {
715 nonce, err := encryption.Nonce()
738 nonce, err := encryption.Nonce()
716 if err != nil {
739 if err != nil {
717 logger.Errorf("Error obtaining nonce: %v", err)
740 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())
721 p.SetCSRFCookie(rw, req, nonce)
744 p.SetCSRFCookie(rw, req, nonce)
722 redirect, err := p.getAppRedirect(req)
745 redirect, err := p.getAppRedirect(req)
723 if err != nil {
746 if err != nil {
724 logger.Errorf("Error obtaining redirect: %v", err)
747 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())
728 redirectURI := p.getOAuthRedirectURI(req)
751 redirectURI := p.getOAuthRedirectURI(req)
@@ -738,34 +761,34 @@ func (p *OAuthProxy) OAuthCallback(rw http.ResponseWriter, req *http.Request) {
738 err := req.ParseForm()
761 err := req.ParseForm()
739 if err != nil {
762 if err != nil {
740 logger.Errorf("Error while parsing OAuth2 callback: %v", err)
763 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())
744 errorString := req.Form.Get("error")
767 errorString := req.Form.Get("error")
745 if errorString != "" {
768 if errorString != "" {
746 logger.Errorf("Error while parsing OAuth2 callback: %s", errorString)
769 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)
751 session, err := p.redeemCode(req)
774 session, err := p.redeemCode(req)
752 if err != nil {
775 if err != nil {
753 logger.Errorf("Error redeeming code during OAuth2 callback: %v", err)
776 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")
758 err = p.enrichSessionState(req.Context(), session)
781 err = p.enrichSessionState(req.Context(), session)
759 if err != nil {
782 if err != nil {
760 logger.Errorf("Error creating session during OAuth2 callback: %v", err)
783 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")
765 state := strings.SplitN(req.Form.Get("state"), ":", 2)
788 state := strings.SplitN(req.Form.Get("state"), ":", 2)
766 if len(state) != 2 {
789 if len(state) != 2 {
767 logger.Error("Error while parsing OAuth2 state: invalid length")
790 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")
771 nonce := state[0]
794 nonce := state[0]
@@ -773,13 +796,13 @@ func (p *OAuthProxy) OAuthCallback(rw http.ResponseWriter, req *http.Request) {
773 c, err := req.Cookie(p.CSRFCookieName)
796 c, err := req.Cookie(p.CSRFCookieName)
774 if err != nil {
797 if err != nil {
775 logger.PrintAuthf(session.Email, req, logger.AuthFailure, "Invalid authentication via OAuth2: unable to obtain CSRF cookie")
798 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())
779 p.ClearCSRFCookie(rw, req)
802 p.ClearCSRFCookie(rw, req)
780 if c.Value != nonce {
803 if c.Value != nonce {
781 logger.PrintAuthf(session.Email, req, logger.AuthFailure, "Invalid authentication via OAuth2: CSRF token mismatch, potential attack")
804 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")
@@ -797,13 +820,13 @@ func (p *OAuthProxy) OAuthCallback(rw http.ResponseWriter, req *http.Request) {
797 err := p.SaveSession(rw, req, session)
820 err := p.SaveSession(rw, req, session)
798 if err != nil {
821 if err != nil {
799 logger.Errorf("Error saving session state for %s: %v", remoteAddr, err)
822 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())
803 http.Redirect(rw, req, redirect, http.StatusFound)
826 http.Redirect(rw, req, redirect, http.StatusFound)
805 logger.PrintAuthf(session.Email, req, logger.AuthFailure, "Invalid authentication via OAuth2: unauthorized")
828 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")
@@ -885,12 +908,12 @@ func (p *OAuthProxy) Proxy(rw http.ResponseWriter, req *http.Request) {
887 case ErrAccessDenied:
910 case ErrAccessDenied:
888− p.ErrorPage(rw, http.StatusUnauthorized, "Permission Denied", "Unauthorized")
911+ p.ErrorPage(rw, req, http.StatusUnauthorized, "Permission Denied", "Unauthorized")
891 // unknown error
914 // unknown error
892 logger.Errorf("Unexpected internal error: %v", err)
915 logger.Errorf("Unexpected internal error: %v", err)
893− p.ErrorPage(rw, http.StatusInternalServerError,
916+ p.ErrorPage(rw, req, http.StatusInternalServerError,
894 "Internal Error", "Internal Error")
917 "Internal Error", "Internal Error")