From 9f94aabbd2bbb95f8757389f85cab133d6f7cc03 Mon Sep 17 00:00:00 2001 From: xenolf Date: Sun, 19 Feb 2017 05:12:14 +0100 Subject: [PATCH] Fix nonce error (#354) * Adding a NonceError type to detect nonce errors * Implement a one off retry on a nonce error. --- acme/error.go | 15 ++++++++++++++- acme/jws.go | 17 ++++++++++++++--- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/acme/error.go b/acme/error.go index 6d7013cf..e4bc934c 100644 --- a/acme/error.go +++ b/acme/error.go @@ -8,7 +8,10 @@ import ( "strings" ) -const tosAgreementError = "Must agree to subscriber agreement before any further actions" +const ( + tosAgreementError = "Must agree to subscriber agreement before any further actions" + invalidNonceError = "JWS has invalid anti-replay nonce" +) // RemoteError is the base type for all errors specific to the ACME protocol. type RemoteError struct { @@ -28,6 +31,12 @@ type TOSError struct { RemoteError } +// NonceError represents the error which is returned if the +// nonce sent by the client was not accepted by the server. +type NonceError struct { + RemoteError +} + type domainError struct { Domain string Error error @@ -73,6 +82,10 @@ func handleHTTPError(resp *http.Response) error { return TOSError{errorDetail} } + if errorDetail.StatusCode == http.StatusBadRequest && strings.HasPrefix(errorDetail.Detail, invalidNonceError) { + return NonceError{errorDetail} + } + return errorDetail } diff --git a/acme/jws.go b/acme/jws.go index 4818f17e..876b39b7 100644 --- a/acme/jws.go +++ b/acme/jws.go @@ -41,15 +41,26 @@ func (j *jws) post(url string, content []byte) (*http.Response, error) { } resp, err := httpPost(url, "application/jose+json", bytes.NewBuffer([]byte(signedContent.FullSerialize()))) - if err != nil { - return nil, err - } + // Even in case of an error, the response should still contain a nonce. nonce, nonceErr := getNonceFromResponse(resp) if nonceErr == nil { j.nonces.Push(nonce) } + if err != nil { + switch err.(type) { + case NonceError: + // In case of a nonce error - retry once + resp, err = httpPost(url, "application/jose+json", bytes.NewBuffer([]byte(signedContent.FullSerialize()))) + if err != nil { + return nil, err + } + default: + return nil, err + } + } + return resp, err }