@@ -833,41 +833,104 @@ func (sp *ServiceProvider) nameIDFormat() string {
833833 return nameIDFormat
834834}
835835
836- // ValidateLogoutResponse returns a nil error iff the logout request is valid.
837- func (sp * ServiceProvider ) ValidateLogoutResponse (r * http.Request ) error {
838- r .ParseForm ()
839- rawResponseBuf , err := base64 .StdEncoding .DecodeString (r .PostForm .Get ("SAMLResponse" ))
836+ // ValidateLogoutResponseRequest validates the LogoutResponse content from the request
837+ func (sp * ServiceProvider ) ValidateLogoutResponseRequest (req * http.Request ) error {
838+ if data := req .URL .Query ().Get ("SAMLResponse" ); data != "" {
839+ return sp .ValidateLogoutResponseRedirect (data )
840+ }
841+
842+ err := req .ParseForm ()
843+ if err != nil {
844+ return fmt .Errorf ("unable to parse form: %v" , err )
845+ }
846+
847+ return sp .ValidateLogoutResponseForm (req .PostForm .Get ("SAMLResponse" ))
848+ }
849+
850+ // ValidatePostLogoutResponse returns a nil error if the logout response is valid.
851+ func (sp * ServiceProvider ) ValidateLogoutResponseForm (postFormData string ) error {
852+ rawResponseBuf , err := base64 .StdEncoding .DecodeString (postFormData )
840853 if err != nil {
841854 return fmt .Errorf ("unable to parse base64: %s" , err )
842855 }
843856
844- resp := LogoutResponse {}
857+ var resp LogoutResponse
858+
845859 if err := xml .Unmarshal (rawResponseBuf , & resp ); err != nil {
846860 return fmt .Errorf ("cannot unmarshal response: %s" , err )
847861 }
848- if resp .Destination != sp .SloURL .String () {
849- return fmt .Errorf ("`Destination` does not match SloURL (expected %q)" , sp .SloURL .String ())
862+
863+ if err := sp .validateLogoutResponse (& resp ); err != nil {
864+ return err
850865 }
851866
852- now := time . Now ()
853- if resp . IssueInstant . Add ( MaxIssueDelay ). Before ( now ) {
854- return fmt . Errorf ( "issueInstant expired at %s" , resp . IssueInstant . Add ( MaxIssueDelay ))
867+ doc := etree . NewDocument ()
868+ if err := doc . ReadFromBytes ( rawResponseBuf ); err != nil {
869+ return err
855870 }
856- if resp .Issuer .Value != sp .IDPMetadata .EntityID {
857- return fmt .Errorf ("issuer does not match the IDP metadata (expected %q)" , sp .IDPMetadata .EntityID )
871+
872+ responseEl := doc .Root ()
873+ if err = sp .validateSigned (responseEl ); err != nil {
874+ return err
858875 }
859- if resp .Status .StatusCode .Value != StatusSuccess {
860- return fmt .Errorf ("status code was not %s" , StatusSuccess )
876+
877+ return nil
878+ }
879+
880+ // ValidateRedirectLogoutResponse returns a nil error if the logout response is valid.
881+ // URL Binding appears to be gzip / flate encoded
882+ // See https://www.oasis-open.org/committees/download.php/20645/sstc-saml-tech-overview-2%200-draft-10.pdf 6.6
883+ func (sp * ServiceProvider ) ValidateLogoutResponseRedirect (queryParameterData string ) error {
884+ rawResponseBuf , err := base64 .StdEncoding .DecodeString (queryParameterData )
885+ if err != nil {
886+ return fmt .Errorf ("unable to parse base64: %s" , err )
861887 }
862888
889+ gr := flate .NewReader (bytes .NewBuffer (rawResponseBuf ))
890+
891+ decoder := xml .NewDecoder (gr )
892+
893+ var resp LogoutResponse
894+
895+ err = decoder .Decode (& resp )
896+ if err != nil {
897+ return fmt .Errorf ("unable to flate decode: %s" , err )
898+ }
899+
900+ if err := sp .validateLogoutResponse (& resp ); err != nil {
901+ return err
902+ }
903+
863904 doc := etree .NewDocument ()
864- if err := doc .ReadFromBytes ( rawResponseBuf ); err != nil {
905+ if _ , err := doc .ReadFrom ( gr ); err != nil {
865906 return err
866907 }
908+
867909 responseEl := doc .Root ()
868910 if err = sp .validateSigned (responseEl ); err != nil {
869911 return err
870912 }
871913
872914 return nil
873915}
916+
917+
918+ // validateLogoutResponse validates the LogoutResponse fields. Returns a nil error if the LogoutResponse is valid.
919+ func (sp * ServiceProvider ) validateLogoutResponse (resp * LogoutResponse ) error {
920+ if resp .Destination != sp .SloURL .String () {
921+ return fmt .Errorf ("`Destination` does not match SloURL (expected %q)" , sp .SloURL .String ())
922+ }
923+
924+ now := time .Now ()
925+ if resp .IssueInstant .Add (MaxIssueDelay ).Before (now ) {
926+ return fmt .Errorf ("issueInstant expired at %s" , resp .IssueInstant .Add (MaxIssueDelay ))
927+ }
928+ if resp .Issuer .Value != sp .IDPMetadata .EntityID {
929+ return fmt .Errorf ("issuer does not match the IDP metadata (expected %q)" , sp .IDPMetadata .EntityID )
930+ }
931+ if resp .Status .StatusCode .Value != StatusSuccess {
932+ return fmt .Errorf ("status code was not %s" , StatusSuccess )
933+ }
934+
935+ return nil
936+ }
0 commit comments