@@ -2,6 +2,7 @@ package main
22
33import (
44 "context"
5+ "encoding/base64"
56 "encoding/json"
67 "errors"
78 "fmt"
@@ -25,6 +26,7 @@ import (
2526 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/app/redirect"
2627 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/authentication/basic"
2728 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/cookies"
29+ "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/encryption"
2830 proxyhttp "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/http"
2931 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/util"
3032
@@ -680,7 +682,29 @@ func (p *OAuthProxy) doOAuthStart(rw http.ResponseWriter, req *http.Request, ove
680682 extraParams := p.provider.Data().LoginURLParams(overrides)
681683 prepareNoCache(rw)
682684
683− csrf, err := cookies.NewCSRF(p.CookieOptions)
685+ var codeChallenge, codeVerifier, codeChallengeMethod string
686+ if p.provider.Data().CodeChallengeMethod != "" {
687+ codeChallengeMethod = p.provider.Data().CodeChallengeMethod
688+ preEncodedCodeVerifier, err := encryption.Nonce(96)
689+ if err != nil {
690+ logger.Errorf("Unable to build random string: %v", err)
691+ p.ErrorPage(rw, req, http.StatusInternalServerError, err.Error())
692+ return
693+ }
694+ codeVerifier = base64.RawURLEncoding.EncodeToString(preEncodedCodeVerifier)
695+
696+ codeChallenge, err = encryption.GenerateCodeChallenge(p.provider.Data().CodeChallengeMethod, codeVerifier)
697+ if err != nil {
698+ logger.Errorf("Error creating code challenge: %v", err)
699+ p.ErrorPage(rw, req, http.StatusInternalServerError, err.Error())
700+ return
701+ }
702+
703+ extraParams.Add("code_challenge", codeChallenge)
704+ extraParams.Add("code_challenge_method", codeChallengeMethod)
705+ }
706+
707+ csrf, err := cookies.NewCSRF(p.CookieOptions, codeVerifier)
684708 if err != nil {
685709 logger.Errorf("Error creating CSRF nonce: %v", err)
686710 p.ErrorPage(rw, req, http.StatusInternalServerError, err.Error())
@@ -732,24 +756,24 @@ func (p *OAuthProxy) OAuthCallback(rw http.ResponseWriter, req *http.Request) {
732756 return
733757 }
734758
735− session, err := p.redeemCode(req)
759+ csrf, err := cookies.LoadCSRFCookie(req, p.CookieOptions)
736760 if err != nil {
737− logger.Errorf("Error redeeming code during OAuth2 callback: %v", err)
738− p.ErrorPage(rw, req, http.StatusInternalServerError, err.Error())
761+ logger.Println(req, logger.AuthFailure, "Invalid authentication via OAuth2: unable to obtain CSRF cookie")
762+ p.ErrorPage(rw, req, http.StatusForbidden, err.Error(), "Login Failed: Unable to find a valid CSRF token. Please try again.")
739763 return
740764 }
741765
742− err = p.enrichSessionState(req.Context(), session)
766+ session, err := p.redeemCode(req, csrf.GetCodeVerifier())
743767 if err != nil {
744− logger.Errorf("Error creating session during OAuth2 callback: %v", err)
768+ logger.Errorf("Error redeeming code during OAuth2 callback: %v", err)
745769 p.ErrorPage(rw, req, http.StatusInternalServerError, err.Error())
746770 return
747771 }
748772
749− csrf, err := cookies.LoadCSRFCookie(req, p.CookieOptions)
773+ err = p.enrichSessionState(req.Context(), session)
750774 if err != nil {
751− logger.PrintAuthf(session.Email, req, logger.AuthFailure, "Invalid authentication via OAuth2: unable to obtain CSRF cookie")
752− p.ErrorPage(rw, req, http.StatusForbidden, err.Error(), "Login Failed: Unable to find a valid CSRF token. Please try again.")
775+ logger.Errorf("Error creating session during OAuth2 callback: %v", err)
776+ p.ErrorPage(rw, req, http.StatusInternalServerError, err.Error())
753777 return
754778 }
755779
@@ -799,14 +823,14 @@ func (p *OAuthProxy) OAuthCallback(rw http.ResponseWriter, req *http.Request) {
799823 }
800824}
801825
802−func (p *OAuthProxy) redeemCode(req *http.Request) (*sessionsapi.SessionState, error) {
826+func (p *OAuthProxy) redeemCode(req *http.Request, codeVerifier string) (*sessionsapi.SessionState, error) {
803827 code := req.Form.Get("code")
804828 if code == "" {
805829 return nil, providers.ErrMissingCode
806830 }
807831
808832 redirectURI := p.getOAuthRedirectURI(req)
809− s, err := p.provider.Redeem(req.Context(), redirectURI, code)
833+ s, err := p.provider.Redeem(req.Context(), redirectURI, code, codeVerifier)
810834 if err != nil {
811835 return nil, err
812836 }