@@ -13,7 +13,6 @@ import (
1313 "maps"
1414 "net/http"
1515 "net/url"
16- "path"
1716 "sort"
1817 "strconv"
1918 "strings"
@@ -881,30 +880,13 @@ func (s *Server) finalizeLogin(ctx context.Context, identity connector.Identity,
881880 userIdentity = & ui
882881 }
883882
884- // an HMAC is used here to ensure that the request ID is unpredictable, ensuring that an attacker who intercepted the original
885- // flow would be unable to poll for the result at the /approval endpoint
886- h := hmac .New (sha256 .New , authReq .HMACKey )
887- h .Write ([]byte (authReq .ID ))
888- mac := h .Sum (nil )
889- hmacParam := base64 .RawURLEncoding .EncodeToString (mac )
890-
891883 // Check if the client requires MFA.
892884 mfaChain , err := s .mfaChainForClient (ctx , authReq .ClientID , authReq .ConnectorID )
893885 if err != nil {
894886 return "" , false , fmt .Errorf ("failed to get MFA chain for client: %v" , err )
895887 }
896888 if len (mfaChain ) > 0 {
897- // Redirect to MFA verification starting with the first authenticator.
898- // Each authenticator redirects to the next one in the chain upon success.
899- // HMAC includes authenticatorID to prevent skipping steps by URL manipulation.
900- h .Reset ()
901- h .Write ([]byte (authReq .ID + "|" + mfaChain [0 ]))
902- v := url.Values {}
903- v .Set ("req" , authReq .ID )
904- v .Set ("hmac" , base64 .RawURLEncoding .EncodeToString (h .Sum (nil )))
905- v .Set ("authenticator" , mfaChain [0 ])
906- returnURL := path .Join (s .issuerURL .Path , "/mfa/verify" ) + "?" + v .Encode ()
907- return returnURL , false , nil
889+ return s .buildMFARedirectURL (authReq , mfaChain [0 ]), false , nil
908890 }
909891
910892 // No MFA required — mark as validated.
@@ -927,8 +909,7 @@ func (s *Server) finalizeLogin(ctx context.Context, identity connector.Identity,
927909 }
928910 }
929911
930- returnURL := path .Join (s .issuerURL .Path , "/approval" ) + "?req=" + authReq .ID + "&hmac=" + hmacParam
931- return returnURL , false , nil
912+ return s .buildApprovalURL (authReq ), false , nil
932913}
933914
934915func (s * Server ) handleApproval (w http.ResponseWriter , r * http.Request ) {
@@ -971,14 +952,7 @@ func (s *Server) handleApproval(w http.ResponseWriter, r *http.Request) {
971952 return
972953 }
973954 if len (mfaChain ) > 0 {
974- h .Write ([]byte (authReq .ID + "|" + mfaChain [0 ]))
975- v := url.Values {}
976- v .Set ("req" , authReq .ID )
977- v .Set ("hmac" , base64 .RawURLEncoding .EncodeToString (h .Sum (nil )))
978- v .Set ("authenticator" , mfaChain [0 ])
979- h .Reset ()
980- totpURL := path .Join (s .issuerURL .Path , "/mfa/verify" ) + "?" + v .Encode ()
981- http .Redirect (w , r , totpURL , http .StatusSeeOther )
955+ http .Redirect (w , r , s .buildMFARedirectURL (authReq , mfaChain [0 ]), http .StatusSeeOther )
982956 return
983957 }
984958 // No MFA required but flag not set — allow through (backward compat).
@@ -995,6 +969,24 @@ func (s *Server) handleApproval(w http.ResponseWriter, r *http.Request) {
995969
996970 switch r .Method {
997971 case http .MethodGet :
972+ // Skip the approval page and issue the code directly if:
973+ // 1. The client didn't force the approval prompt, AND
974+ // 2. Either the server is configured to skip approval globally,
975+ // or the user has already consented to all requested scopes for this client.
976+ // This handles the MFA redirect case: after MFA completion the user lands on
977+ // /approval via GET, and we don't want to show the consent screen again.
978+ if ! authReq .ForceApprovalPrompt {
979+ if s .skipApproval {
980+ s .sendCodeResponse (w , r , authReq )
981+ return
982+ }
983+ ui , err := s .storage .GetUserIdentity (ctx , authReq .Claims .UserID , authReq .ConnectorID )
984+ if err == nil && scopesCoveredByConsent (ui .Consents [authReq .ClientID ], authReq .Scopes ) {
985+ s .sendCodeResponse (w , r , authReq )
986+ return
987+ }
988+ }
989+
998990 client , err := s .storage .GetClient (ctx , authReq .ClientID )
999991 if err != nil {
1000992 s .logger .ErrorContext (r .Context (), "Failed to get client" , "client_id" , authReq .ClientID , "err" , err )
0 commit comments