Giter VIP home page Giter VIP logo

node-acme-client's Introduction

acme-client test

A simple and unopinionated ACME client.

This module is written to handle communication with a Boulder/Let's Encrypt-style ACME API.

Compatibility

acme-client Node.js
v5.x >= v16 Upgrade guide
v4.x >= v10 Changelog
v3.x >= v8 Changelog
v2.x >= v4 Changelog
v1.x >= v4 Changelog

Table of contents

Installation

$ npm install acme-client

Usage

const acme = require('acme-client');

const accountPrivateKey = '<PEM encoded private key>';

const client = new acme.Client({
    directoryUrl: acme.directory.letsencrypt.staging,
    accountKey: accountPrivateKey
});

Directory URLs

acme.directory.buypass.staging;
acme.directory.buypass.production;

acme.directory.letsencrypt.staging;
acme.directory.letsencrypt.production;

acme.directory.zerossl.production;

External account binding

To enable external account binding when creating your ACME account, provide your KID and HMAC key to the client constructor.

const client = new acme.Client({
    directoryUrl: 'https://acme-provider.example.com/directory-url',
    accountKey: accountPrivateKey,
    externalAccountBinding: {
        kid: 'YOUR-EAB-KID',
        hmacKey: 'YOUR-EAB-HMAC-KEY'
    }
});

Specifying the account URL

During the ACME account creation process, the server will check the supplied account key and either create a new account if the key is unused, or return the existing ACME account bound to that key.

In some cases, for example with some EAB providers, this account creation step may be prohibited and might require you to manually specify the account URL beforehand. This can be done through accountUrl in the client constructor.

const client = new acme.Client({
    directoryUrl: acme.directory.letsencrypt.staging,
    accountKey: accountPrivateKey,
    accountUrl: 'https://acme-v02.api.letsencrypt.org/acme/acct/12345678'
});

You can fetch the clients current account URL, either after creating an account or supplying it through the constructor, using getAccountUrl():

const myAccountUrl = client.getAccountUrl();

Cryptography

For key pairs acme-client utilizes native Node.js cryptography APIs, supporting signing and generation of both RSA and ECDSA keys. The module @peculiar/x509 is used to generate and parse Certificate Signing Requests.

These utility methods are exposed through .crypto.

const privateRsaKey = await acme.crypto.createPrivateRsaKey();
const privateEcdsaKey = await acme.crypto.createPrivateEcdsaKey();

const [certificateKey, certificateCsr] = await acme.crypto.createCsr({
    commonName: '*.example.com',
    altNames: ['example.com']
});

Legacy .forge interface

The legacy node-forge crypto interface is still available for backward compatibility, however this interface is now considered deprecated and will be removed in a future major version of acme-client.

You should consider migrating to the new .crypto API at your earliest convenience. More details can be found in the acme-client v5 upgrade guide.

Auto mode

For convenience an auto() method is included in the client that takes a single config object. This method will handle the entire process of getting a certificate for one or multiple domains.

const autoOpts = {
    csr: '<PEM encoded CSR>',
    email: '[email protected]',
    termsOfServiceAgreed: true,
    challengeCreateFn: async (authz, challenge, keyAuthorization) => {},
    challengeRemoveFn: async (authz, challenge, keyAuthorization) => {}
};

const certificate = await client.auto(autoOpts);

Challenge priority

When ordering a certificate using auto mode, acme-client uses a priority list when selecting challenges to respond to. Its default value is ['http-01', 'dns-01'] which translates to "use http-01 if any challenges exist, otherwise fall back to dns-01".

While most challenges can be validated using the method of your choosing, please note that wildcard certificates can only be validated through dns-01. More information regarding Let's Encrypt challenge types can be found here.

To modify challenge priority, provide a list of challenge types in challengePriority:

await client.auto({
    ...,
    challengePriority: ['http-01', 'dns-01']
});

Internal challenge verification

When using auto mode, acme-client will first validate that challenges are satisfied internally before completing the challenge at the ACME provider. In some cases (firewalls, etc) this internal challenge verification might not be possible to complete.

If internal challenge validation needs to travel through an HTTP proxy, see HTTP client defaults.

To completely disable acme-clients internal challenge verification, enable skipChallengeVerification:

await client.auto({
    ...,
    skipChallengeVerification: true
});

API

For more fine-grained control you can interact with the ACME API using the methods documented below.

const account = await client.createAccount({
    termsOfServiceAgreed: true,
    contact: ['mailto:[email protected]']
});

const order = await client.createOrder({
    identifiers: [
        { type: 'dns', value: 'example.com' },
        { type: 'dns', value: '*.example.com' }
    ]
});

HTTP client defaults

This module uses axios when communicating with the ACME HTTP API, and exposes the client instance through .axios.

For example, should you need to change the default axios configuration to route requests through an HTTP proxy, this can be achieved as follows:

const acme = require('acme-client');

acme.axios.defaults.proxy = {
    host: '127.0.0.1',
    port: 9000
};

A complete list of axios options and documentation can be found at:

Debugging

To get a better grasp of what acme-client is doing behind the scenes, you can either pass it a logger function, or enable debugging through an environment variable.

Setting a logger function may for example be useful for passing messages on to another logging system, or just dumping them to the console.

acme.setLogger((message) => {
    console.log(message);
});

Debugging to the console can also be enabled through debug by setting an environment variable.

DEBUG=acme-client node index.js

License

MIT

node-acme-client's People

Contributors

bryanvaz avatar dependabot[bot] avatar dweitzman-codaio avatar frenzzy avatar mmalone avatar nmorsman avatar shittyadvice avatar timwolla avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

node-acme-client's Issues

JWS verification error on client.revokeCertificate

I am trying to revoke a certificate that was issued using client.auto() using client.revokeCertificate(), but it throws the following error:

Error: JWS verification error
    at AcmeApi.apiRequest (node_modules/.pnpm/[email protected]/node_modules/acme-client/src/api.js:56:19)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at async AcmeClient.revokeCertificate (node_modules/.pnpm/[email protected]/node_modules/acme-client/src/client.js:681:22)

The reproduction code is below (it is a staging certificate, no security issue posting it here):

import * as acme from 'acme-client'

const accountUrl =
  'https://acme-staging-v02.api.letsencrypt.org/acme/acct/59183794'
const certificate = `-----BEGIN CERTIFICATE-----
MIIFbDCCBFSgAwIBAgITAPog0TD3lAcsQWrmy6prUar91TANBgkqhkiG9w0BAQsF
ADBZMQswCQYDVQQGEwJVUzEgMB4GA1UEChMXKFNUQUdJTkcpIExldCdzIEVuY3J5
cHQxKDAmBgNVBAMTHyhTVEFHSU5HKSBBcnRpZmljaWFsIEFwcmljb3QgUjMwHhcN
MjIwNjMwMTE0NzA0WhcNMjIwOTI4MTE0NzAzWjAmMSQwIgYDVQQDExtjZXJ0LXRl
c3QuaW50ZXJuYWwuc3Z0di5vcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQDPXwdEoHKGPhQmlK3yB3DeVjCn6r2XDyv/0bJpf83Qez3I4PYu5WUCz+tZ
hf/tz3FVW7IntcyIGxEZDKHFS2cO5D36zV8PixFBy7i02ioxaic2Kl/fVZ7GTPmX
jXO5pD3sRmyX6PN70r8TwBZAQ+rzM4BjR4PBEWTmBQe/oCln5fhr1Yj/vL3E/bVO
7CGBTP+0BoGQfDeY6nT0YIpYByDRu7VVYL1RVyLq7/wqDphzsw/uYDIxCbCAFOSG
zbo/7A1V6fw3y0G3s22YA+zxkNyBiznJTSo2e8hesdYUVNqgs0MMBvzefWmYogS9
N9RImGN+IUCYUygfeH//wMqzHZ6HAgMBAAGjggJeMIICWjAOBgNVHQ8BAf8EBAMC
BaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAw
HQYDVR0OBBYEFGOSCPl36dS1aexxbcguNJQeqz6eMB8GA1UdIwQYMBaAFN5yekjf
McOmUN+fhSPfVzdLXS5lMF0GCCsGAQUFBwEBBFEwTzAlBggrBgEFBQcwAYYZaHR0
cDovL3N0Zy1yMy5vLmxlbmNyLm9yZzAmBggrBgEFBQcwAoYaaHR0cDovL3N0Zy1y
My5pLmxlbmNyLm9yZy8wJgYDVR0RBB8wHYIbY2VydC10ZXN0LmludGVybmFsLnN2
dHYub3JnMEwGA1UdIARFMEMwCAYGZ4EMAQIBMDcGCysGAQQBgt8TAQEBMCgwJgYI
KwYBBQUHAgEWGmh0dHA6Ly9jcHMubGV0c2VuY3J5cHQub3JnMIIBBAYKKwYBBAHW
eQIEAgSB9QSB8gDwAHYAKHYaGJAn++880NYaAY12sFBXKcenQRvMvfYE9F1CYVMA
AAGBtKaUsgAABAMARzBFAiABNygKB8S9DXEMn8ZX29GbRm2/FtlZttjUK52a3j1J
QAIhAOvD9OzQLtT8VcgKExFXZ41xEku7jZqVKQIgIhoq+xy6AHYAsMyD5aX5fWuv
fAnMKEkEhyrH6IsTLGNQt8b9JuFsbHcAAAGBtKaUrQAABAMARzBFAiEAmGGv5Kyb
cyftxolYuQpg7/QLbKrx0dxlm8rWhGQXr5QCIFDNHxS3FnYr8ntgtHu5hxbcgfYA
utZXFPLJqsgRFzX9MA0GCSqGSIb3DQEBCwUAA4IBAQBS5ns4Ma4/u5+nfCQjKZmB
wWQ396Y0vWO4DaKuICVf7Vm+mNexxo4IT1aJ/XebyjiJDtQvoEQa/h0w83p/PoQS
ms7JwRNMCMAjSECbt9mtYjZ7rFQt5zFfbz406PaZOWkRsyTTWdboImpKpb6wsqMC
MnijSywozFidfEbwlGc4zKSVEM63/kOMPpXQFwovvpO0IKwTLzIpznUqLKTzAwLj
T8gcakqLhwypN33GwpGTkFdgte+kwnQHNGFRdIkyoxJMc3g+MZmSR9VxzJqNFPc0
WWwodfKziK7Nr6vtFcH1KoghVI6mc40p2Hz6eFNehZVMdlHzQ3dmE1FphVTBJZA/
-----END CERTIFICATE-----`

const keyPem = `-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAz18HRKByhj4UJpSt8gdw3lYwp+q9lw8r/9GyaX/N0Hs9yOD2
LuVlAs/rWYX/7c9xVVuyJ7XMiBsRGQyhxUtnDuQ9+s1fD4sRQcu4tNoqMWonNipf
31Wexkz5l41zuaQ97EZsl+jze9K/E8AWQEPq8zOAY0eDwRFk5gUHv6ApZ+X4a9WI
/7y9xP21TuwhgUz/tAaBkHw3mOp09GCKWAcg0bu1VWC9UVci6u/8Kg6Yc7MP7mAy
MQmwgBTkhs26P+wNVen8N8tBt7NtmAPs8ZDcgYs5yU0qNnvIXrHWFFTaoLNDDAb8
3n1pmKIEvTfUSJhjfiFAmFMoH3h//8DKsx2ehwIDAQABAoIBAQCbADLUjwFLajdx
pwxlrj4lUlNID+Pqd4Kx0rICvW6/eH70GYKqBvj7oyINZmWoAbh6zvFZUUAyM9kR
2ilI6VV9Agl53+xAF/isL3Q+ahvDl877Y60JqFvnaT3ek/J+Pk+yvekJmyvzz9DX
GGsJWjrR0hsTGi9XLoM2k2f5SePpLdmF8W9BTDpX8jqLmFM2qbEHLgpxjEgE5RN5
219xgWpmaIlRTCFkpENgLPc9mKki0+1JO4OXM91yTps+iIfm2xio1P81qfDMrTjG
eTw2zESqGJNNBcBSKy6WCQTse0rGRlpIru6DMIQmYvh2UOxGq0srW+DuKE8Y9izf
A5s+Fi1hAoGBAOpeNhUVMEXzHlmQYLmgoz9OXI0ic9OT4PBNBxUksQYleWFJh13i
VBQa50pg5fHQZrggl/PDui9+E4D0vZbXV/YM4871mvXYxy8zt1hVBXcKRTsO8suF
ozvhIKhbzVCKSgBVBsRMccYBPX96qxecKHc8k2FakWBRn9bSFM4zFgKJAoGBAOKC
65bPvfsbJfLJXkUb884rEzVD+GQY3omqcFSTSpYsmrYo5eQAmWNt8MM0zdnS5r/Q
1HqTBitnw/cQEAq+vUDaZFFDGjgqZXfudAMetfWmHNadirlfWsFkWTwS8BV3flw2
VLABJP0ONGqJuxlNELTQwEfFasA4Qvf64hryw5SPAoGAeAANCPZ0ZRx6abl3DRSn
AX8J/GmrjKWnAuMtwTGsQk1lvN59JSEBk9nCm17J3eqq7ZQiuXl6F18idWDawiU4
sI8hBZLT6RU5x31fXycJSU5E7FxXDMY8MiivJAT9N0PXaBwg/tl1V8DC0Ebq24fl
YEfv3mUVEpA4mzGt3XZiyqECgYAH1rgIdBADDKF3kXhts9XwIJV9lmiWLjYlVXyl
sKM4JzsDas7p4Qtt4XQfjUhCbYYGxVdVFpl0Dxc9CZVEie1KvUQTe9sAbpcsW1gw
c3OgmKsO0kzmtWISp0JfTdh5JbV9w3OS07dP+pndxf5vlbcqSr8cvLxBArFum4QP
oq8aCQKBgH94w+qtgtGtV+JtSD54rsamOTd3IqgYL96syADu9jPLg5L+NSyTKKve
zEJtHXqNtQ6unOlXzIXa2m7X8NaxuReQIMDsrcX2u2oKq1picwGhqjYMsYpGyvtl
yivAFRa3rlpDQuumVTtfW832R563cT0CwtGW39Xp3jdSumq+tmkF
-----END RSA PRIVATE KEY-----`

async function main() {
  const client = new acme.Client({
    accountUrl,
    directoryUrl: acme.directory.letsencrypt.staging,
    accountKey: keyPem,
  })
  await client.revokeCertificate(certificate, {
    reason: 5, // cessationOfOperation; https://datatracker.ietf.org/doc/html/rfc5280#section-5.3.1
  })
}

main().catch((e) => {
  console.error('Fatal:', e)
  process.exit(1)
})

What am I doing wrong?

Ability to throw exceptions instead of waiting

It would be nice if there will be ability to receive an exception in methods like getCertificates if status != 'valid'

In a lot of cases, it is better to fail the script than to hang it up.

Is it okay if I'll send a pull request with the wait parameter, which will be true by default, but will add the ability to turn it to false?

Thanks

I wanted to let you know I have published a package using node-acme-client.

http-reverse-proxy-ts

It would have been much harder without your prior effort.

Thanks.

Error in Documentation and Seemingly Irregular Behaviour - Identifer and Challenge Types

Dear Node-Acme-Client,

While testing and using your tool, I came across some irregularities. The documentation and ACME Guide specifies several different responses to Challenge requests. This might be confusing. At one point requesting a challenge with order of type dns returns a http-01 challenge, while at another point requesting a challenge with type dns returns a dns-01 and http-01 challenge to choose from.

In reality it seems as if requesting a challenge using an order of type dns, currently returns a dns-01 challenge, which seems expected to occur. However, since it is not in accordance to the documentation this requires some fixes. Furthermore, in reality the resolving challenge might also be of type http-01, as I currently cannot explain, how some of my challenges were successful, although differentiating from other tests.

Perhaps the tool is not working as expected or someone was messing with live configuration at Lets Encrypt. Perhaps this could be verified again, as I am unsure of whether the tool is working as expected.

with kind regards,
Cob @ CobbleVision

Update: This behavior is reproducible for wildcard domains, which seem to be limited to dns challenges.

client.auto() doesn't work with ZeroSSL: One or more identifiers are duplicated

First of all I'd like to thank you for recently implementing externalAccountBinding! I was very excited to implement ZeroSSL as a backup to Let's Encrypt only to be a bit disappointed it didn't work with client.auto();

The error from ZeroSSL's ACME happens when ordering a certificate: One or more identifiers are duplicated

After some debugging it seems that forge.readCsrDomains will add the common name to the alt names even if you specify it to be empty as shown below.
const [key, csr] = await forge.createCsr({ commonName: 'example.com', altNames: [], });

This breaks ZeroSSL because the line below (auto.js line 64) results in an array with duplicate domain names.
const domains = [csrDomains.commonName].concat(csrDomains.altNames);

axios pinned to vulnerable version

=== npm audit security report ===                        
                                                                                
                                                                                
                                 Manual Review                                  
             Some vulnerabilities require your attention to resolve             
                                                                                
          Visit https://go.npm.me/audit-guide for additional guidance           
                                                                                
                                                                                
  High            Server-Side Request Forgery                                   
                                                                                
  Package         axios                                                         
                                                                                
  Patched in      >=0.21.1                                                      
                                                                                
  Dependency of   acme-client                                                   
                                                                                
  Path            acme-client > axios                                           
                                                                                
  More info       https://npmjs.com/advisories/1594

[ABANDONED] Add option to only renew certificate if it has expired

Issue has been abandoned - revised issue: #28

Dependent upon feature request #28

Please can you add another option to the client.auto() function called renewUponExpiry.

It will have the following properties:

  • boolean data type.
    • false: certificate will be renewed every time the client.auto() function is executed.
      • return value: default (certificate is returned).
    • true: certificate will only be renewed when it expires.
      • return value:
        • default (certificate is returned) if certificate needs renewing.
        • files in the directory referenced by the cache option if the certificate is still valid.
  • checks whether the certificate in the directory referenced by the cache option has expired.
  • prevents the rate limit from being reached from unnecessary repeated certificate requests.
  • default value: false.

Error: Error finalizing order :: signature algorithm not supported

Iam getting stuck while finalizing order. I would appreciate any help ^^

Heres the line which is throwing error (/acme-client/src/api.js:57):
throw new Error(util.formatResponseError(resp));
Error:
Error: Error finalizing order :: signature algorithm not supported

Heres how my CSR and orderObject looks like:

  • orderObject:
    {
    status: "pending",
    expires: "2022-05-02T11:55:07Z",
    identifiers: [{ type: "dns", value: "check.roconpaas.com" }],
    authorizations: [
    "https://acme-staging-v02.api.letsencrypt.org/acme/authz-v3/2279844994",
    ],
    finalize:
    "https://acme-staging-v02.api.letsencrypt.org/acme/finalize/49305998/2403126244",
    url: "https://acme-staging-v02.api.letsencrypt.org/acme/order/49305998/2403126244",
    }
  • CSR
    -----BEGIN CERTIFICATE REQUEST-----
    MIIClDCCAXwCAQAwHjEcMBoGA1UEAwwTY2hlY2sucm9jb25wYWFzLmNvbTCCASIw
    DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANYdOBZehNB+9xeW3YP8IYWwrLLv
    k0q7dCe1/dv1cahxXzVjRW1pO+Dx5c1T5blRJAyjgRYc2nEdBKTmTJDQ6L8aX/2t
    BdW/TvoZ3xb4YhW0nOkSUDCeRTC5wELW0M0VdI/ysMl6tiVoEXgmbIHmnTQg4ono
    BR8lG7eQTHOD2YUvU3CxW3pPfyvLTeQ6aX2bAGnM6c+yBrVua2OcNzca8fls9kK+
    f3W06KD4F47kRiVs3K93aB2RBvr6XbllCfl/4jUev/IVGGKNDprDIG1vQlzgDpJQ
    3dMRSjVjyKjOKAGd9ZlGPKu3vBU/WAnBqOnI8T3MTpd+DblB8mIfoCRE1CUCAwEA
    AaAxMC8GCSqGSIb3DQEJDjEiMCAwHgYDVR0RBBcwFYITY2hlY2sucm9jb25wYWFz
    LmNvbTANBgkqhkiG9w0BAQUFAAOCAQEAVROi/es1Bs8p9gvKG6qbihAiHS5P1fbC
    VGCZCu/GYmINNPRz4LdUQK2JvALqZfCGR8daqsEieNEir3464VDwARb7ifefoe9W
    iIeixRhOY++sHZtL9q7KpSEXFQnhobYWdZ849g6xk7ulfetDZs/grE5+86gfd1WP
    5mOpRDVSNOECrn4dEAgaWYsIbi7eWZZasrAt+OWSEPqcYf9ytNZ8MHv6MkiWyDIb
    GnwwryZ0EP1QL8GivcBSvjzcV84z6xgbDf8wtgKXB22Fepa1pwnvZIxLpgyBu/1C
    RRa0OTesR5KWcGdSCOjbbgjKRpMQOX5fyfV6UD1wDxceH9fkU5YeWg==
    -----END CERTIFICATE REQUEST-----

How to renew

Hi, great job 👍
How can I renew my certificates? Is calling auto method to issue and renew certificates the right way?
Should I store CSR? Do I need it to renew my certificates?

Parse error reading JWS at AcmeApi._callee$

using the auto() method, my challenge fails each time with this error...

Error: Parse error reading JWS
    at AcmeApi._callee$ (/node_modules/acme-client/lib/api.js:126:39)
    at tryCatch (/node_modules/regenerator-runtime/runtime.js:62:40)
    at Generator.invoke [as _invoke] (/node_modules/regenerator-runtime/runtime.js:296:22)
    at Generator.prototype.(anonymous function) [as next] (/node_modules/regenerator-runtime/runtime.js:114:21)
    at step (/node_modules/babel-runtime/helpers/asyncToGenerator.js:17:30)
    at /node_modules/babel-runtime/helpers/asyncToGenerator.js:28:13
    at <anonymous>

The issue appears to be in creating the challenge, rather than an issue with the challenge failing. I tried to delve deeper down the stack but found it difficult to follow through the 'regenerator-runtime' library

Thanks in advance!

Eliminate dependency on CLI OpenSSL

acme-client requires OpenSSL to be installed and available in $PATH.

https://github.com/digitalbazaar/forge works pretty great. I've used it to replace OpenSSL needs for cross platform nodejs apps which needed certificate management (key generation, CSR and signing).

It would be nice if this library wouldn't depend on CLI tools and it would make it more cross platform than it already is.

If you are open to changing to node forge, I can help with this.

preferredChain logic might not be sufficient to differentiate all possible chains

Just as a heads up. No immediate action should be required.

see: https://letsencrypt.org/2020/12/21/extending-android-compatibility.html

What about the alternate chain? Today, some ACME clients are able to instead request an alternate chain, if their user has configured it. We currently provide the option of getting the chain: Subscriber Certificate < – R3 < – ISRG Root X1 We will continue to offer this same chain as an alternate. However, note that most ACME clients don’t yet have a way to select this alternate chain (for example, Certbot selects chains by looking to see if they contain a given Issuer Name, but this chain doesn’t contain any Issuer Names which the high compatibility chain above doesn’t). We’ll be working with ACME client developers to create more flexible chain selection mechanisms going forward.

Misleading error when HTTPS requests return non-success statuses

When the directory URL returning a non-success status, the thrown error is rather misleading (it has nothing to do with dns resolution as one might be lead to think). For example:

# call client.createOrder()
acme-client HTTP request: get https://acme-v02.api.letsencrypt.org/directory +0ms
acme-client RESP 501 get https://acme-v02.api.letsencrypt.org/directory +12ms
# throws Error: Could not resolve URL for API resource: "newOrder"

That 501 here is caused by a axios bug (fixed in axios/axios@e426910) related to https proxy handling. I'd suggest to include the URL and statuscode in the thrown error message in such cases (e.g. when statuscode is not between 200 and 299).

unable to load X509 request

Trying to use this package as illustrated in this example
https://github.com/publishlab/node-acme-client/blob/master/examples/auto.js

But seem to keep hitting this issue. Can't figure it out,

Error: unable to load X509 request 140026476918424:error:0906D06C:PEM routines:PEM_read_bio:no start line:pem_lib.c:701:Expecting: CERTIFICATE REQUEST at ChildProcess.<anonymous> (/node_modules/openssl-wrapper/lib/index.js:91:13) at Object.apply (/node_modules/harmony-reflect/reflect.js:2064:37) at ChildProcess.emit (events.js:180:13)

Running on an AWS EC2 Ubuntu, Can post the code if required, but essentially it's just a copy of the linked example

DNS rebinding

This is great package.

There is an issue involving DNS rebinding.

Some routers will not allow a client on the local network to access a server on the local network via an externally managed domain name/IP-address. There is a hack called DNS rebinding that they are trying to thwart.

With the http-01 challenge, the verifyChallenge method in the package attempts to access the site and verify the authorization token and key before moving on to requesting the certificate from LetsEncrypt.

This works fine if the router does not block the request, and in fact some routers are spotty on enforcing this so sometimes it gets through.

All that said, it might make sense to note this in the examples/documentation for users of the package that they can skip this step.

There might also be a need to add a noVerify option to the auto process interface.

Thanks for producing such a great piece of code.

client.auto creates a new account for every certificate request? Even when the client has the same account key?

My apologies if this is a stupid question. But i am trying to register SSL certificates with LE using your library, and using the client.auto() function.

Each time i create the client i am using the same accountKey (one i generated and saved to my local file system) but i hit the rate limit for new accounts, I was assuming that if i create the client with the same account key it would keep ordering certificates using the same account is this not correct?


var accountKey = 'NEVERCHANGINGACCOUNTKEY';


//Create a client
        client = new acme.Client({
            directoryUrl: directoryURL,
            accountKey: accountKey,
        });

//Create a CSR
        acme.openssl.createCsr({
            // commonName: '*.' + domain,
            commonName: domain,
            // altNames: ['www.' +domain],
        }).then(function(csr) {
            return next(null, accountKey, csr);
        }, function(err) {
            return next(err);
        });

client.auto({
            csr: certCsr,
            email: '[email protected]',
            termsOfServiceAgreed: true,
            challengeCreateFn: createChallenge,
            challengeRemoveFn: removeChallenge,
        }).then(challengeComplete, challengeFailed);

This is the error when the challenge is created

Error: Error creating new account :: too many registrations for this IP: see https://letsencrypt.org/docs/rate-limits/
    at AcmeApi._callee$ (/fluro/server/node_modules/acme-client/lib/api.js:126:39)

Timeout in verifyChallenge

Hey guys, first of all thanks a lot for building such a useful library and for managing it so actively. I am using this package for a project where I have built an integration to generate SSL certificates (it is a kind of hosting service).

I have observed an issue that verifyChallenge don't really timeout in cases where DNS challenge has not been fulfilled.

So for example, I create an order for example.com and decided to go for DNS challenge but submitted a request to verifyChallange without actually creating a TXT record. In this case, verifyChallange function waits for a lot - like several minutes before generating an error/response.

Bad base64 decode

I’m having a little trouble using the library. I hope it’s okay to ask for help here.

I’m trying to invoke createOrder(), but it fails with the following stack trace:

Error: error:09091064:PEM routines:PEM_read_bio_ex:bad base64 decode
    at Sign.sign (internal/crypto/sig.js:84:29)
    at HttpClient.createSignedBody (C:\\Rest\node_modules\acme-client\src\http.js:195:50)
    at HttpClient.signedRequest (C:\\Rest\node_modules\acme-client\src\http.js:220:33)
    at processTicksAndRejections (internal/process/task_queues.js:93:5)
    at async AcmeApi.apiRequest (C:\\Rest\node_modules\acme-client\src\api.js:51:22)
    at async AcmeClient.createOrder (C:\\Rest\node_modules\acme-client\src\client.js:202:22)
    at async issueRoute53 (webpack:///./src/Modules/Issue.ts?:37:19)
    at async catchPromise (webpack:///./src/CatchPromise.ts?:7:22)

The error message suggests that the issue lies with the accountKey. The PEM string may not be in the correct format, although I have corrected some errors in the format, and am not sure whether there are any more.

In an abstract form, my code looks something like this:

'use strict';

const Acme = require('acme-client');

const PEM_LINE_LENGTH = 64;

run({domain: 'example.com', email: '[email protected]'});

async function run({domain, email})
{
    const { accountKey, accountUrl } = await createAccount({email});
    await createOrder({accountKey, accountUrl, domain});
};

async function createAccount({email})
{
    const acme = new Acme.Client
    (
        {
            directoryUrl: Acme.directory.letsencrypt.staging,
            accountKey: (await Acme.forge.createCsr({}))[0]
        }
    );
    const account = await acme.createAccount
    (
        {
            contact: [ `mailto:${email}` ],
            termsOfServiceAgreed: true
        }
    );
    return { accountKey: account.key.n, accountUrl: acme.getAccountUrl() };
};

async function createOrder({accountKey, accountUrl, domain})
{
    const accountKeyPem = generateAccountKeyPem({accountKey});
    const acme = new Acme.Client
    (
        {
            directoryUrl: Acme.directory.letsencrypt.staging,
            accountKey: accountKeyPem,
            accountUrl
        }
    );
    const order = await acme.createOrder
    (
        {
            identifiers:
            [
                {
                    type: 'dns',
                    value: domain
                }
            ]
        }
    );
    console.log(order);
};

function generateAccountKeyPem({accountKey})
{
    let pem = '';
    pem += '-----BEGIN RSA PRIVATE KEY-----\n';
    for (let index = 0; index < accountKey.length - 1; index += PEM_LINE_LENGTH)
    {
        pem += accountKey.slice(index, index + PEM_LINE_LENGTH) + '\n';
    };
    pem += '-----END RSA PRIVATE KEY-----\n';
    return pem;
};

It would be much appreciated if anyone is able to provide assistance.

Consider replacing Axios

When the XMLHttpRequestglobal is defined, axios uses it over node's API which presents an issue in environments like jest which by default sets this global as part of its jsdom environment and which in turn triggers a Error: Headers User-Agent forbidden coming from this module because it attempts to set the User-Agent which is forbidden in browsers.

jest can be configured to not use jsdom but I feel that using node-fetch or a wrapper over Axios would be a better choice for a http client becuse this module should never run in the browser so does not need that part of Axios.

verifyChallenge should ignore bad certificates when following redirects

From https://letsencrypt.org/docs/challenge-types/:

Our implementation of the HTTP-01 challenge follows redirects, up to 10 redirects deep. It only accepts redirects to “http:” or “https:”, and only to ports 80 or 443. It does not accept redirects to IP addresses. When redirected to an HTTPS URL, it does not validate certificates (since this challenge is intended to bootstrap valid certificates, it may encounter self-signed or expired certificates along the way).

I haven't tested this, but I think the code to change would be

const resp = await axios.get(challengeUrl);

Current code:
const resp = await axios.get(challengeUrl);

Modified code:
const resp = await axios.get(challengeUrl, { httpsAgent: new https.Agent({rejectUnauthorized: false} });

Issue with "too many currently pending authorizations"

I'm using this module to do ACMEv2 challenges for a lot of domains and sometimes I run into this error:

Error: Error creating new order :: too many currently pending authorizations: see https://letsencrypt.org/docs/rate-limits/
    at AcmeApi.apiRequest (node_modules/acme-client/src/api.js:54:19)
    at runMicrotasks (<anonymous>)
    at async AcmeClient.createOrder (node_modules/acme-client/src/client.js:281:22)

My code is pretty much exactly following the example from https://github.com/publishlab/node-acme-client/blob/master/examples/api.js.

What I gather, this is a client-caused error with too many open (e.g. unfulfilled) authorization requests. My requests fail for various reasons that are not in my control and I wonder if this module could either automatically cancel/destroy failed authorization request or expose a method to do so (that could be called in case of a challenge error).

From https://community.letsencrypt.org/t/error-429-too-many-pending-authorizations/27273/7:

if you get an authz for one requested domain but fail to get it for another, make sure you proactively destroy the first authz before giving up

According to https://community.letsencrypt.org/t/too-many-currently-pending-authorizations/64571 it sounds like the issue should be "mitigated" by using ACME v2, but I certainly still see it.

Optional dependecies

I am proposing to change "hard" packages dependency:

  • bluebird
  • node-forge
  • openssl-wrapper

towards to npm "optionalDependencies" where implementator decide about the need to install them.
Best regards.

[NOTICE] node-acme-client < v4.2.4 will break on September 15, 2022

At Let's Encrypt, we're planning to stop supporting SHA-1 self-signatures on CSRs (in part because Go is removing support for those). In a review of our logs, node-acme-client showed up as a popular client that was sending CSRs with SHA-1 self-signatures. It looks like the problem is here:

/* Sign CSR */
csr.sign(privateKey);

You don't set a specific signature algorithm, so node-forge defaults to SHA-1:

https://github.com/digitalbazaar/forge/blob/cbf0bd590d47fe3120a57e7c36f2f4e64381ad81/lib/x509.js#L1128-L1130

I recommend adding a second argument containing the appropriate OID for sha256WithRSAEncryption.

Add option to check validity of certificate

Issue has been abandoned - revised issue: #30

Abandoned due to flaw: If the old certificate was retrieved, it would not match the newly generated key and would result in an error

Please can you add another option to the client.auto() function called renewIfExpired.

It will have the following properties:

  • string data type.
    • contains the directory path of a certificate.
  • return value:
    • if the certificate in the specified directory has expired, a new certificate is returned.
    • if the certificate in the specified directory is still valid, it is returned.
  • default value: null (new certificate is always generated).
  • prevents the rate limit from being reached from unnecessary repeated certificate requests.
  • allows the user to use their own certificate and have node-acme-client as a fallback to generate a certificate in case their certificate expires.

Possible Implementation

if (renewIfExpired) {
    if (fs.existsSync(renewIfExpired)) {
        var cert = fs.readFileSync(renewIfExpired);
        var expiry = (new Date((await ACME.forge.readCertificateInfo(cert)).notAfter)).valueOf();
        var time = (new Date()).valueOf();

        if (expiry >= time) {
            return cert;
        }
    } else {
        // ERROR: file does not exist
    }
}

// Continue certificate generation

By the way, thank you so much for such a great package!
I really appreciate the sophisticated code, clear documentation and constant bug fixes (all of the other major ACME clients are either really complicated or have serious bugs).

Builds are broken

Possibly related:

Let's Encrypt:

Nonces are not shared between instances of Boulder, they are only valid for the Boulder instance that issued them to begin with. So if two curl requests are load balanced to different Boulder instances, a seemingly valid nonce would fail.

TravisCI:

However, with our current NAT each TCP/UDP connection may be established through a different NAT host than any previous connections.

Support for ES256 account keys

Quick question, do you have any plans or bandwidth to add support for ES256 or EdDSA account keys? (the RFC 8555 spec says ES256 is now mandatory for servers)

BTWs thanks for building a low level client, literally every other client only has an auto function, which breaks on multi-domain dns cert orders.

Renew Certificates

Hey guys, need to know that how we can renew certificates with this package?

Authorization not found in DNS TXT records

"Error: Authorization not found in DNS TXT records for test.managed-test.de"

When I do a dig _acme-challenge.test.managed-test.de @mydnsserver TXT I get the correct TXT record.
I've read the documentation about debugging, but since I'm doing this on a node-red installation I'm totally not sure what I'm supposed to do where. Any help very much appreciated.

The value "jwk" is invalid for option "format"

I am trying to create acme account based on the examples provided, but I end up receiving this error. Any idea on how to solve it ?

TypeError [ERR_INVALID_OPT_VALUE]: The value "jwk" is invalid for option "format"
    at parseKeyFormat (internal/crypto/keys.js:147:9)
    at parseKeyFormatAndType (internal/crypto/keys.js:182:18)
    at parseKeyEncoding (internal/crypto/keys.js:208:7)
    at parsePublicKeyEncoding (internal/crypto/keys.js:244:10)
    at PublicKeyObject.export (internal/crypto/keys.js:116:11)
    at getJwk (/Users/adithya/Documents/adithya/acme-ssl/node_modules/acme-client/src/crypto/index.js:157:54)
    at HttpClient.getJwk (/Users/adithya/Documents/adithya/acme-ssl/node_modules/acme-client/src/http.js:97:24)
    at HttpClient.createSignedBody (/Users/adithya/Documents/adithya/acme-ssl/node_modules/acme-client/src/http.js:234:26)
    at HttpClient.signedRequest (/Users/adithya/Documents/adithya/acme-ssl/node_modules/acme-client/src/http.js:299:27)
    at processTicksAndRejections (internal/process/task_queues.js:93:5) {
  code: 'ERR_INVALID_OPT_VALUE'
}

Here is the code.

const acme = require('acme-client');
const fs = require('fs');
const util = require('util');
fs.readFileAsync = util.promisify(fs.readFile);
fs.writeFileAsync = util.promisify(fs.writeFile);

let privateKey;
let client;
let createNewAccount = false;

fs.readFileAsync('./key')
.then(content => {
    if(content.length){
       privateKey = content;
       createNewAccount = false;
       return 
    }else{
        return acme.crypto.createPrivateKey()
    }
})
.then(content => {
    if(content){
        privateKey = content.toString();
        fs.writeFileAsync('./key', privateKey);
        return privateKey;
    }else{
        return content
    }
})
.then(() => {
    client = new acme.Client({
        directoryUrl: acme.directory.letsencrypt.staging,
        accountKey: privateKey
    });
})
.then(() => {
    return client.createAccount({
        termsOfServiceAgreed: true
    });
})
.catch(e => {
    console.log(e)

})

Axios bump to >=0.21.2

Hi there,

is it possible to bump axios again? The required security fix is trivial, see: axios/axios#3980
but it is hard for me to know, whether the accompanying changes would break acme-client...

Or is it not necessary, because in acme-client's usage of axios the vulnerability will never be executed anyway (which is, what I think...)

Thanks for any thoughts!

Michael.

Only get certain challenge types issued

Could you implement an option, where you can specify which type of challenges you want to get with your order? (I know this is already kind of implemented in automode with opts.challengePriority)

This would also avoid this error:
Unable to produce key authorization, unknown challenge type: tls-alpn-01

Which certificate is which using auto?

Heya, excuse this basic question I'm fairly new to implementing SSL like this.

I've managed to get everything working with Route53 on AWS.

When I do

 const certificate = await client.auto({
 ...

Then log out the certificate you get 3 back, which I guess are cert, key, privateKey?

Which ones which please?

Question: Is it possible to change challenges?

Hi 👋

I think everything with this module works well.

One of my hosts cannot set the DNS programmatically but when I use this module it requires HTTP and DNS challenge. Is it configurable?

I'm calling auto like this:

client.auto({
    csr: res.readCertCsr, // cert csr buffer
    email: csrOpts.emailAddress,
    termsOfServiceAgreed: true,
    challengeCreateFn
    challengeRemoveFn
  })
  .then(acmeAutoSuccess)
  .catch(acmeAutoFailure)

I tried using challengePriority: ["http-01"] as an option but it wont limit to HTTP.

Maybe you know better than I that both are required and I just have to deal with it.

Add function to check validity of certificate

Issue has been abandoned

Not a core part of the ACME protocol: Unnecessary to have it in an ACME client

Please can a new function be implemented called ACME.verify().

It will have the following properties:

  • arguments:
    • certificate (string):
      • contains the directory path of a certificate.
  • return value:
    • if the specified certificate is still valid, true is returned.
    • if the specified certificate has expired, false is returned.
  • default arguments:
    • certificate:
      • '' (throws an error: no certificate specified).
  • prevents the rate limit from being reached from unnecessary repeated certificate requests.

Possible Implementation

const fs = require('fs');
const forge = require('./crypto/forge');

module.exports = async function(certificate) {
    if (certificate) {
        if (fs.existsSync(certificate)) {
            let cert = fs.readFileSync(certificate);
            let expiry = (new Date((await forge.readCertificateInfo(cert)).notAfter)).valueOf();
            let time = (new Date()).valueOf();

            if (expiry > time) {
                return true;
            } else {
                return false;
            }
        } else {
            throw new Error('certificate does not exist');
        }
    } else {
        throw new Error('no certificate specified');
    }
}

Possible Usage

const fs = require('fs');
const acme = require('acme-client');

if (acme.verify('path/to/cert.pem')) {
    var key = fs.readFileSync('path/to/key.pem');
    var cert = fs.readFileSync('path/to/cert.pem');
} else {
    // generate key and certificate using acme-client
    fs.writeFileSync('path/to/key.pem', key);
    fs.writeFileSync('path/to/cert.pem', cert);
}

// use key and certificate

By the way, thank you so much for such a great package!
I really appreciate the sophisticated code, clear documentation and constant bug fixes (all of the other major ACME clients are either really complicated or have serious bugs).

Changelog

Thanks for the library.

I've been working on acme-middleware as a middleware for expressjs.
Today, I came back to node-acme-client and found out it is now updated to version 4.0.0.

Do you maintain a changelog somewhere?

That'd help to know if I have to update to the latest version, and what need to be updated.

Thank you!

Cannot catch errors with OpenSSL CLI spawn

When the OpenSSL CLI is unavailable (or presumably otherwise has a problem), the resulting error cannot be caught promise-style.

const acme = require('acme-client');
acme.openssl.createPrivateKey().then(() => {
  
}).catch((e) => {
  console.error('CAUGHT', e);
});

The resulting output:

events.js:167
throw er; // Unhandled 'error' event
^

Error: spawn openssl ENOENT
at Process.ChildProcess._handle.onexit (internal/child_process.js:229:19)
at onErrorNT (internal/child_process.js:406:16)
at process._tickCallback (internal/process/next_tick.js:63:19)
at Function.Module.runMain (internal/modules/cjs/loader.js:746:11)
at startup (internal/bootstrap/node.js:240:19)
at bootstrapNodeJSCore (internal/bootstrap/node.js:564:3)
Emitted 'error' event at:
at Process.ChildProcess._handle.onexit (internal/child_process.js:235:12)
at onErrorNT (internal/child_process.js:406:16)
[... lines matching original stack trace ...]
at bootstrapNodeJSCore (internal/bootstrap/node.js:564:3)

Hopefully, this can be a non-issue once #3 is wrapped up. Thank you for your efforts!

You can add an example for re use the client in api mode?

I use

const client = new acme.Client({
    directoryUrl: acme.directory.letsencrypt.staging,
    accountKey: greenlockacc.acckey,//key pem encoded.
    accountUrl: greenlockacc.client.api.accountUrl//string
});

But i recive

(node:73378) UnhandledPromiseRejectionWarning: TypeError [ERR_INVALID_ARG_TYPE]: The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type object
    at Function.from (buffer.js:293:9)
    at new AcmeClient (/Volumes/Container disk2/nodejsmain/node_modules/acme-client/src/client.js:47:38)
    at next (/Volumes/Container disk2/nodejsmain/resources/module/workercontroll/workercontroll.js:157:36)
    at processTicksAndRejections (internal/process/task_queues.js:93:5)
(node:73378) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:73378) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

No idea what I'm doing wrong..
I have already checkt the typs of the values. there acre correct.

Possible error in example/api.js

I believe the code at line 87 of example/api.js is incorrect.

The current line is:

        const challenges = { authz };

I suspect the line should read:

        const { challenges } = authz;

problem with the auto.js execution

Hey there, I'm trying to use auto.js, essentially as is, to test getting a cert but I keep running into an exception.

Would write "pCWJ_5UHalASCBfjQk5HlQhuqbRsK61UTn8McdY3dIo.hOYObyEDbWAyjJ5yIJ0h2Kf2GjEb-SegaCGzhrtyHA4" to path "/Users/max/src/github.com/acme-www/.well-known/acme-challenge/pCWJ_5UHalASCBfjQk5HlQhuqbRsK61UTn8McdY3dIo"
(node:39313) UnhandledPromiseRejectionWarning: TypeError [ERR_INVALID_CALLBACK]: Callback must be a function. Received undefined
    at maybeCallback (fs.js:142:9)
    at Object.writeFile (fs.js:1175:14)
    at Object.challengeCreateFn (/Users/max/src/github.com/smallstep/node-acme-test/test.js:35:25)
    at _callee3$ (/Users/max/src/github.com/smallstep/node-acme-test/node_modules/acme-client/lib/auto.js:211:61)
From previous event:
    at _callee4$ (/Users/max/src/github.com/smallstep/node-acme-test/node_modules/acme-client/lib/auto.js:267:40)
    at tryCatch (/Users/max/src/github.com/smallstep/node-acme-test/node_modules/regenerator-runtime/runtime.js:62:40)
    at Generator.invoke [as _invoke] (/Users/max/src/github.com/smallstep/node-acme-test/node_modules/regenerator-runtime/runtime.js:296:22)
    at Generator.prototype.<computed> [as next] (/Users/max/src/github.com/smallstep/node-acme-test/node_modules/regenerator-runtime/runtime.js:114:21)
    at step (/Users/max/src/github.com/smallstep/node-acme-test/node_modules/babel-runtime/helpers/asyncToGenerator.js:17:30)
    at /Users/max/src/github.com/smallstep/node-acme-test/node_modules/babel-runtime/helpers/asyncToGenerator.js:28:13
(node:39313) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)
(node:39313) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.```

from the line that is writing the http challenge file. It's been a minute since I've used node.js, so I'm sure it's an issue on my end. All I did was uncomment the line of code that was doing the `await fs.writeFile(filePath, fileContents);`. I also tried `return ...` at the head of that line and still the same error.

Appreciate the time/help!

During http-01 verification, extra line break is there in response

Thanks for the amazing library!!
In http-01 validation, when debugging response from http:///.well-known/acme-challenge/ , it contains an extra line break \n with content hence resulting the error Authorization not found in HTTP response from....

Also, I've checked my keyAuth content again and again and it doesn't have any linebreaks. Shouldn't it be handled there in the verify.js?

Error: Parse error reading JWS at AcmeApi._callee

using the auto() method, my challenge fails each time with this error...

Error: Parse error reading JWS
    at AcmeApi._callee$ (/node_modules/acme-client/lib/api.js:126:39)
    at tryCatch (/node_modules/regenerator-runtime/runtime.js:62:40)
    at Generator.invoke [as _invoke] (/node_modules/regenerator-runtime/runtime.js:296:22)
    at Generator.prototype.(anonymous function) [as next] (/node_modules/regenerator-runtime/runtime.js:114:21)
    at step (/node_modules/babel-runtime/helpers/asyncToGenerator.js:17:30)
    at /node_modules/babel-runtime/helpers/asyncToGenerator.js:28:13
    at <anonymous>

The issue appears to be in creating the challenge, rather than an issue with the challenge failing. I tried to delve deeper down the stack but found it difficult to follow through the 'regenerator-runtime' library

Thanks in advance!

DNS-01 verifyChallenge fails with "Error: Unable to verify ACME challenge, URL not found"

After successfully creating account, order, CSR and a DNS TXT record for my domain, I'm trying to verify the internal challenge, but I fail with
"Error: Unable to verify ACME challenge, URL not found"

These are the data objects I can access at this point:

order: object
  status: "pending"
  expires: "2021-05-25T12:29:19Z"
  identifiers: array[1]
    0: object
      type: "dns"
      value: "test.managed-test.de"
  authorizations: array[1]
    0: "https://acme-staging-v02.api.letsencrypt.org/acme/authz-v3/---"
  finalize: "https://acme-staging-v02.api.letsencrypt.org/acme/finalize/---/---"
  url: "https://acme-staging-v02.api.letsencrypt.org/acme/order/---/---"


authorizations: array[1]
  0: object
    identifier: object
      type: "dns"
      value: "test.managed-test.de"
    status: "pending"
    expires: "2021-05-25T12:29:19Z"
    challenges: array[1]
      0: object
        type: "dns-01"
        status: "pending"
        url: "https://acme-staging-v02.api.letsencrypt.org/acme/chall-v3/---"
        token: "---"
    url: "https://acme-staging-v02.api.letsencrypt.org/acme/authz-v3/---"

But both

const verify = await client.verifyChallenge(order.authorizations[0], authorizations[0].challenges[0]);

and

const verify = await client.verifyChallenge( authorizations[0], authorizations[0].challenges[0]);

give the aforementioned error.
Which bothers me in itself - what URL, aren't we doing dns here? I know I at least do...
So anybody have any enlightening words for me?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.