func (h *Handler) verifyAndExtractJWSPayload(next nextHTTP) nextHTTP {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := r.Context()
		jws, err := jwsFromContext(ctx)
		if err != nil {
			api.WriteError(w, err)
			return
		}
		jwk, err := jwkFromContext(ctx)
		if err != nil {
			api.WriteError(w, err)
			return
		}
		if jwk.Algorithm != "" && jwk.Algorithm != jws.Signatures[0].Protected.Algorithm {
			api.WriteError(w, acme.NewError(acme.ErrorMalformedType, "verifier and signature algorithm do not match"))
			return
		}
		payload, err := jws.Verify(jwk)
		if err != nil {
			api.WriteError(w, acme.WrapError(acme.ErrorMalformedType, err, "error verifying jws"))
			return
		}
		ctx = context.WithValue(ctx, payloadContextKey, &payloadInfo{
			value:       payload,
			isPostAsGet: string(payload) == "",
			isEmptyJSON: string(payload) == "{}",
		})
		next(w, r.WithContext(ctx))
	}
}
verifyAndExtractJWSPayload extracts the JWK from the JWS and saves it in the context.
Make sure to parse and validate the JWS before running this middleware.
package api
import (
	"context"
	"crypto/rsa"
	"errors"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
	"github.com/go-chi/chi"
	"github.com/smallstep/certificates/acme"
	"github.com/smallstep/certificates/api"
	"github.com/smallstep/certificates/authority/provisioner"
	"github.com/smallstep/certificates/logging"
	"github.com/smallstep/nosql"
	"go.step.sm/crypto/jose"
	"go.step.sm/crypto/keyutil"
)
type nextHTTP = func(http.ResponseWriter, *http.Request)
func logNonce(w http.ResponseWriter, nonce string) {
	if rl, ok := w.(logging.ResponseLogger); ok {
		m := map[string]interface{}{
			"nonce": nonce,
		}
		rl.WithFields(m)
	}
}
func baseURLFromRequest(r *http.Request) *url.URL {
	if r.Host == "" {
		return nil
	}
	return &url.URL{Scheme: "https", Host: r.Host}
}
func (h *Handler) baseURLFromRequest(next nextHTTP) nextHTTP {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := context.WithValue(r.Context(), baseURLContextKey, baseURLFromRequest(r))
		next(w, r.WithContext(ctx))
	}
}
func (h *Handler) addNonce(next nextHTTP) nextHTTP {
	return func(w http.ResponseWriter, r *http.Request) {
		nonce, err := h.db.CreateNonce(r.Context())
		if err != nil {
			api.WriteError(w, err)
			return
		}
		w.Header().Set("Replay-Nonce", string(nonce))
		w.Header().Set("Cache-Control", "no-store")
		logNonce(w, string(nonce))
		next(w, r)
	}
}
func (h *Handler) addDirLink(next nextHTTP) nextHTTP {
	return func(w http.ResponseWriter, r *http.Request) {
		w.Header().Add("Link", link(h.linker.GetLink(r.Context(), DirectoryLinkType), "index"))
		next(w, r)
	}
}
func (h *Handler) verifyContentType(next nextHTTP) nextHTTP {
	return func(w http.ResponseWriter, r *http.Request) {
		var expected []string
		p, err := provisionerFromContext(r.Context())
		if err != nil {
			api.WriteError(w, err)
			return
		}
		u := url.URL{Path: h.linker.GetUnescapedPathSuffix(CertificateLinkType, p.GetName(), "")}
		if strings.Contains(r.URL.String(), u.EscapedPath()) {
			expected = []string{"application/jose+json", "application/pkix-cert", "application/pkcs7-mime"}
		} else {
			expected = []string{"application/jose+json"}
		}
		ct := r.Header.Get("Content-Type")
		for _, e := range expected {
			if ct == e {
				next(w, r)
				return
			}
		}
		api.WriteError(w, acme.NewError(acme.ErrorMalformedType,
			"expected content-type to be in %s, but got %s", expected, ct))
	}
}
func (h *Handler) parseJWS(next nextHTTP) nextHTTP {
	return func(w http.ResponseWriter, r *http.Request) {
		body, err := ioutil.ReadAll(r.Body)
		if err != nil {
			api.WriteError(w, acme.WrapErrorISE(err, "failed to read request body"))
			return
		}
		jws, err := jose.ParseJWS(string(body))
		if err != nil {
			api.WriteError(w, acme.WrapError(acme.ErrorMalformedType, err, "failed to parse JWS from request body"))
			return
		}
		ctx := context.WithValue(r.Context(), jwsContextKey, jws)
		next(w, r.WithContext(ctx))
	}
}
func (h *Handler) validateJWS(next nextHTTP) nextHTTP {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := r.Context()
		jws, err := jwsFromContext(r.Context())
		if err != nil {
			api.WriteError(w, err)
			return
		}
		if len(jws.Signatures) == 0 {
			api.WriteError(w, acme.NewError(acme.ErrorMalformedType, "request body does not contain a signature"))
			return
		}
		if len(jws.Signatures) > 1 {
			api.WriteError(w, acme.NewError(acme.ErrorMalformedType, "request body contains more than one signature"))
			return
		}
		sig := jws.Signatures[0]
		uh := sig.Unprotected
		if len(uh.KeyID) > 0 ||
			uh.JSONWebKey != nil ||
			len(uh.Algorithm) > 0 ||
			len(uh.Nonce) > 0 ||
			len(uh.ExtraHeaders) > 0 {
			api.WriteError(w, acme.NewError(acme.ErrorMalformedType, "unprotected header must not be used"))
			return
		}
		hdr := sig.Protected
		switch hdr.Algorithm {
		case jose.RS256, jose.RS384, jose.RS512, jose.PS256, jose.PS384, jose.PS512:
			if hdr.JSONWebKey != nil {
				switch k := hdr.JSONWebKey.Key.(type) {
				case *rsa.PublicKey:
					if k.Size() < keyutil.MinRSAKeyBytes {
						api.WriteError(w, acme.NewError(acme.ErrorMalformedType,
							"rsa keys must be at least %d bits (%d bytes) in size",
							8*keyutil.MinRSAKeyBytes, keyutil.MinRSAKeyBytes))
						return
					}
				default:
					api.WriteError(w, acme.NewError(acme.ErrorMalformedType,
						"jws key type and algorithm do not match"))
					return
				}
			}
		case jose.ES256, jose.ES384, jose.ES512, jose.EdDSA:
		default:
			api.WriteError(w, acme.NewError(acme.ErrorBadSignatureAlgorithmType, "unsuitable algorithm: %s", hdr.Algorithm))
			return
		}
		if err := h.db.DeleteNonce(ctx, acme.Nonce(hdr.Nonce)); err != nil {
			api.WriteError(w, err)
			return
		}
		jwsURL, ok := hdr.ExtraHeaders["url"].(string)
		if !ok {
			api.WriteError(w, acme.NewError(acme.ErrorMalformedType, "jws missing url protected header"))
			return
		}
		reqURL := &url.URL{Scheme: "https", Host: r.Host, Path: r.URL.Path}
		if jwsURL != reqURL.String() {
			api.WriteError(w, acme.NewError(acme.ErrorMalformedType,
				"url header in JWS (%s) does not match request url (%s)", jwsURL, reqURL))
			return
		}
		if hdr.JSONWebKey != nil && len(hdr.KeyID) > 0 {
			api.WriteError(w, acme.NewError(acme.ErrorMalformedType, "jwk and kid are mutually exclusive"))
			return
		}
		if hdr.JSONWebKey == nil && hdr.KeyID == "" {
			api.WriteError(w, acme.NewError(acme.ErrorMalformedType, "either jwk or kid must be defined in jws protected header"))
			return
		}
		next(w, r)
	}
}
func (h *Handler) extractJWK(next nextHTTP) nextHTTP {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := r.Context()
		jws, err := jwsFromContext(r.Context())
		if err != nil {
			api.WriteError(w, err)
			return
		}
		jwk := jws.Signatures[0].Protected.JSONWebKey
		if jwk == nil {
			api.WriteError(w, acme.NewError(acme.ErrorMalformedType, "jwk expected in protected header"))
			return
		}
		if !jwk.Valid() {
			api.WriteError(w, acme.NewError(acme.ErrorMalformedType, "invalid jwk in protected header"))
			return
		}
		jwk.KeyID, err = acme.KeyToID(jwk)
		if err != nil {
			api.WriteError(w, acme.WrapErrorISE(err, "error getting KeyID from JWK"))
			return
		}
		ctx = context.WithValue(ctx, jwkContextKey, jwk)
		acc, err := h.db.GetAccountByKeyID(ctx, jwk.KeyID)
		switch {
		case errors.Is(err, acme.ErrNotFound):
			break
		case err != nil:
			api.WriteError(w, err)
			return
		default:
			if !acc.IsValid() {
				api.WriteError(w, acme.NewError(acme.ErrorUnauthorizedType, "account is not active"))
				return
			}
			ctx = context.WithValue(ctx, accContextKey, acc)
		}
		next(w, r.WithContext(ctx))
	}
}
func (h *Handler) lookupProvisioner(next nextHTTP) nextHTTP {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := r.Context()
		nameEscaped := chi.URLParam(r, "provisionerID")
		name, err := url.PathUnescape(nameEscaped)
		if err != nil {
			api.WriteError(w, acme.WrapErrorISE(err, "error url unescaping provisioner name '%s'", nameEscaped))
			return
		}
		p, err := h.ca.LoadProvisionerByName(name)
		if err != nil {
			api.WriteError(w, err)
			return
		}
		acmeProv, ok := p.(*provisioner.ACME)
		if !ok {
			api.WriteError(w, acme.NewError(acme.ErrorAccountDoesNotExistType, "provisioner must be of type ACME"))
			return
		}
		ctx = context.WithValue(ctx, provisionerContextKey, acme.Provisioner(acmeProv))
		next(w, r.WithContext(ctx))
	}
}
func (h *Handler) lookupJWK(next nextHTTP) nextHTTP {
	return func(w http.ResponseWriter, r *http.Request) {
		ctx := r.Context()
		jws, err := jwsFromContext(ctx)
		if err != nil {
			api.WriteError(w, err)
			return
		}
		kidPrefix := h.linker.GetLink(ctx, AccountLinkType, "")
		kid := jws.Signatures[0].Protected.KeyID
		if !strings.HasPrefix(kid, kidPrefix) {
			api.WriteError(w, acme.NewError(acme.ErrorMalformedType,
				"kid does not have required prefix; expected %s, but got %s",
				kidPrefix, kid))
			return
		}
		accID := strings.TrimPrefix(kid, kidPrefix)
		acc, err := h.db.GetAccount(ctx, accID)
		switch {
		case nosql.IsErrNotFound(err):
			api.WriteError(w, acme.NewError(acme.ErrorAccountDoesNotExistType, "account with ID '%s' not found", accID))
			return
		case err != nil:
			api.WriteError(w, err)
			return
		default:
			if !acc.IsValid() {
				api.WriteError(w, acme.NewError(acme.ErrorUnauthorizedType, "account is not active"))
				return
			}
			ctx = context.WithValue(ctx, accContextKey, acc)
			ctx = context.WithValue(ctx, jwkContextKey, acc.Key)
			next(w, r.WithContext(ctx))
			return
		}
	}
}