diff --git a/index.js b/index.js index 59a51c5..bd0f437 100644 --- a/index.js +++ b/index.js @@ -900,118 +900,127 @@ ACME._finalizeOrder = function (me, options, validatedDomains) { var csr = me.RSA.generateCsrWeb64(options.domainKeypair, validatedDomains); var body = { csr: csr }; var payload = JSON.stringify(body); + var tried = false; + var orderUrl = ''; function pollCert() { - var jws = me.RSA.signJws( - options.accountKeypair, - undefined, - { - nonce: me._nonce, - alg: me._alg || 'RS256', + var certReq; + var jws; + if (!tried) { + tried = true; + jws = me.RSA.signJws( + options.accountKeypair, + undefined, + { + nonce: me._nonce, + alg: me._alg || 'RS256', + url: me._finalize, + kid: me._kid + }, + Buffer.from(payload) + ); + certReq = { + method: 'POST', url: me._finalize, - kid: me._kid - }, - Buffer.from(payload) - ); + headers: { 'Content-Type': 'application/jose+json' }, + json: jws + }; + } else { + jws = me.RSA.signJws( + options.accountKeypair, + undefined, + { + nonce: me._nonce, + alg: me._alg || 'RS256', + url: orderUrl, + kid: me._kid + }, + '' + ); + certReq = { + method: 'POST', + url: orderUrl, + headers: { 'Content-Type': 'application/jose+json' }, + json: jws + }; + } if (me.debug) { console.debug('finalize:', me._finalize); } me._nonce = null; - return me - ._request({ - method: 'POST', - url: me._finalize, - headers: { 'Content-Type': 'application/jose+json' }, - json: jws - }) - .then(function (resp) { - // https://tools.ietf.org/html/draft-ietf-acme-acme-12#section-7.1.3 - // Possible values are: "pending" => ("invalid" || "ready") => "processing" => "valid" - me._nonce = resp.toJSON().headers['replay-nonce']; - if (me.debug) { - console.debug('order finalized: resp.body:'); - } - if (me.debug) { - console.debug(resp.body); - } + return me._request(certReq).then(function (resp) { + // https://tools.ietf.org/html/draft-ietf-acme-acme-12#section-7.1.3 + // Possible values are: "pending" => ("invalid" || "ready") => "processing" => "valid" + me._nonce = resp.toJSON().headers['replay-nonce']; + if (resp.headers.location) { + orderUrl = resp.headers.location; + } - if ('valid' === resp.body.status) { - me._expires = resp.body.expires; - me._certificate = resp.body.certificate; + if (me.debug) { + console.debug('order finalized: resp.body:'); + } + if (me.debug) { + console.debug(resp.body); + } - return resp.body; // return order - } + if ('valid' === resp.body.status) { + me._expires = resp.body.expires; + me._certificate = resp.body.certificate; - if ('processing' === resp.body.status) { - return ACME._wait().then(pollCert); - } + return resp.body; // return order + } - if (me.debug) { - console.debug( - 'Error: bad status:\n' + JSON.stringify(resp.body, null, 2) - ); - } + if ('processing' === resp.body.status) { + return ACME._wait().then(pollCert); + } - if ('pending' === resp.body.status) { - return Promise.reject( - new Error( - "Did not finalize order: status 'pending'." + - ' Best guess: You have not accepted at least one challenge for each domain:\n' + - "Requested: '" + - options.domains.join(', ') + - "'\n" + - "Validated: '" + - validatedDomains.join(', ') + - "'\n" + - JSON.stringify(resp.body, null, 2) - ) - ); - } - - if ('invalid' === resp.body.status) { - return Promise.reject( - new Error( - "Did not finalize order: status 'invalid'." + - ' Best guess: One or more of the domain challenges could not be verified' + - ' (or the order was canceled).\n' + - "Requested: '" + - options.domains.join(', ') + - "'\n" + - "Validated: '" + - validatedDomains.join(', ') + - "'\n" + - JSON.stringify(resp.body, null, 2) - ) - ); - } - - if ('ready' === resp.body.status) { - return Promise.reject( - new Error( - "Did not finalize order: status 'ready'." + - " Hmmm... this state shouldn't be possible here. That was the last state." + - " This one should at least be 'processing'.\n" + - "Requested: '" + - options.domains.join(', ') + - "'\n" + - "Validated: '" + - validatedDomains.join(', ') + - "'\n" + - JSON.stringify(resp.body, null, 2) + - '\n\n' + - 'Please open an issue at https://git.coolaj86.com/coolaj86/acme-v2.js' - ) - ); - } + if (me.debug) { + console.debug( + 'Error: bad status:\n' + JSON.stringify(resp.body, null, 2) + ); + } + if ('pending' === resp.body.status) { return Promise.reject( new Error( - "Didn't finalize order: Unhandled status '" + - resp.body.status + - "'." + - ' This is not one of the known statuses...\n' + + "Did not finalize order: status 'pending'." + + ' Best guess: You have not accepted at least one challenge for each domain:\n' + + "Requested: '" + + options.domains.join(', ') + + "'\n" + + "Validated: '" + + validatedDomains.join(', ') + + "'\n" + + JSON.stringify(resp.body, null, 2) + ) + ); + } + + if ('invalid' === resp.body.status) { + return Promise.reject( + new Error( + "Did not finalize order: status 'invalid'." + + ' Best guess: One or more of the domain challenges could not be verified' + + ' (or the order was canceled).\n' + + "Requested: '" + + options.domains.join(', ') + + "'\n" + + "Validated: '" + + validatedDomains.join(', ') + + "'\n" + + JSON.stringify(resp.body, null, 2) + ) + ); + } + + if ('ready' === resp.body.status) { + return Promise.reject( + new Error( + "Did not finalize order: status 'ready'." + + " Hmmm... this state shouldn't be possible here. That was the last state." + + " This one should at least be 'processing'.\n" + "Requested: '" + options.domains.join(', ') + "'\n" + @@ -1023,7 +1032,26 @@ ACME._finalizeOrder = function (me, options, validatedDomains) { 'Please open an issue at https://git.coolaj86.com/coolaj86/acme-v2.js' ) ); - }); + } + + return Promise.reject( + new Error( + "Didn't finalize order: Unhandled status '" + + resp.body.status + + "'." + + ' This is not one of the known statuses...\n' + + "Requested: '" + + options.domains.join(', ') + + "'\n" + + "Validated: '" + + validatedDomains.join(', ') + + "'\n" + + JSON.stringify(resp.body, null, 2) + + '\n\n' + + 'Please open an issue at https://git.coolaj86.com/coolaj86/acme-v2.js' + ) + ); + }); } return pollCert(); @@ -1177,7 +1205,11 @@ ACME._getCertificate = function (me, options) { me._authorizations = resp.body.authorizations; me._order = location; me._finalize = resp.body.finalize; - //if (me.debug) console.debug('[DEBUG] finalize:', me._finalize); return; + if (me.debug) + console.debug('[DEBUG] pre-finalize:', resp.headers); + if (me.debug) console.debug('[DEBUG] pre-finalize:', resp.body); + if (me.debug) + console.debug('[DEBUG] pre-finalize:', me._finalize); if (!me._authorizations) { return Promise.reject(