diff --git a/acme/autocert/autocert.go b/acme/autocert/autocert.go index 77788d0..af2403d 100644 --- a/acme/autocert/autocert.go +++ b/acme/autocert/autocert.go @@ -404,6 +404,11 @@ func (m *Manager) verify(ctx context.Context, domain string) error { if err != nil { return err } + // maybe don't need to at all + if authz.Status == acme.StatusValid { + return nil + } + // pick a challenge: prefer tls-sni-02 over tls-sni-01 // TODO: consider authz.Combinations var chal *acme.Challenge diff --git a/acme/internal/acme/acme.go b/acme/internal/acme/acme.go index 93140af..cee8f3e 100644 --- a/acme/internal/acme/acme.go +++ b/acme/internal/acme/acme.go @@ -302,6 +302,10 @@ func (c *Client) UpdateReg(ctx context.Context, a *Account) (*Account, error) { // Authorize performs the initial step in an authorization flow. // The caller will then need to choose from and perform a set of returned // challenges using c.Accept in order to successfully complete authorization. +// +// If an authorization has been previously granted, the CA may return +// a valid authorization (Authorization.Status is StatusValid). If so, the caller +// need not fulfill any challenge and can proceed to requesting a certificate. func (c *Client) Authorize(ctx context.Context, domain string) (*Authorization, error) { if _, err := c.Discover(ctx); err != nil { return nil, err @@ -331,7 +335,7 @@ func (c *Client) Authorize(ctx context.Context, domain string) (*Authorization, if err := json.NewDecoder(res.Body).Decode(&v); err != nil { return nil, fmt.Errorf("acme: invalid response: %v", err) } - if v.Status != StatusPending { + if v.Status != StatusPending && v.Status != StatusValid { return nil, fmt.Errorf("acme: unexpected status: %s", v.Status) } return v.authorization(res.Header.Get("Location")), nil diff --git a/acme/internal/acme/acme_test.go b/acme/internal/acme/acme_test.go index 4edeadc..fc54079 100644 --- a/acme/internal/acme/acme_test.go +++ b/acme/internal/acme/acme_test.go @@ -368,6 +368,23 @@ func TestAuthorize(t *testing.T) { } } +func TestAuthorizeValid(t *testing.T) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == "HEAD" { + w.Header().Set("replay-nonce", "nonce") + return + } + w.WriteHeader(http.StatusCreated) + w.Write([]byte(`{"status":"valid"}`)) + })) + defer ts.Close() + client := Client{Key: testKey, dir: &Directory{AuthzURL: ts.URL}} + _, err := client.Authorize(context.Background(), "example.com") + if err != nil { + t.Errorf("err = %v", err) + } +} + func TestPollAuthz(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method != "GET" {