diff --git a/go.mod b/go.mod
index 8d684f17f84bffdafb4a0fe51ea62064f1c0befc..747c43a81166e74661390bc7a24a49049c9c5e20 100644
--- a/go.mod
+++ b/go.mod
@@ -10,7 +10,7 @@ require (
 	git.autistici.org/id/go-sso v0.0.0-20230822064459-ed921a53bb33
 	git.autistici.org/id/keystore v0.0.0-20230901162242-63f23c4799e9
 	git.autistici.org/id/usermetadb v0.0.0-20240906121505-52bc1650d93d
-	github.com/crewjam/saml v0.4.13
+	github.com/crewjam/saml v0.4.14
 	github.com/elazarl/go-bindata-assetfs v1.0.1
 	github.com/go-webauthn/webauthn v0.10.2
 	github.com/gorilla/csrf v1.7.2
@@ -52,7 +52,7 @@ require (
 	github.com/prometheus/client_model v0.6.1 // indirect
 	github.com/prometheus/common v0.55.0 // indirect
 	github.com/prometheus/procfs v0.15.1 // indirect
-	github.com/russellhaering/goxmldsig v1.2.0 // indirect
+	github.com/russellhaering/goxmldsig v1.3.0 // indirect
 	github.com/x448/float16 v0.8.4 // indirect
 	go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.34.0 // indirect
 	go.opentelemetry.io/contrib/propagators/b3 v1.9.0 // indirect
diff --git a/go.sum b/go.sum
index 6e0c0456c7d4c4553ffc939ab630d5d88362fddd..10445ab8cabed191082f4fd5b53670dd89e76a16 100644
--- a/go.sum
+++ b/go.sum
@@ -198,6 +198,8 @@ github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ
 github.com/crewjam/httperr v0.2.0/go.mod h1:Jlz+Sg/XqBQhyMjdDiC+GNNRzZTD7x39Gu3pglZ5oH4=
 github.com/crewjam/saml v0.4.13 h1:TYHggH/hwP7eArqiXSJUvtOPNzQDyQ7vwmwEqlFWhMc=
 github.com/crewjam/saml v0.4.13/go.mod h1:igEejV+fihTIlHXYP8zOec3V5A8y3lws5bQBFsTm4gA=
+github.com/crewjam/saml v0.4.14 h1:g9FBNx62osKusnFzs3QTN5L9CVA/Egfgm+stJShzw/c=
+github.com/crewjam/saml v0.4.14/go.mod h1:UVSZCf18jJkk6GpWNVqcyQJMD5HsRugBPf4I1nl2mME=
 github.com/daaku/go.zipexe v1.0.0/go.mod h1:z8IiR6TsVLEYKwXAoE/I+8ys/sDkgTzSL0CLnGVd57E=
 github.com/daaku/go.zipexe v1.0.1/go.mod h1:5xWogtqlYnfBXkSB1o9xysukNP9GTvaNkqzUZbt3Bw8=
 github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -734,6 +736,8 @@ github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA=
 github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
 github.com/russellhaering/goxmldsig v1.2.0 h1:Y6GTTc9Un5hCxSzVz4UIWQ/zuVwDvzJk80guqzwx6Vg=
 github.com/russellhaering/goxmldsig v1.2.0/go.mod h1:gM4MDENBQf7M+V824SGfyIUVFWydB7n0KkEubVJl+Tw=
+github.com/russellhaering/goxmldsig v1.3.0 h1:DllIWUgMy0cRUMfGiASiYEa35nsieyD3cigIwLonTPM=
+github.com/russellhaering/goxmldsig v1.3.0/go.mod h1:gM4MDENBQf7M+V824SGfyIUVFWydB7n0KkEubVJl+Tw=
 github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
 github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
diff --git a/vendor/github.com/crewjam/saml/.golangci.yml b/vendor/github.com/crewjam/saml/.golangci.yml
index 4fbc0405df2e767914f38275bb0cac9f9017b78f..23f37cbf54209237c01a33760341ecc55a7972cc 100644
--- a/vendor/github.com/crewjam/saml/.golangci.yml
+++ b/vendor/github.com/crewjam/saml/.golangci.yml
@@ -7,41 +7,35 @@
 
 linters:
   enable:
-    - gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification [fast: true, auto-fix: true]
-    - goimports # Goimports does everything that gofmt does. Additionally it checks unused imports [fast: true, auto-fix: true]
-    - gosec # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases [fast: true, auto-fix: false]
-    - misspell # Finds commonly misspelled English words in comments [fast: true, auto-fix: true]
-    - deadcode # Finds unused code [fast: true, auto-fix: false]
-    - revive # Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes [fast: true, auto-fix: false]
-    - unconvert # Remove unnecessary type conversions [fast: true, auto-fix: false]
-
-  disable:
-    # TODO(ross): fix errors reported by these checkers and enable them
     - bodyclose # checks whether HTTP response body is closed successfully [fast: false, auto-fix: false]
-    - depguard # Go linter that checks if package imports are in a list of acceptable packages [fast: true, auto-fix: false]
-    - dupl # Tool for code clone detection [fast: true, auto-fix: false]
     - errcheck # Inspects source code for security problems [fast: true, auto-fix: false]
-    - gochecknoglobals # Checks that no globals are present in Go code [fast: true, auto-fix: false]
-    - gochecknoinits # Checks that no init functions are present in Go code [fast: true, auto-fix: false]
-    - goconst # Finds repeated strings that could be replaced by a constant [fast: true, auto-fix: false]
     - gocritic # The most opinionated Go source code linter [fast: true, auto-fix: false]
     - gocyclo # Computes and checks the cyclomatic complexity of functions [fast: true, auto-fix: false]
+    - gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification [fast: true, auto-fix: true]
+    - goimports # Goimports does everything that gofmt does. Additionally it checks unused imports [fast: true, auto-fix: true]
+    - gosec # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases [fast: true, auto-fix: false]
     - gosimple # Linter for Go source code that specializes in simplifying a code [fast: false, auto-fix: false]
     - govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string [fast: false, auto-fix: false]
     - ineffassign # Detects when assignments to existing variables are not used [fast: true, auto-fix: false]
-    - interfacer # Linter that suggests narrower interface types [fast: false, auto-fix: false]
-    - lll # Reports long lines [fast: true, auto-fix: false]
-    - maligned # Tool to detect Go structs that would take less memory if their fields were sorted [fast: true, auto-fix: false]
+    - misspell # Finds commonly misspelled English words in comments [fast: true, auto-fix: true]
     - nakedret # Finds naked returns in functions greater than a specified function length [fast: true, auto-fix: false]
     - prealloc # Finds slice declarations that could potentially be preallocated [fast: true, auto-fix: false]
-    - scopelint # Scopelint checks for unpinned variables in go programs [fast: true, auto-fix: false]
+    - revive # Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes [fast: true, auto-fix: false]
     - staticcheck # Staticcheck is a go vet on steroids, applying a ton of static analysis checks [fast: false, auto-fix: false]
-    - structcheck # Finds unused struct fields [fast: true, auto-fix: false]
     - stylecheck # Stylecheck is a replacement for golint [fast: false, auto-fix: false]
     - typecheck # Like the front-end of a Go compiler, parses and type-checks Go code [fast: true, auto-fix: false]
+    - unconvert # Remove unnecessary type conversions [fast: true, auto-fix: false]
     - unparam # Reports unused function parameters [fast: false, auto-fix: false]
     - unused # Checks Go code for unused constants, variables, functions and types [fast: false, auto-fix: false]
-    - varcheck # Finds unused global variables and constants [fast: true, auto-fix: false]
+
+  disable:
+    # TODO(ross): fix errors reported by these checkers and enable them
+    - dupl # Tool for code clone detection [fast: true, auto-fix: false]
+    - gochecknoglobals # Checks that no globals are present in Go code [fast: true, auto-fix: false]
+    - gochecknoinits # Checks that no init functions are present in Go code [fast: true, auto-fix: false]
+    - goconst # Finds repeated strings that could be replaced by a constant [fast: true, auto-fix: false]
+    - lll # Reports long lines [fast: true, auto-fix: false]
+    - depguard # Go linter that checks if package imports are in a list of acceptable packages [fast: true, auto-fix: false]
 linters-settings:
   goimports:
     local-prefixes: github.com/crewjam/saml
diff --git a/vendor/github.com/crewjam/saml/identity_provider.go b/vendor/github.com/crewjam/saml/identity_provider.go
index bcea5828f56f9912bcda591ef09c197c74a9f768..abaaad68e27dd60965d50c0485d1cccb0ec962ce 100644
--- a/vendor/github.com/crewjam/saml/identity_provider.go
+++ b/vendor/github.com/crewjam/saml/identity_provider.go
@@ -9,7 +9,6 @@ import (
 	"encoding/xml"
 	"fmt"
 	"io"
-	"io/ioutil"
 	"net/http"
 	"net/url"
 	"os"
@@ -96,6 +95,7 @@ type AssertionMaker interface {
 // and password).
 type IdentityProvider struct {
 	Key                     crypto.PrivateKey
+	Signer                  crypto.Signer
 	Logger                  logger.Interface
 	Certificate             *x509.Certificate
 	Intermediates           []*x509.Certificate
@@ -196,10 +196,13 @@ func (idp *IdentityProvider) Handler() http.Handler {
 }
 
 // ServeMetadata is an http.HandlerFunc that serves the IDP metadata
-func (idp *IdentityProvider) ServeMetadata(w http.ResponseWriter, r *http.Request) {
+func (idp *IdentityProvider) ServeMetadata(w http.ResponseWriter, _ *http.Request) {
 	buf, _ := xml.MarshalIndent(idp.Metadata(), "", "  ")
 	w.Header().Set("Content-Type", "application/samlmetadata+xml")
-	w.Write(buf)
+	if _, err := w.Write(buf); err != nil {
+		idp.Logger.Printf("ERROR: %s", err)
+		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
+	}
 }
 
 // ServeSSO handles SAML auth requests.
@@ -362,7 +365,7 @@ func NewIdpAuthnRequest(idp *IdentityProvider, r *http.Request) (*IdpAuthnReques
 		if err != nil {
 			return nil, fmt.Errorf("cannot decode request: %s", err)
 		}
-		req.RequestBuffer, err = ioutil.ReadAll(newSaferFlateReader(bytes.NewReader(compressedRequest)))
+		req.RequestBuffer, err = io.ReadAll(newSaferFlateReader(bytes.NewReader(compressedRequest)))
 		if err != nil {
 			return nil, fmt.Errorf("cannot decompress request: %s", err)
 		}
@@ -716,9 +719,7 @@ func (DefaultAssertionMaker) MakeAssertion(req *IdpAuthnRequest, session *Sessio
 		})
 	}
 
-	for _, ca := range session.CustomAttributes {
-		attributes = append(attributes, ca)
-	}
+	attributes = append(attributes, session.CustomAttributes...)
 
 	if len(session.Groups) != 0 {
 		groupMemberAttributeValues := []AttributeValue{}
@@ -830,24 +831,8 @@ const canonicalizerPrefixList = ""
 
 // MakeAssertionEl sets `AssertionEl` to a signed, possibly encrypted, version of `Assertion`.
 func (req *IdpAuthnRequest) MakeAssertionEl() error {
-	keyPair := tls.Certificate{
-		Certificate: [][]byte{req.IDP.Certificate.Raw},
-		PrivateKey:  req.IDP.Key,
-		Leaf:        req.IDP.Certificate,
-	}
-	for _, cert := range req.IDP.Intermediates {
-		keyPair.Certificate = append(keyPair.Certificate, cert.Raw)
-	}
-	keyStore := dsig.TLSCertKeyStore(keyPair)
-
-	signatureMethod := req.IDP.SignatureMethod
-	if signatureMethod == "" {
-		signatureMethod = dsig.RSASHA1SignatureMethod
-	}
-
-	signingContext := dsig.NewDefaultSigningContext(keyStore)
-	signingContext.Canonicalizer = dsig.MakeC14N10ExclusiveCanonicalizerWithPrefixList(canonicalizerPrefixList)
-	if err := signingContext.SetSignatureMethod(signatureMethod); err != nil {
+	signingContext, err := req.signingContext()
+	if err != nil {
 		return err
 	}
 
@@ -1048,24 +1033,8 @@ func (req *IdpAuthnRequest) MakeResponse() error {
 
 	// Sign the response element (we've already signed the Assertion element)
 	{
-		keyPair := tls.Certificate{
-			Certificate: [][]byte{req.IDP.Certificate.Raw},
-			PrivateKey:  req.IDP.Key,
-			Leaf:        req.IDP.Certificate,
-		}
-		for _, cert := range req.IDP.Intermediates {
-			keyPair.Certificate = append(keyPair.Certificate, cert.Raw)
-		}
-		keyStore := dsig.TLSCertKeyStore(keyPair)
-
-		signatureMethod := req.IDP.SignatureMethod
-		if signatureMethod == "" {
-			signatureMethod = dsig.RSASHA1SignatureMethod
-		}
-
-		signingContext := dsig.NewDefaultSigningContext(keyStore)
-		signingContext.Canonicalizer = dsig.MakeC14N10ExclusiveCanonicalizerWithPrefixList(canonicalizerPrefixList)
-		if err := signingContext.SetSignatureMethod(signatureMethod); err != nil {
+		signingContext, err := req.signingContext()
+		if err != nil {
 			return err
 		}
 
@@ -1083,3 +1052,44 @@ func (req *IdpAuthnRequest) MakeResponse() error {
 	req.ResponseEl = responseEl
 	return nil
 }
+
+// signingContext will create a signing context for the request.
+func (req *IdpAuthnRequest) signingContext() (*dsig.SigningContext, error) {
+	// Create a cert chain based off of the IDP cert and its intermediates.
+	certificates := [][]byte{req.IDP.Certificate.Raw}
+	for _, cert := range req.IDP.Intermediates {
+		certificates = append(certificates, cert.Raw)
+	}
+
+	var signingContext *dsig.SigningContext
+	var err error
+	// If signer is set, use it instead of the private key.
+	if req.IDP.Signer != nil {
+		signingContext, err = dsig.NewSigningContext(req.IDP.Signer, certificates)
+		if err != nil {
+			return nil, err
+		}
+	} else {
+		keyPair := tls.Certificate{
+			Certificate: certificates,
+			PrivateKey:  req.IDP.Key,
+			Leaf:        req.IDP.Certificate,
+		}
+		keyStore := dsig.TLSCertKeyStore(keyPair)
+
+		signingContext = dsig.NewDefaultSigningContext(keyStore)
+	}
+
+	// Default to using SHA1 if the signature method isn't set.
+	signatureMethod := req.IDP.SignatureMethod
+	if signatureMethod == "" {
+		signatureMethod = dsig.RSASHA1SignatureMethod
+	}
+
+	signingContext.Canonicalizer = dsig.MakeC14N10ExclusiveCanonicalizerWithPrefixList(canonicalizerPrefixList)
+	if err := signingContext.SetSignatureMethod(signatureMethod); err != nil {
+		return nil, err
+	}
+
+	return signingContext, nil
+}
diff --git a/vendor/github.com/crewjam/saml/logger/logger.go b/vendor/github.com/crewjam/saml/logger/logger.go
index c211aba60fafef6d006a7b6e821416656e6a2b3a..03bb0bdef92da70a135fbe159b023922a7bce692 100644
--- a/vendor/github.com/crewjam/saml/logger/logger.go
+++ b/vendor/github.com/crewjam/saml/logger/logger.go
@@ -1,3 +1,4 @@
+// Package logger provides a logging interface.
 package logger
 
 import (
diff --git a/vendor/github.com/crewjam/saml/metadata.go b/vendor/github.com/crewjam/saml/metadata.go
index f7486d380076106c5fa93ff869f9cabea923916d..006a9e67a586a60487eb74add81c6da11c9937c5 100644
--- a/vendor/github.com/crewjam/saml/metadata.go
+++ b/vendor/github.com/crewjam/saml/metadata.go
@@ -2,6 +2,8 @@ package saml
 
 import (
 	"encoding/xml"
+	"fmt"
+	"net/url"
 	"time"
 
 	"github.com/beevik/etree"
@@ -19,6 +21,9 @@ const HTTPArtifactBinding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact"
 // SOAPBinding is the official URN for the SOAP binding (transport)
 const SOAPBinding = "urn:oasis:names:tc:SAML:2.0:bindings:SOAP"
 
+// SOAPBindingV1 is the URN for the SOAP binding in SAML 1.0
+const SOAPBindingV1 = "urn:oasis:names:tc:SAML:1.0:bindings:SOAP-binding"
+
 // EntitiesDescriptor represents the SAML object of the same name.
 //
 // See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.3.1
@@ -65,7 +70,7 @@ type EntityDescriptor struct {
 }
 
 // MarshalXML implements xml.Marshaler
-func (m EntityDescriptor) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+func (m EntityDescriptor) MarshalXML(e *xml.Encoder, _ xml.StartElement) error {
 	type Alias EntityDescriptor
 	aux := &struct {
 		ValidUntil    RelaxedTime `xml:"validUntil,attr,omitempty"`
@@ -188,6 +193,76 @@ type Endpoint struct {
 	ResponseLocation string `xml:"ResponseLocation,attr,omitempty"`
 }
 
+func checkEndpointLocation(binding string, location string) (string, error) {
+	// Within the SAML standard, the complex type EndpointType describes a
+	// SAML protocol binding endpoint at which a SAML entity can be sent
+	// protocol messages. In particular, the location of an endpoint type is
+	// defined as follows in the Metadata for the OASIS Security Assertion
+	// Markup Language (SAML) V2.0 - 2.2.2 Complex Type EndpointType:
+	//
+	//   Location [Required] A required URI attribute that specifies the
+	//   location of the endpoint. The allowable syntax of this URI depends
+	//   on the protocol binding.
+	switch binding {
+	case HTTPPostBinding,
+		HTTPRedirectBinding,
+		HTTPArtifactBinding,
+		SOAPBinding,
+		SOAPBindingV1:
+		locationURL, err := url.Parse(location)
+		if err != nil {
+			return "", fmt.Errorf("invalid url %q: %w", location, err)
+		}
+		switch locationURL.Scheme {
+		case "http", "https":
+		// ok
+		default:
+			return "", fmt.Errorf("invalid url scheme %q for binding %q",
+				locationURL.Scheme, binding)
+		}
+	default:
+		// We don't know what form location should take, but the protocol
+		// requires that we validate its syntax.
+		//
+		// In practice, lots of metadata contains random bindings, for example
+		// "urn:mace:shibboleth:1.0:profiles:AuthnRequest" from our own test suite.
+		//
+		// We can't fail, but we also can't allow a location parameter whose syntax we
+		// cannot verify. The least-bad course of action here is to set location to
+		// and empty string, and hope the caller doesn't care need it.
+		location = ""
+	}
+
+	return location, nil
+}
+
+// UnmarshalXML implements xml.Unmarshaler
+func (m *Endpoint) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
+	type Alias Endpoint
+	aux := &struct {
+		*Alias
+	}{
+		Alias: (*Alias)(m),
+	}
+	if err := d.DecodeElement(aux, &start); err != nil {
+		return err
+	}
+
+	var err error
+	m.Location, err = checkEndpointLocation(m.Binding, m.Location)
+	if err != nil {
+		return err
+	}
+	if m.ResponseLocation != "" {
+		m.ResponseLocation, err = checkEndpointLocation(m.Binding, m.ResponseLocation)
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
 // IndexedEndpoint represents the SAML IndexedEndpointType object.
 //
 // See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.2.3
@@ -199,6 +274,38 @@ type IndexedEndpoint struct {
 	IsDefault        *bool   `xml:"isDefault,attr"`
 }
 
+// UnmarshalXML implements xml.Unmarshaler
+func (m *IndexedEndpoint) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
+	type Alias IndexedEndpoint
+	aux := &struct {
+		*Alias
+	}{
+		Alias: (*Alias)(m),
+	}
+	if err := d.DecodeElement(aux, &start); err != nil {
+		return err
+	}
+
+	var err error
+	m.Location, err = checkEndpointLocation(m.Binding, m.Location)
+	if err != nil {
+		return err
+	}
+	if m.ResponseLocation != nil {
+		responseLocation, err := checkEndpointLocation(m.Binding, *m.ResponseLocation)
+		if err != nil {
+			return err
+		}
+		if responseLocation != "" {
+			m.ResponseLocation = &responseLocation
+		} else {
+			m.ResponseLocation = nil
+		}
+	}
+
+	return nil
+}
+
 // SSODescriptor represents the SAML complex type SSODescriptor
 //
 // See http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf §2.4.2
diff --git a/vendor/github.com/crewjam/saml/schema.go b/vendor/github.com/crewjam/saml/schema.go
index 13ea2ef60e93b0dcc533aae182d78a86e8f37010..23cddbcabb7fdefed2ecb902b4c392330cea93cc 100644
--- a/vendor/github.com/crewjam/saml/schema.go
+++ b/vendor/github.com/crewjam/saml/schema.go
@@ -48,7 +48,7 @@ type AuthnRequest struct {
 	NameIDPolicy          *NameIDPolicy `xml:"urn:oasis:names:tc:SAML:2.0:protocol NameIDPolicy"`
 	Conditions            *Conditions
 	RequestedAuthnContext *RequestedAuthnContext
-	//Scoping               *Scoping // TODO
+	// Scoping               *Scoping // TODO
 
 	ForceAuthn                     *bool  `xml:",attr"`
 	IsPassive                      *bool  `xml:",attr"`
@@ -108,7 +108,7 @@ func (r *LogoutRequest) Element() *etree.Element {
 }
 
 // MarshalXML implements xml.Marshaler
-func (r *LogoutRequest) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+func (r *LogoutRequest) MarshalXML(e *xml.Encoder, _ xml.StartElement) error {
 	type Alias LogoutRequest
 	aux := &struct {
 		IssueInstant RelaxedTime  `xml:",attr"`
@@ -209,9 +209,9 @@ func (r *AuthnRequest) Element() *etree.Element {
 	if r.RequestedAuthnContext != nil {
 		el.AddChild(r.RequestedAuthnContext.Element())
 	}
-	//if r.Scoping != nil {
-	//	el.AddChild(r.Scoping.Element())
-	//}
+	// if r.Scoping != nil {
+	// 	el.AddChild(r.Scoping.Element())
+	// }
 	if r.ForceAuthn != nil {
 		el.CreateAttr("ForceAuthn", strconv.FormatBool(*r.ForceAuthn))
 	}
@@ -237,7 +237,7 @@ func (r *AuthnRequest) Element() *etree.Element {
 }
 
 // MarshalXML implements xml.Marshaler
-func (r *AuthnRequest) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+func (r *AuthnRequest) MarshalXML(e *xml.Encoder, _ xml.StartElement) error {
 	type Alias AuthnRequest
 	aux := &struct {
 		IssueInstant RelaxedTime `xml:",attr"`
@@ -374,7 +374,7 @@ func (r *ArtifactResolve) SoapRequest() *etree.Element {
 }
 
 // MarshalXML implements xml.Marshaler
-func (r *ArtifactResolve) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+func (r *ArtifactResolve) MarshalXML(e *xml.Encoder, _ xml.StartElement) error {
 	type Alias ArtifactResolve
 	aux := &struct {
 		IssueInstant RelaxedTime `xml:",attr"`
@@ -448,7 +448,7 @@ func (r *ArtifactResponse) Element() *etree.Element {
 }
 
 // MarshalXML implements xml.Marshaler
-func (r *ArtifactResponse) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+func (r *ArtifactResponse) MarshalXML(e *xml.Encoder, _ xml.StartElement) error {
 	type Alias ArtifactResponse
 	aux := &struct {
 		IssueInstant RelaxedTime `xml:",attr"`
@@ -542,7 +542,7 @@ func (r *Response) Element() *etree.Element {
 }
 
 // MarshalXML implements xml.Marshaler
-func (r *Response) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+func (r *Response) MarshalXML(e *xml.Encoder, _ xml.StartElement) error {
 	type Alias Response
 	aux := &struct {
 		IssueInstant RelaxedTime `xml:",attr"`
@@ -667,7 +667,7 @@ const (
 	StatusRequestUnsupported = "urn:oasis:names:tc:SAML:2.0:status:RequestUnsupported"
 
 	// StatusRequestVersionDeprecated means the SAML responder cannot process any requests with the protocol version specified in the request.
-	StatusRequestVersionDeprecated = "urn:oasis:names:tc:SAML:2.0:status:RequestVersionDeprecated"
+	StatusRequestVersionDeprecated = "urn:oasis:names:tc:SAML:2.0:status:RequestVersionDeprecated" //nolint:gosec
 
 	// StatusRequestVersionTooHigh means the SAML responder cannot process the request because the protocol version specified in the request message is a major upgrade from the highest protocol version supported by the responder.
 	StatusRequestVersionTooHigh = "urn:oasis:names:tc:SAML:2.0:status:RequestVersionTooHigh"
@@ -1153,9 +1153,9 @@ func (a *SubjectLocality) Element() *etree.Element {
 // See http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf §2.7.2.2
 type AuthnContext struct {
 	AuthnContextClassRef *AuthnContextClassRef
-	//AuthnContextDecl          *AuthnContextDecl        ... TODO
-	//AuthnContextDeclRef       *AuthnContextDeclRef     ... TODO
-	//AuthenticatingAuthorities []AuthenticatingAuthority... TODO
+	// AuthnContextDecl          *AuthnContextDecl        ... TODO
+	// AuthnContextDeclRef       *AuthnContextDeclRef     ... TODO
+	// AuthenticatingAuthorities []AuthenticatingAuthority... TODO
 }
 
 // Element returns an etree.Element representing the object in XML form.
@@ -1292,7 +1292,7 @@ func (r *LogoutResponse) Element() *etree.Element {
 }
 
 // MarshalXML implements xml.Marshaler
-func (r *LogoutResponse) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+func (r *LogoutResponse) MarshalXML(e *xml.Encoder, _ xml.StartElement) error {
 	type Alias LogoutResponse
 	aux := &struct {
 		IssueInstant RelaxedTime `xml:",attr"`
diff --git a/vendor/github.com/crewjam/saml/service_provider.go b/vendor/github.com/crewjam/saml/service_provider.go
index 6f6e7f4fc81c85808e919661665f8ae7c804c3c9..30b35670d64704ce29618ad41596dd5595f28aff 100644
--- a/vendor/github.com/crewjam/saml/service_provider.go
+++ b/vendor/github.com/crewjam/saml/service_provider.go
@@ -12,7 +12,7 @@ import (
 	"errors"
 	"fmt"
 	"html/template"
-	"io/ioutil"
+	"io"
 	"net/http"
 	"net/url"
 	"regexp"
@@ -23,6 +23,7 @@ import (
 	dsig "github.com/russellhaering/goxmldsig"
 	"github.com/russellhaering/goxmldsig/etreeutils"
 
+	"github.com/crewjam/saml/logger"
 	"github.com/crewjam/saml/xmlenc"
 )
 
@@ -189,13 +190,13 @@ func (sp *ServiceProvider) Metadata() *EntityDescriptor {
 		}
 	}
 
-	var sloEndpoints []Endpoint
-	for _, binding := range sp.LogoutBindings {
-		sloEndpoints = append(sloEndpoints, Endpoint{
+	sloEndpoints := make([]Endpoint, len(sp.LogoutBindings))
+	for i, binding := range sp.LogoutBindings {
+		sloEndpoints[i] = Endpoint{
 			Binding:          binding,
 			Location:         sp.SloURL.String(),
 			ResponseLocation: sp.SloURL.String(),
-		})
+		}
 	}
 
 	return &EntityDescriptor{
@@ -245,25 +246,29 @@ func (sp *ServiceProvider) MakeRedirectAuthenticationRequest(relayState string)
 }
 
 // Redirect returns a URL suitable for using the redirect binding with the request
-func (req *AuthnRequest) Redirect(relayState string, sp *ServiceProvider) (*url.URL, error) {
+func (r *AuthnRequest) Redirect(relayState string, sp *ServiceProvider) (*url.URL, error) {
 	w := &bytes.Buffer{}
 	w1 := base64.NewEncoder(base64.StdEncoding, w)
 	w2, _ := flate.NewWriter(w1, 9)
 	doc := etree.NewDocument()
-	doc.SetRoot(req.Element())
+	doc.SetRoot(r.Element())
 	if _, err := doc.WriteTo(w2); err != nil {
 		panic(err)
 	}
-	w2.Close()
-	w1.Close()
+	if err := w2.Close(); err != nil {
+		panic(err)
+	}
+	if err := w1.Close(); err != nil {
+		panic(err)
+	}
 
-	rv, _ := url.Parse(req.Destination)
+	rv, _ := url.Parse(r.Destination)
 	// We can't depend on Query().set() as order matters for signing
 	query := rv.RawQuery
 	if len(query) > 0 {
-		query += "&SAMLRequest=" + url.QueryEscape(string(w.Bytes()))
+		query += "&SAMLRequest=" + url.QueryEscape(w.String())
 	} else {
-		query += "SAMLRequest=" + url.QueryEscape(string(w.Bytes()))
+		query += "SAMLRequest=" + url.QueryEscape(w.String())
 	}
 
 	if relayState != "" {
@@ -352,11 +357,11 @@ func (sp *ServiceProvider) getIDPSigningCerts() ([]*x509.Certificate, error) {
 		return nil, errors.New("cannot find any signing certificate in the IDP SSO descriptor")
 	}
 
-	var certs []*x509.Certificate
+	certs := make([]*x509.Certificate, len(certStrs))
 
 	// cleanup whitespace
 	regex := regexp.MustCompile(`\s+`)
-	for _, certStr := range certStrs {
+	for i, certStr := range certStrs {
 		certStr = regex.ReplaceAllString(certStr, "")
 		certBytes, err := base64.StdEncoding.DecodeString(certStr)
 		if err != nil {
@@ -367,7 +372,7 @@ func (sp *ServiceProvider) getIDPSigningCerts() ([]*x509.Certificate, error) {
 		if err != nil {
 			return nil, err
 		}
-		certs = append(certs, parsedCert)
+		certs[i] = parsedCert
 	}
 
 	return certs, nil
@@ -439,9 +444,9 @@ func GetSigningContext(sp *ServiceProvider) (*dsig.SigningContext, error) {
 		Leaf:        sp.Certificate,
 	}
 	// TODO: add intermediates for SP
-	//for _, cert := range sp.Intermediates {
-	//	keyPair.Certificate = append(keyPair.Certificate, cert.Raw)
-	//}
+	// for _, cert := range sp.Intermediates {
+	// 	keyPair.Certificate = append(keyPair.Certificate, cert.Raw)
+	// }
 	keyStore := dsig.TLSCertKeyStore(keyPair)
 
 	if sp.SignatureMethod != dsig.RSASHA1SignatureMethod &&
@@ -508,9 +513,9 @@ func (sp *ServiceProvider) MakePostAuthenticationRequest(relayState string) ([]b
 }
 
 // Post returns an HTML form suitable for using the HTTP-POST binding with the request
-func (req *AuthnRequest) Post(relayState string) []byte {
+func (r *AuthnRequest) Post(relayState string) []byte {
 	doc := etree.NewDocument()
-	doc.SetRoot(req.Element())
+	doc.SetRoot(r.Element())
 	reqBuf, err := doc.WriteToBytes()
 	if err != nil {
 		panic(err)
@@ -530,7 +535,7 @@ func (req *AuthnRequest) Post(relayState string) []byte {
 		SAMLRequest string
 		RelayState  string
 	}{
-		URL:         req.Destination,
+		URL:         r.Destination,
 		SAMLRequest: encodedReqBuf,
 		RelayState:  relayState,
 	}
@@ -579,7 +584,7 @@ type InvalidResponseError struct {
 }
 
 func (ivr *InvalidResponseError) Error() string {
-	return fmt.Sprintf("Authentication failed")
+	return "Authentication failed"
 }
 
 // ErrBadStatus is returned when the assertion provided is valid but the
@@ -606,7 +611,7 @@ func (sp *ServiceProvider) handleArtifactRequest(ctx context.Context, artifactID
 
 	artifactResolveRequest, err := sp.MakeArtifactResolveRequest(artifactID)
 	if err != nil {
-		retErr.PrivateErr = fmt.Errorf("Cannot generate artifact resolution request: %s", err)
+		retErr.PrivateErr = fmt.Errorf("cannot generate artifact resolution request: %s", err)
 		return nil, retErr
 	}
 
@@ -633,12 +638,16 @@ func (sp *ServiceProvider) handleArtifactRequest(ctx context.Context, artifactID
 		retErr.PrivateErr = fmt.Errorf("cannot resolve artifact: %s", err)
 		return nil, retErr
 	}
-	defer response.Body.Close()
+	defer func() {
+		if err := response.Body.Close(); err != nil {
+			logger.DefaultLogger.Printf("Error while closing response body during artifact resolution: %v", err)
+		}
+	}()
 	if response.StatusCode != 200 {
 		retErr.PrivateErr = fmt.Errorf("Error during artifact resolution: HTTP status %d (%s)", response.StatusCode, response.Status)
 		return nil, retErr
 	}
-	responseBody, err := ioutil.ReadAll(response.Body)
+	responseBody, err := io.ReadAll(response.Body)
 	if err != nil {
 		retErr.PrivateErr = fmt.Errorf("Error during artifact resolution: %s", err)
 		return nil, retErr
@@ -753,11 +762,12 @@ func (sp *ServiceProvider) parseArtifactResponse(artifactResponseEl *etree.Eleme
 
 	var signatureRequirement signatureRequirement
 	sigErr := sp.validateSignature(artifactResponseEl)
-	if sigErr == nil {
+	switch sigErr {
+	case nil:
 		signatureRequirement = signatureNotRequired
-	} else if sigErr == errSignatureElementNotPresent {
+	case errSignatureElementNotPresent:
 		signatureRequirement = signatureRequired
-	} else {
+	default:
 		retErr.PrivateErr = sigErr
 		return nil, retErr
 	}
@@ -888,13 +898,14 @@ func (sp *ServiceProvider) parseResponse(responseEl *etree.Element, possibleRequ
 	}
 
 	if signatureRequirement == signatureRequired {
-		if responseSignatureErr == nil {
+		switch responseSignatureErr {
+		case nil:
 			// since the request has a signature, none of the Assertions need one
 			signatureRequirement = signatureNotRequired
-		} else if responseSignatureErr == errSignatureElementNotPresent {
+		case errSignatureElementNotPresent:
 			// the request has no signature, so assertions must be signed
 			signatureRequirement = signatureRequired // nop
-		} else {
+		default:
 			return nil, responseSignatureErr
 		}
 	}
@@ -1078,7 +1089,7 @@ func (sp *ServiceProvider) validateAssertion(assertion *Assertion, possibleReque
 	return nil
 }
 
-var errSignatureElementNotPresent = errors.New("Signature element not present")
+var errSignatureElementNotPresent = errors.New("signature element not present")
 
 // validateSignature returns nil iff the Signature embedded in the element is valid
 func (sp *ServiceProvider) validateSignature(el *etree.Element) error {
@@ -1154,9 +1165,9 @@ func (sp *ServiceProvider) SignLogoutRequest(req *LogoutRequest) error {
 		Leaf:        sp.Certificate,
 	}
 	// TODO: add intermediates for SP
-	//for _, cert := range sp.Intermediates {
-	//	keyPair.Certificate = append(keyPair.Certificate, cert.Raw)
-	//}
+	// for _, cert := range sp.Intermediates {
+	// 	keyPair.Certificate = append(keyPair.Certificate, cert.Raw)
+	// }
 	keyStore := dsig.TLSCertKeyStore(keyPair)
 
 	if sp.SignatureMethod != dsig.RSASHA1SignatureMethod &&
@@ -1222,22 +1233,26 @@ func (sp *ServiceProvider) MakeRedirectLogoutRequest(nameID, relayState string)
 }
 
 // Redirect returns a URL suitable for using the redirect binding with the request
-func (req *LogoutRequest) Redirect(relayState string) *url.URL {
+func (r *LogoutRequest) Redirect(relayState string) *url.URL {
 	w := &bytes.Buffer{}
 	w1 := base64.NewEncoder(base64.StdEncoding, w)
 	w2, _ := flate.NewWriter(w1, 9)
 	doc := etree.NewDocument()
-	doc.SetRoot(req.Element())
+	doc.SetRoot(r.Element())
 	if _, err := doc.WriteTo(w2); err != nil {
 		panic(err)
 	}
-	w2.Close()
-	w1.Close()
+	if err := w2.Close(); err != nil {
+		panic(err)
+	}
+	if err := w1.Close(); err != nil {
+		panic(err)
+	}
 
-	rv, _ := url.Parse(req.Destination)
+	rv, _ := url.Parse(r.Destination)
 
 	query := rv.Query()
-	query.Set("SAMLRequest", string(w.Bytes()))
+	query.Set("SAMLRequest", w.String())
 	if relayState != "" {
 		query.Set("RelayState", relayState)
 	}
@@ -1258,9 +1273,9 @@ func (sp *ServiceProvider) MakePostLogoutRequest(nameID, relayState string) ([]b
 }
 
 // Post returns an HTML form suitable for using the HTTP-POST binding with the request
-func (req *LogoutRequest) Post(relayState string) []byte {
+func (r *LogoutRequest) Post(relayState string) []byte {
 	doc := etree.NewDocument()
-	doc.SetRoot(req.Element())
+	doc.SetRoot(r.Element())
 	reqBuf, err := doc.WriteToBytes()
 	if err != nil {
 		panic(err)
@@ -1280,7 +1295,7 @@ func (req *LogoutRequest) Post(relayState string) []byte {
 		SAMLRequest string
 		RelayState  string
 	}{
-		URL:         req.Destination,
+		URL:         r.Destination,
 		SAMLRequest: encodedReqBuf,
 		RelayState:  relayState,
 	}
@@ -1332,22 +1347,26 @@ func (sp *ServiceProvider) MakeRedirectLogoutResponse(logoutRequestID, relayStat
 }
 
 // Redirect returns a URL suitable for using the redirect binding with the LogoutResponse.
-func (resp *LogoutResponse) Redirect(relayState string) *url.URL {
+func (r *LogoutResponse) Redirect(relayState string) *url.URL {
 	w := &bytes.Buffer{}
 	w1 := base64.NewEncoder(base64.StdEncoding, w)
 	w2, _ := flate.NewWriter(w1, 9)
 	doc := etree.NewDocument()
-	doc.SetRoot(resp.Element())
+	doc.SetRoot(r.Element())
 	if _, err := doc.WriteTo(w2); err != nil {
 		panic(err)
 	}
-	w2.Close()
-	w1.Close()
+	if err := w2.Close(); err != nil {
+		panic(err)
+	}
+	if err := w1.Close(); err != nil {
+		panic(err)
+	}
 
-	rv, _ := url.Parse(resp.Destination)
+	rv, _ := url.Parse(r.Destination)
 
 	query := rv.Query()
-	query.Set("SAMLResponse", string(w.Bytes()))
+	query.Set("SAMLResponse", w.String())
 	if relayState != "" {
 		query.Set("RelayState", relayState)
 	}
@@ -1368,9 +1387,9 @@ func (sp *ServiceProvider) MakePostLogoutResponse(logoutRequestID, relayState st
 }
 
 // Post returns an HTML form suitable for using the HTTP-POST binding with the LogoutResponse.
-func (resp *LogoutResponse) Post(relayState string) []byte {
+func (r *LogoutResponse) Post(relayState string) []byte {
 	doc := etree.NewDocument()
-	doc.SetRoot(resp.Element())
+	doc.SetRoot(r.Element())
 	reqBuf, err := doc.WriteToBytes()
 	if err != nil {
 		panic(err)
@@ -1390,7 +1409,7 @@ func (resp *LogoutResponse) Post(relayState string) []byte {
 		SAMLResponse string
 		RelayState   string
 	}{
-		URL:          resp.Destination,
+		URL:          r.Destination,
 		SAMLResponse: encodedReqBuf,
 		RelayState:   relayState,
 	}
@@ -1411,9 +1430,9 @@ func (sp *ServiceProvider) SignLogoutResponse(resp *LogoutResponse) error {
 		Leaf:        sp.Certificate,
 	}
 	// TODO: add intermediates for SP
-	//for _, cert := range sp.Intermediates {
-	//	keyPair.Certificate = append(keyPair.Certificate, cert.Raw)
-	//}
+	// for _, cert := range sp.Intermediates {
+	// 	keyPair.Certificate = append(keyPair.Certificate, cert.Raw)
+	// }
 	keyStore := dsig.TLSCertKeyStore(keyPair)
 
 	if sp.SignatureMethod != dsig.RSASHA1SignatureMethod &&
@@ -1502,10 +1521,7 @@ func (sp *ServiceProvider) ValidateLogoutResponseForm(postFormData string) error
 		retErr.PrivateErr = err
 		return retErr
 	}
-	if err := sp.validateLogoutResponse(&resp); err != nil {
-		return err
-	}
-	return nil
+	return sp.validateLogoutResponse(&resp)
 }
 
 // ValidateLogoutResponseRedirect returns a nil error if the logout response is valid.
@@ -1524,7 +1540,7 @@ func (sp *ServiceProvider) ValidateLogoutResponseRedirect(queryParameterData str
 	}
 	retErr.Response = string(rawResponseBuf)
 
-	gr, err := ioutil.ReadAll(newSaferFlateReader(bytes.NewBuffer(rawResponseBuf)))
+	gr, err := io.ReadAll(newSaferFlateReader(bytes.NewBuffer(rawResponseBuf)))
 	if err != nil {
 		retErr.PrivateErr = err
 		return retErr
@@ -1550,10 +1566,7 @@ func (sp *ServiceProvider) ValidateLogoutResponseRedirect(queryParameterData str
 		retErr.PrivateErr = err
 		return retErr
 	}
-	if err := sp.validateLogoutResponse(&resp); err != nil {
-		return err
-	}
-	return nil
+	return sp.validateLogoutResponse(&resp)
 }
 
 // validateLogoutResponse validates the LogoutResponse fields. Returns a nil error if the LogoutResponse is valid.
@@ -1585,6 +1598,7 @@ func firstSet(a, b string) string {
 
 // findChildren returns all the elements matching childNS/childTag that are direct children of parentEl.
 func findChildren(parentEl *etree.Element, childNS string, childTag string) ([]*etree.Element, error) {
+	//nolint:prealloc // We don't know how many child elements we'll actually put into this array.
 	var rv []*etree.Element
 	for _, childEl := range parentEl.ChildElements() {
 		if childEl.Tag != childTag {
diff --git a/vendor/github.com/crewjam/saml/util.go b/vendor/github.com/crewjam/saml/util.go
index c9731b1b187324bd1daae4ffab82c845df461873..eda053eebd7610a2623673ce9087580076d61ada 100644
--- a/vendor/github.com/crewjam/saml/util.go
+++ b/vendor/github.com/crewjam/saml/util.go
@@ -21,6 +21,7 @@ var Clock *dsig.Clock
 // rand.Reader, but it can be replaced for testing.
 var RandReader = rand.Reader
 
+//nolint:unparam // This always receives 20, but we want the option to do more or less if needed.
 func randomBytes(n int) []byte {
 	rv := make([]byte, n)
 
diff --git a/vendor/github.com/crewjam/saml/xmlenc/cbc.go b/vendor/github.com/crewjam/saml/xmlenc/cbc.go
index 77ddb3b2fd91e8e6c016804816294921d97fea36..991ba1ebdcb39a268c106088ee6a25b84ec919cd 100644
--- a/vendor/github.com/crewjam/saml/xmlenc/cbc.go
+++ b/vendor/github.com/crewjam/saml/xmlenc/cbc.go
@@ -31,7 +31,7 @@ func (e CBC) Algorithm() string {
 
 // Encrypt encrypts plaintext with key, which should be a []byte of length KeySize().
 // It returns an xenc:EncryptedData element.
-func (e CBC) Encrypt(key interface{}, plaintext []byte, nonce []byte) (*etree.Element, error) {
+func (e CBC) Encrypt(key interface{}, plaintext []byte, _ []byte) (*etree.Element, error) {
 	keyBuf, ok := key.([]byte)
 	if !ok {
 		return nil, ErrIncorrectKeyType("[]byte")
diff --git a/vendor/github.com/crewjam/saml/xmlenc/decrypt.go b/vendor/github.com/crewjam/saml/xmlenc/decrypt.go
index 93991f9f5f754e7e3cc5a80a12ae5c453e7774bf..98a575da981ee56a3ad2b52fff07fcbbe5ff68e1 100644
--- a/vendor/github.com/crewjam/saml/xmlenc/decrypt.go
+++ b/vendor/github.com/crewjam/saml/xmlenc/decrypt.go
@@ -90,6 +90,7 @@ func validateRSAKeyIfPresent(key interface{}, encryptedKey *etree.Element) (*rsa
 	// if the key will work, or let the service provider know which key
 	// to use to decrypt the message. Either way, verification is not
 	// security-critical.
+	//nolint:revive,staticcheck // Keep the later empty branch so that we know to address this at a later date.
 	if el := encryptedKey.FindElement("./KeyInfo/X509Data/X509Certificate"); el != nil {
 		certPEMbuf := el.Text()
 		certPEMbuf = "-----BEGIN CERTIFICATE-----\n" + certPEMbuf + "\n-----END CERTIFICATE-----\n"
diff --git a/vendor/github.com/crewjam/saml/xmlenc/digest.go b/vendor/github.com/crewjam/saml/xmlenc/digest.go
index 801347f26f57aae4bea1304db1eb9c02fcf5d8df..3eaaf7bc7871aa07ded496d0b9e0998d9209993e 100644
--- a/vendor/github.com/crewjam/saml/xmlenc/digest.go
+++ b/vendor/github.com/crewjam/saml/xmlenc/digest.go
@@ -6,6 +6,7 @@ import (
 	"crypto/sha512"
 	"hash"
 
+	//nolint:staticcheck // We should support this for legacy reasons.
 	"golang.org/x/crypto/ripemd160"
 )
 
diff --git a/vendor/github.com/russellhaering/goxmldsig/README.md b/vendor/github.com/russellhaering/goxmldsig/README.md
index a7758875156484209dc3960c4120d7c2c09bbb5e..c572e8c75ca90102316509723391d60cf940a785 100644
--- a/vendor/github.com/russellhaering/goxmldsig/README.md
+++ b/vendor/github.com/russellhaering/goxmldsig/README.md
@@ -15,6 +15,19 @@ $ go get github.com/russellhaering/goxmldsig
 
 ## Usage
 
+Include the [`types.Signature`](https://pkg.go.dev/github.com/russellhaering/goxmldsig/types#Signature) struct from this package in your application messages.
+
+```go
+import (
+    sigtypes "github.com/russellhaering/goxmldsig/types"
+)
+
+type AppHdr struct {
+    ...
+    Signature *sigtypes.Signature
+}
+```
+
 ### Signing
 
 ```go
diff --git a/vendor/github.com/russellhaering/goxmldsig/etreeutils/canonicalize.go b/vendor/github.com/russellhaering/goxmldsig/etreeutils/canonicalize.go
index 8437fe4037a254281bf904c586649d299c5131bb..82ceb0a2a517617d82244e45bd6ba09f040184ea 100644
--- a/vendor/github.com/russellhaering/goxmldsig/etreeutils/canonicalize.go
+++ b/vendor/github.com/russellhaering/goxmldsig/etreeutils/canonicalize.go
@@ -16,7 +16,8 @@ func TransformExcC14n(el *etree.Element, inclusiveNamespacesPrefixList string, c
 		prefixSet[prefix] = struct{}{}
 	}
 
-	err := transformExcC14n(DefaultNSContext, DefaultNSContext, el, prefixSet, comments)
+	ctx := NewDefaultNSContext()
+	err := transformExcC14n(ctx, ctx, el, prefixSet, comments)
 	if err != nil {
 		return err
 	}
@@ -31,7 +32,7 @@ func transformExcC14n(ctx, declared NSContext, el *etree.Element, inclusiveNames
 	}
 
 	visiblyUtilizedPrefixes := map[string]struct{}{
-		el.Space: struct{}{},
+		el.Space: {},
 	}
 
 	filteredAttrs := []etree.Attr{}
diff --git a/vendor/github.com/russellhaering/goxmldsig/etreeutils/namespace.go b/vendor/github.com/russellhaering/goxmldsig/etreeutils/namespace.go
index baf1124f66e08e3c1e8e9d841fd4e25fb75d0d70..6a5be036304350b7efdda9de03009f6c3afeff5a 100644
--- a/vendor/github.com/russellhaering/goxmldsig/etreeutils/namespace.go
+++ b/vendor/github.com/russellhaering/goxmldsig/etreeutils/namespace.go
@@ -2,9 +2,7 @@ package etreeutils
 
 import (
 	"errors"
-
 	"fmt"
-
 	"sort"
 
 	"github.com/beevik/etree"
@@ -19,20 +17,25 @@ const (
 	XMLNSNamespace = "http://www.w3.org/2000/xmlns/"
 )
 
-var (
-	DefaultNSContext = NSContext{
+func NewDefaultNSContext() NSContext {
+	defaultLimit := 1000
+	return NSContext{
 		prefixes: map[string]string{
 			defaultPrefix: XMLNamespace,
 			xmlPrefix:     XMLNamespace,
 			xmlnsPrefix:   XMLNSNamespace,
 		},
+		limit: &defaultLimit,
 	}
+}
 
+var (
 	EmptyNSContext = NSContext{}
 
 	ErrReservedNamespace       = errors.New("disallowed declaration of reserved namespace")
 	ErrInvalidDefaultNamespace = errors.New("invalid default namespace declaration")
 	ErrTraversalHalted         = errors.New("traversal halted")
+	ErrTraversalLimit          = errors.New("traversal limit reached")
 )
 
 type ErrUndeclaredNSPrefix struct {
@@ -45,6 +48,17 @@ func (e ErrUndeclaredNSPrefix) Error() string {
 
 type NSContext struct {
 	prefixes map[string]string
+	limit    *int
+}
+
+// CheckLimit checks the traversal limit before calling the handler function
+func (ctx NSContext) CheckLimit() error {
+	if *ctx.limit <= 0 {
+		return ErrTraversalLimit
+	}
+	*ctx.limit--
+
+	return nil
 }
 
 func (ctx NSContext) Copy() NSContext {
@@ -53,7 +67,7 @@ func (ctx NSContext) Copy() NSContext {
 		prefixes[k] = v
 	}
 
-	return NSContext{prefixes: prefixes}
+	return NSContext{prefixes: prefixes, limit: ctx.limit}
 }
 
 func (ctx NSContext) declare(prefix, namespace string) etree.Attr {
@@ -140,7 +154,12 @@ type NSIterHandler func(NSContext, *etree.Element) error
 // NSTraverse traverses an element tree, invoking the passed handler for each element
 // in the tree.
 func NSTraverse(ctx NSContext, el *etree.Element, handle NSIterHandler) error {
-	ctx, err := ctx.SubContext(el)
+	err := ctx.CheckLimit()
+	if err != nil {
+		return err
+	}
+
+	ctx, err = ctx.SubContext(el)
 	if err != nil {
 		return err
 	}
@@ -223,7 +242,7 @@ func NSDetatch(ctx NSContext, el *etree.Element) (*etree.Element, error) {
 // NSSelectOne behaves identically to NSSelectOneCtx, but uses DefaultNSContext as the
 // surrounding context.
 func NSSelectOne(el *etree.Element, namespace, tag string) (*etree.Element, error) {
-	return NSSelectOneCtx(DefaultNSContext, el, namespace, tag)
+	return NSSelectOneCtx(NewDefaultNSContext(), el, namespace, tag)
 }
 
 // NSSelectOneCtx conducts a depth-first search for an element with the specified namespace
@@ -243,7 +262,6 @@ func NSSelectOneCtx(ctx NSContext, el *etree.Element, namespace, tag string) (*e
 
 		return ErrTraversalHalted
 	})
-
 	if err != nil {
 		return nil, err
 	}
@@ -254,7 +272,7 @@ func NSSelectOneCtx(ctx NSContext, el *etree.Element, namespace, tag string) (*e
 // NSFindIterate behaves identically to NSFindIterateCtx, but uses DefaultNSContext
 // as the surrounding context.
 func NSFindIterate(el *etree.Element, namespace, tag string, handle NSIterHandler) error {
-	return NSFindIterateCtx(DefaultNSContext, el, namespace, tag, handle)
+	return NSFindIterateCtx(NewDefaultNSContext(), el, namespace, tag, handle)
 }
 
 // NSFindIterateCtx conducts a depth-first traversal searching for elements with the
@@ -294,7 +312,7 @@ func NSFindIterateCtx(ctx NSContext, el *etree.Element, namespace, tag string, h
 // NSFindOne behaves identically to NSFindOneCtx, but uses DefaultNSContext for
 // context.
 func NSFindOne(el *etree.Element, namespace, tag string) (*etree.Element, error) {
-	return NSFindOneCtx(DefaultNSContext, el, namespace, tag)
+	return NSFindOneCtx(NewDefaultNSContext(), el, namespace, tag)
 }
 
 // NSFindOneCtx conducts a depth-first search for the specified element. If such an element
@@ -306,7 +324,6 @@ func NSFindOneCtx(ctx NSContext, el *etree.Element, namespace, tag string) (*etr
 		found = el
 		return ErrTraversalHalted
 	})
-
 	if err != nil {
 		return nil, err
 	}
@@ -325,6 +342,11 @@ func NSIterateChildren(ctx NSContext, el *etree.Element, handle NSIterHandler) e
 
 	// Iterate the child elements.
 	for _, child := range el.ChildElements() {
+		err := ctx.CheckLimit()
+		if err != nil {
+			return err
+		}
+
 		err = handle(ctx, child)
 		if err != nil {
 			return err
@@ -368,7 +390,7 @@ func NSFindChildrenIterateCtx(ctx NSContext, el *etree.Element, namespace, tag s
 // NSFindOneChild behaves identically to NSFindOneChildCtx, but uses
 // DefaultNSContext for context.
 func NSFindOneChild(el *etree.Element, namespace, tag string) (*etree.Element, error) {
-	return NSFindOneChildCtx(DefaultNSContext, el, namespace, tag)
+	return NSFindOneChildCtx(NewDefaultNSContext(), el, namespace, tag)
 }
 
 // NSFindOneCtx conducts a depth-first search for the specified element. If such an
@@ -394,11 +416,10 @@ func NSFindOneChildCtx(ctx NSContext, el *etree.Element, namespace, tag string)
 func NSBuildParentContext(el *etree.Element) (NSContext, error) {
 	parent := el.Parent()
 	if parent == nil {
-		return DefaultNSContext, nil
+		return NewDefaultNSContext(), nil
 	}
 
 	ctx, err := NSBuildParentContext(parent)
-
 	if err != nil {
 		return ctx, err
 	}
diff --git a/vendor/github.com/russellhaering/goxmldsig/sign.go b/vendor/github.com/russellhaering/goxmldsig/sign.go
index 2be34b7f63aebcd0e6f9b0f9202e272783d930f2..cc77f790a37bbdb2ed8bf741f5b14ca7bfb81b6d 100644
--- a/vendor/github.com/russellhaering/goxmldsig/sign.go
+++ b/vendor/github.com/russellhaering/goxmldsig/sign.go
@@ -2,10 +2,12 @@ package dsig
 
 import (
 	"crypto"
+	"crypto/ecdsa"
 	"crypto/rand"
 	"crypto/rsa"
 	_ "crypto/sha1"
 	_ "crypto/sha256"
+	"crypto/x509"
 	"encoding/base64"
 	"errors"
 	"fmt"
@@ -15,11 +17,18 @@ import (
 )
 
 type SigningContext struct {
-	Hash          crypto.Hash
+	Hash crypto.Hash
+
+	// This field will be nil and unused if the SigningContext is created with
+	// NewSigningContext
 	KeyStore      X509KeyStore
 	IdAttribute   string
 	Prefix        string
 	Canonicalizer Canonicalizer
+
+	// KeyStore is mutually exclusive with signer and certs
+	signer crypto.Signer
+	certs  [][]byte
 }
 
 func NewDefaultSigningContext(ks X509KeyStore) *SigningContext {
@@ -32,13 +41,54 @@ func NewDefaultSigningContext(ks X509KeyStore) *SigningContext {
 	}
 }
 
+// NewSigningContext creates a new signing context with the given signer and certificate chain.
+// Note that e.g. rsa.PrivateKey implements the crypto.Signer interface.
+// The certificate chain is a slice of ASN.1 DER-encoded X.509 certificates.
+// A SigningContext created with this function should not use the KeyStore field.
+// It will return error if passed a nil crypto.Signer
+func NewSigningContext(signer crypto.Signer, certs [][]byte) (*SigningContext, error) {
+	if signer == nil {
+		return nil, errors.New("signer cannot be nil for NewSigningContext")
+	}
+	ctx := &SigningContext{
+		Hash:          crypto.SHA256,
+		IdAttribute:   DefaultIdAttr,
+		Prefix:        DefaultPrefix,
+		Canonicalizer: MakeC14N11Canonicalizer(),
+
+		signer: signer,
+		certs:  certs,
+	}
+	return ctx, nil
+}
+
+func (ctx *SigningContext) getPublicKeyAlgorithm() x509.PublicKeyAlgorithm {
+	if ctx.KeyStore != nil {
+		return x509.RSA
+	} else {
+		switch ctx.signer.Public().(type) {
+		case *ecdsa.PublicKey:
+			return x509.ECDSA
+		case *rsa.PublicKey:
+			return x509.RSA
+		}
+	}
+
+	return x509.UnknownPublicKeyAlgorithm
+}
+
 func (ctx *SigningContext) SetSignatureMethod(algorithmID string) error {
-	hash, ok := signatureMethodsByIdentifier[algorithmID]
+	info, ok := signatureMethodsByIdentifier[algorithmID]
 	if !ok {
-		return fmt.Errorf("Unknown SignatureMethod: %s", algorithmID)
+		return fmt.Errorf("unknown SignatureMethod: %s", algorithmID)
 	}
 
-	ctx.Hash = hash
+	algo := ctx.getPublicKeyAlgorithm()
+	if info.PublicKeyAlgorithm != algo {
+		return fmt.Errorf("SignatureMethod %s is incompatible with %s key", algorithmID, algo)
+	}
+
+	ctx.Hash = info.Hash
 
 	return nil
 }
@@ -58,6 +108,46 @@ func (ctx *SigningContext) digest(el *etree.Element) ([]byte, error) {
 	return hash.Sum(nil), nil
 }
 
+func (ctx *SigningContext) signDigest(digest []byte) ([]byte, error) {
+	if ctx.KeyStore != nil {
+		key, _, err := ctx.KeyStore.GetKeyPair()
+		if err != nil {
+			return nil, err
+		}
+
+		rawSignature, err := rsa.SignPKCS1v15(rand.Reader, key, ctx.Hash, digest)
+		if err != nil {
+			return nil, err
+		}
+
+		return rawSignature, nil
+	} else {
+		rawSignature, err := ctx.signer.Sign(rand.Reader, digest, ctx.Hash)
+		if err != nil {
+			return nil, err
+		}
+
+		return rawSignature, nil
+	}
+}
+
+func (ctx *SigningContext) getCerts() ([][]byte, error) {
+	if ctx.KeyStore != nil {
+		if cs, ok := ctx.KeyStore.(X509ChainStore); ok {
+			return cs.GetChain()
+		}
+
+		_, cert, err := ctx.KeyStore.GetKeyPair()
+		if err != nil {
+			return nil, err
+		}
+
+		return [][]byte{cert}, nil
+	} else {
+		return ctx.certs, nil
+	}
+}
+
 func (ctx *SigningContext) constructSignedInfo(el *etree.Element, enveloped bool) (*etree.Element, error) {
 	digestAlgorithmIdentifier := ctx.GetDigestAlgorithmIdentifier()
 	if digestAlgorithmIdentifier == "" {
@@ -97,7 +187,6 @@ func (ctx *SigningContext) constructSignedInfo(el *etree.Element, enveloped bool
 		reference.CreateAttr(URIAttr, "#"+dataId)
 	}
 
-
 	// /SignedInfo/Reference/Transforms
 	transforms := ctx.createNamespacedElement(reference, TransformsTag)
 	if enveloped {
@@ -172,20 +261,12 @@ func (ctx *SigningContext) ConstructSignature(el *etree.Element, enveloped bool)
 		return nil, err
 	}
 
-	key, cert, err := ctx.KeyStore.GetKeyPair()
+	rawSignature, err := ctx.signDigest(digest)
 	if err != nil {
 		return nil, err
 	}
 
-	certs := [][]byte{cert}
-	if cs, ok := ctx.KeyStore.(X509ChainStore); ok {
-		certs, err = cs.GetChain()
-		if err != nil {
-			return nil, err
-		}
-	}
-
-	rawSignature, err := rsa.SignPKCS1v15(rand.Reader, key, ctx.Hash, digest)
+	certs, err := ctx.getCerts()
 	if err != nil {
 		return nil, err
 	}
@@ -222,7 +303,9 @@ func (ctx *SigningContext) SignEnveloped(el *etree.Element) (*etree.Element, err
 }
 
 func (ctx *SigningContext) GetSignatureMethodIdentifier() string {
-	if ident, ok := signatureMethodIdentifiers[ctx.Hash]; ok {
+	algo := ctx.getPublicKeyAlgorithm()
+
+	if ident, ok := signatureMethodIdentifiers[algo][ctx.Hash]; ok {
 		return ident
 	}
 	return ""
@@ -247,11 +330,5 @@ func (ctx *SigningContext) SignString(content string) ([]byte, error) {
 	}
 	digest := hash.Sum(nil)
 
-	var signature []byte
-	if key, _, err := ctx.KeyStore.GetKeyPair(); err != nil {
-		return nil, fmt.Errorf("unable to fetch key for signing: %v", err)
-	} else if signature, err = rsa.SignPKCS1v15(rand.Reader, key, ctx.Hash, digest); err != nil {
-		return nil, fmt.Errorf("error signing: %v", err)
-	}
-	return signature, nil
+	return ctx.signDigest(digest)
 }
diff --git a/vendor/github.com/russellhaering/goxmldsig/validate.go b/vendor/github.com/russellhaering/goxmldsig/validate.go
index 2c65ca1c16f5427e028fed11a043d1144aa08ed9..45e301382b18209ae3487db1272df0d9cb57d457 100644
--- a/vendor/github.com/russellhaering/goxmldsig/validate.go
+++ b/vendor/github.com/russellhaering/goxmldsig/validate.go
@@ -2,7 +2,6 @@ package dsig
 
 import (
 	"bytes"
-	"crypto/rsa"
 	"crypto/x509"
 	"encoding/base64"
 	"errors"
@@ -213,26 +212,12 @@ func (ctx *ValidationContext) verifySignedInfo(sig *types.Signature, canonicaliz
 		return err
 	}
 
-	signatureAlgorithm, ok := signatureMethodsByIdentifier[signatureMethodId]
+	algo, ok := x509SignatureAlgorithmByIdentifier[signatureMethodId]
 	if !ok {
 		return errors.New("Unknown signature method: " + signatureMethodId)
 	}
 
-	hash := signatureAlgorithm.New()
-	_, err = hash.Write(canonical)
-	if err != nil {
-		return err
-	}
-
-	hashed := hash.Sum(nil)
-
-	pubKey, ok := cert.PublicKey.(*rsa.PublicKey)
-	if !ok {
-		return errors.New("Invalid public key")
-	}
-
-	// Verify that the private key matching the public key from the cert was what was used to sign the 'SignedInfo' and produce the 'SignatureValue'
-	err = rsa.VerifyPKCS1v15(pubKey, signatureAlgorithm, hashed[:], decodedSignature)
+	err = cert.CheckSignature(algo, canonical, decodedSignature)
 	if err != nil {
 		return err
 	}
diff --git a/vendor/github.com/russellhaering/goxmldsig/xml_constants.go b/vendor/github.com/russellhaering/goxmldsig/xml_constants.go
index d2b98e257a645ef20eb0bfc700c8e1e94019629e..0526062e94259f9db2dc2094468808a25fd11356 100644
--- a/vendor/github.com/russellhaering/goxmldsig/xml_constants.go
+++ b/vendor/github.com/russellhaering/goxmldsig/xml_constants.go
@@ -1,6 +1,9 @@
 package dsig
 
-import "crypto"
+import (
+	"crypto"
+	"crypto/x509"
+)
 
 const (
 	DefaultPrefix = "ds"
@@ -39,12 +42,17 @@ func (id AlgorithmID) String() string {
 }
 
 const (
-	RSASHA1SignatureMethod   = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"
-	RSASHA256SignatureMethod = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"
-	RSASHA512SignatureMethod = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"
+	RSASHA1SignatureMethod     = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"
+	RSASHA256SignatureMethod   = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"
+	RSASHA384SignatureMethod   = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha384"
+	RSASHA512SignatureMethod   = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"
+	ECDSASHA1SignatureMethod   = "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha1"
+	ECDSASHA256SignatureMethod = "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256"
+	ECDSASHA384SignatureMethod = "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha384"
+	ECDSASHA512SignatureMethod = "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha512"
 )
 
-//Well-known signature algorithms
+// Well-known signature algorithms
 const (
 	// Supported canonicalization algorithms
 	CanonicalXML10ExclusiveAlgorithmId             AlgorithmID = "http://www.w3.org/2001/10/xml-exc-c14n#"
@@ -62,23 +70,54 @@ const (
 var digestAlgorithmIdentifiers = map[crypto.Hash]string{
 	crypto.SHA1:   "http://www.w3.org/2000/09/xmldsig#sha1",
 	crypto.SHA256: "http://www.w3.org/2001/04/xmlenc#sha256",
+	crypto.SHA384: "http://www.w3.org/2001/04/xmldsig-more#sha384",
 	crypto.SHA512: "http://www.w3.org/2001/04/xmlenc#sha512",
 }
 
+type signatureMethodInfo struct {
+	PublicKeyAlgorithm x509.PublicKeyAlgorithm
+	Hash               crypto.Hash
+}
+
 var digestAlgorithmsByIdentifier = map[string]crypto.Hash{}
-var signatureMethodsByIdentifier = map[string]crypto.Hash{}
+var signatureMethodsByIdentifier = map[string]signatureMethodInfo{}
 
 func init() {
 	for hash, id := range digestAlgorithmIdentifiers {
 		digestAlgorithmsByIdentifier[id] = hash
 	}
-	for hash, id := range signatureMethodIdentifiers {
-		signatureMethodsByIdentifier[id] = hash
+	for algo, hashToMethod := range signatureMethodIdentifiers {
+		for hash, method := range hashToMethod {
+			signatureMethodsByIdentifier[method] = signatureMethodInfo{
+				PublicKeyAlgorithm: algo,
+				Hash:               hash,
+			}
+		}
 	}
 }
 
-var signatureMethodIdentifiers = map[crypto.Hash]string{
-	crypto.SHA1:   RSASHA1SignatureMethod,
-	crypto.SHA256: RSASHA256SignatureMethod,
-	crypto.SHA512: RSASHA512SignatureMethod,
+var signatureMethodIdentifiers = map[x509.PublicKeyAlgorithm]map[crypto.Hash]string{
+	x509.RSA: {
+		crypto.SHA1:   RSASHA1SignatureMethod,
+		crypto.SHA256: RSASHA256SignatureMethod,
+		crypto.SHA384: RSASHA384SignatureMethod,
+		crypto.SHA512: RSASHA512SignatureMethod,
+	},
+	x509.ECDSA: {
+		crypto.SHA1:   ECDSASHA1SignatureMethod,
+		crypto.SHA256: ECDSASHA256SignatureMethod,
+		crypto.SHA384: ECDSASHA384SignatureMethod,
+		crypto.SHA512: ECDSASHA512SignatureMethod,
+	},
+}
+
+var x509SignatureAlgorithmByIdentifier = map[string]x509.SignatureAlgorithm{
+	RSASHA1SignatureMethod:     x509.SHA1WithRSA,
+	RSASHA256SignatureMethod:   x509.SHA256WithRSA,
+	RSASHA384SignatureMethod:   x509.SHA384WithRSA,
+	RSASHA512SignatureMethod:   x509.SHA512WithRSA,
+	ECDSASHA1SignatureMethod:   x509.ECDSAWithSHA1,
+	ECDSASHA256SignatureMethod: x509.ECDSAWithSHA256,
+	ECDSASHA384SignatureMethod: x509.ECDSAWithSHA384,
+	ECDSASHA512SignatureMethod: x509.ECDSAWithSHA512,
 }
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 368dcbafb827add6251ca5c364ac378d12ccc588..471160a6f8ba2e9dfee85c06f82e1a72925b40c2 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -38,8 +38,8 @@ github.com/cespare/xxhash/v2
 # github.com/coreos/go-systemd/v22 v22.5.0
 ## explicit; go 1.12
 github.com/coreos/go-systemd/v22/daemon
-# github.com/crewjam/saml v0.4.13
-## explicit; go 1.16
+# github.com/crewjam/saml v0.4.14
+## explicit; go 1.19
 github.com/crewjam/saml
 github.com/crewjam/saml/logger
 github.com/crewjam/saml/xmlenc
@@ -145,7 +145,7 @@ github.com/prometheus/procfs/internal/util
 ## explicit; go 1.13
 github.com/rs/cors
 github.com/rs/cors/internal
-# github.com/russellhaering/goxmldsig v1.2.0
+# github.com/russellhaering/goxmldsig v1.3.0
 ## explicit; go 1.15
 github.com/russellhaering/goxmldsig
 github.com/russellhaering/goxmldsig/etreeutils