"authorization must be pending" and "no such challenge"

My domain is: oscarkilo.com
I ran this command: HTTP requests to staging using a new golang client
It produced this output: see below
My web server is: a custom golang binary
The operating system my web server runs on is: Debian
My hosting provider, if applicable, is: none
I can login to a root shell on my machine: yes
I'm using a control panel to manage my site: no
The version of my client is: custom Go code

Greetings. I'm trying to get a new certificate by implementing my own client in Go using the HTTP API with the http-01 challenge. I can create an order:
{
"identifier": {
"type": "dns",
"value": "oscarkilo.com"
},
"status": "pending",
"expires": "2022-11-13T16:20:24Z",
"challenges": [
{
"type": "http-01",
"status": "pending",
"url": "https://acme-staging-v02.api.letsencrypt.org/acme/chall-v3/4229901414/5jlvXg",
"token": "hjIpkaeS5LkhnkynrXIHUrQ6nkCDVgSzgVi1ZlHSfNk"
},
{
"type": "dns-01",
"status": "pending",
"url": "https://acme-staging-v02.api.letsencrypt.org/acme/chall-v3/4229901414/aUEQSw",
"token": "hjIpkaeS5LkhnkynrXIHUrQ6nkCDVgSzgVi1ZlHSfNk"
},
{
"type": "tls-alpn-01",
"status": "pending",
"url": "https://acme-staging-v02.api.letsencrypt.org/acme/chall-v3/4229901414/XK3BWA",
"token": "hjIpkaeS5LkhnkynrXIHUrQ6nkCDVgSzgVi1ZlHSfNk"
}
]
}
And I can request an http-01 challenge:
{
"type": "http-01",
"status": "pending",
"url": "https://acme-staging-v02.api.letsencrypt.org/acme/chall-v3/4229901414/5jlvXg",
"token": "hjIpkaeS5LkhnkynrXIHUrQ6nkCDVgSzgVi1ZlHSfNk"
}
But I don't see the incoming /.well-known/acme-challenge/... request, and when I check back on the status of the challenge 2 seconds later, I get this:
{
"type": "urn:ietf:params:acme:error:malformed",
"detail": "Unable to update challenge :: authorization must be pending",
"status": 400
}
and the order page shows this:
{
"type": "urn:ietf:params:acme:error:malformed",
"detail": "No such challenge",
"status": 404
}

Is there a way for me to see the URL of the request that LetsEncrypt sent to me in order to verify the challenge? It should be something like this:
http://oscarkilo.com/.well-known/acme-challenge/10RgoQjyBzvBU0M-XsO8PS9eQzmdxQS2VnyIK4Ae7U8
When I open this URL in my browser, my server gets the request and responds correctly. But I don't see this request from LetsEncrypt before the challenge fails and disappears.

I searched for other similar issues, and it seems that people are getting this error with the dns-01 challenge, which is not what I'm doing. Other people had trouble with expired authz, but I'm making a new account and a new order each time, so that should not be a problem.

Also, what is the right way to interpret the message "authorization must be pending"? Does it mean "authorization is probably pending" or "authorization is not pending, but should be"? I'm assuming it's the latter because the /authz-v3 page returns "no such challenge".

Most of the source code is here: package certrefresh// LetsEncrypt.org Clientimport ( "bytes" "cryp - Pastebin.com
I call NewAccount() followed by NewOrder().
Then 2 seconds later, CheckChallengeStatus() fails.

Thank you very much.

1 Like

Did you actually trigger the challenge verification?

3 Likes

I believe so. Line 393 sends a POST to https://acme-staging-v02.api.letsencrypt.org/acme/chall-v3/4229901414/5jlvXg
The response is:
{
"type": "http-01",
"status": "pending",
"url": "https://acme-staging-v02.api.letsencrypt.org/acme/chall-v3/4229901414/5jlvXg",
"token": "hjIpkaeS5LkhnkynrXIHUrQ6nkCDVgSzgVi1ZlHSfNk"
}
Is that enough to trigger it?

1 Like

It's probably something stupid I'm doing. That's why it would really help to get any error message from LetsEncrypt about this challenge.

1 Like

@igor-nav I moved your topic to the Client Dev section. This may draw interest from a broader group of development experts. Griffin certainly has expertise as he developed an ACME client. Hopefully he or another volunteer will see something.

5 Likes

Please provide more debugging information such as the actual contents of POST data being send (preferably the entire thing from start to end) and the responses instead of mentioning a certain line in the code and us requiring to decipher the source code. You probably get more responses that way.

If your client cannot produce such debugging info, now is probably a good start to implement it.

5 Likes

First, new-order request.

2022/11/06 10:39:33 HTTP POST https://acme-staging-v02.api.letsencrypt.org/acme/new-order
2022/11/06 10:39:33 --- request:
{"payload":"eyJpZGVudGlmaWVycyI6W3sidHlwZSI6ImRucyIsInZhbHVlIjoib3NjYXJraWxvLmNvbSJ9XX0","protected":"eyJhbGciOiJSUzI1NiIsIm5vbmNlIjoiQjM3Q2pjanFzQUx6ZkF6dnowRk92M0pPQ3NMd0lkbC1tMDUyMm1Fd2huWTE1ZHMiLCJ1cmwiOiJodHRwczovL2FjbWUtc3RhZ2luZy12MDIuYXBpLmxldHNlbmNyeXB0Lm9yZy9hY21lL25ldy1vcmRlciIsImtpZCI6Imh0dHBzOi8vYWNtZS1zdGFnaW5nLXYwMi5hcGkubGV0c2VuY3J5cHQub3JnL2FjbWUvYWNjdC83NTEzOTAzNCJ9","signature":"BEzKONqAaOzPIcR89IQHpxsk-Q9Q6hDvYO8WhIvReh259dO1Vkdt_GQow3h4VFk7vDTvN1NCI8vuyMByV59uq9uhZm3lMO6X8c4Te32oRtVT1jHYflWARFoQ_AY_VR8c2ub4-zeVuLLz9ChN51jFLOCvTRH0TBbaXGa8Z9YeU75t-JlCj7Ap8DtijncUmiZsryEL2-DGvY7bzW7ZP7qyhAhJD49FhDfcFsTueYcgO5IiwRuEjjQDEObMD8HIVrG_uxlG6LfXvyguS9hY4y30Zj3t192mZIkPii7qpS2i1WssNabq95L301GBTQIOOcIvymEBs8Fo644vd1Q1k1eMuw"}
2022/11/06 10:39:33 --- response:
{
  "status": "pending",
  "expires": "2022-11-13T18:39:33Z",
  "identifiers": [
    {
      "type": "dns",
      "value": "oscarkilo.com"
    }
  ],
  "authorizations": [
    "https://acme-staging-v02.api.letsencrypt.org/acme/authz-v3/4230895534"
  ],
  "finalize": "https://acme-staging-v02.api.letsencrypt.org/acme/finalize/75139034/5033877174"
}

Then, the new challenge:

2022/11/06 10:39:33 HTTP POST https://acme-staging-v02.api.letsencrypt.org/acme/authz-v3/4230895534
2022/11/06 10:39:33 --- request:
{"payload":"","protected":"eyJhbGciOiJSUzI1NiIsIm5vbmNlIjoiOEYwNWxpbFk1OEJmaFZVR1JkR3ZUQTRMTnl1OWwyTUxsc1BObC1ET2IwaHpwSmsiLCJ1cmwiOiJodHRwczovL2FjbWUtc3RhZ2luZy12MDIuYXBpLmxldHNlbmNyeXB0Lm9yZy9hY21lL2F1dGh6LXYzLzQyMzA4OTU1MzQiLCJraWQiOiJodHRwczovL2FjbWUtc3RhZ2luZy12MDIuYXBpLmxldHNlbmNyeXB0Lm9yZy9hY21lL2FjY3QvNzUxMzkwMzQifQ","signature":"AIumWYKLql5kwysHmnsAAjwUDV5I_yGs__QngKeIivjgn5Qx7VeVPZ0_AjTMtpokJq7j_lyAkvLVijR8pFJV7EdHf0bhdtZs1LlDw7RxA9x0LYoDQOZVWaVuCI0803LIyl9tZGj4R1um9vUhuoTdUj4y9ecOnRCzV7MqPQavBre7i17NhOO5tk_wQUewuDw-pGZ_DztxWrwh6lGCX4XekI4F-b694SfoJqlVhODvsiL8Y4Io_6ukt6qpasOyoG2HcyoFlNSywtvdD28YROt59Z_yXjsKtQrskWCekBOZHW1DdWL7fTgixZP7o7BWKC4yWwz8DKF-YPCT0zk_kj1Bvw"}
2022/11/06 10:39:33 --- response:
{
  "identifier": {
    "type": "dns",
    "value": "oscarkilo.com"
  },
  "status": "pending",
  "expires": "2022-11-13T18:39:33Z",
  "challenges": [
    {
      "type": "http-01",
      "status": "pending",
      "url": "https://acme-staging-v02.api.letsencrypt.org/acme/chall-v3/4230895534/n1apyQ",
      "token": "jbzeJawKuXVhRleEn9U1swg2iqNFKG7d4DiOVDTgoOM"
    },
    {
      "type": "dns-01",
      "status": "pending",
      "url": "https://acme-staging-v02.api.letsencrypt.org/acme/chall-v3/4230895534/lbGzFA",
      "token": "jbzeJawKuXVhRleEn9U1swg2iqNFKG7d4DiOVDTgoOM"
    },
    {
      "type": "tls-alpn-01",
      "status": "pending",
      "url": "https://acme-staging-v02.api.letsencrypt.org/acme/chall-v3/4230895534/QCZxwQ",
      "token": "jbzeJawKuXVhRleEn9U1swg2iqNFKG7d4DiOVDTgoOM"
    }
  ]
}

Then, triggering the http-01 challenge:

2022/11/06 10:39:33 HTTP POST https://acme-staging-v02.api.letsencrypt.org/acme/chall-v3/4230895534/n1apyQ
2022/11/06 10:39:33 --- request:
{"payload":"e30","protected":"eyJhbGciOiJSUzI1NiIsIm5vbmNlIjoiQjM3QzNTQlp5YUlDNDgwbS1UejJTTFZ2UF9MZzBlMlZ2ZUp2RmRVN0g4RlNFVXMiLCJ1cmwiOiJodHRwczovL2FjbWUtc3RhZ2luZy12MDIuYXBpLmxldHNlbmNyeXB0Lm9yZy9hY21lL2NoYWxsLXYzLzQyMzA4OTU1MzQvbjFhcHlRIiwia2lkIjoiaHR0cHM6Ly9hY21lLXN0YWdpbmctdjAyLmFwaS5sZXRzZW5jcnlwdC5vcmcvYWNtZS9hY2N0Lzc1MTM5MDM0In0","signature":"Mi3J3z8CrE94cZwHKJqoCeZMxC9er1vUnDGm0ufZxUc6Jykf6acgSnG2-hkm5HoQ2v1NUytRJRU-XiHiyHsqY-yo8iTbS_KhTdnMDnJuy330Mk9M3T_vG7BeheBciIc9TJXd5uuGRPzjYQD3EpFhCRH_q3iva-6IWHvDFbAyiW5bAJuG3BXg3trZL8fa-ACtMnjxJtgdqwDy2OWovxEZdKONTzW8RGUGWNMCCAJsVhUdkUncLVxPo5yDMHhcz3-jPRSopIRb5vyoLrsGPp13Cf6WB6mXCzB82R93I2mnlWWy8yc4J_EesgIYLVOvKoOquyipKTjwybrm2l2I7iiipw"}
2022/11/06 10:39:33 --- response:
{
  "type": "http-01",
  "status": "pending",
  "url": "https://acme-staging-v02.api.letsencrypt.org/acme/chall-v3/4230895534/n1apyQ",
  "token": "jbzeJawKuXVhRleEn9U1swg2iqNFKG7d4DiOVDTgoOM"
}

Then, 2 seconds later, it fails:

HTTP POST https://acme-staging-v02.api.letsencrypt.org/acme/chall-v3/4230895534
2022/11/06 10:41:59 --- request:
{"payload":"","protected":"eyJhbGciOiJSUzI1NiIsIm5vbmNlIjoiQTI3MnRoRFJ5U3BOd3hrQThfZjl6YWlMbzV2N2VDaTlIV0t2dmMzMUg2QU1pd3MiLCJ1cmwiOiJodHRwczovL2FjbWUtc3RhZ2luZy12MDIuYXBpLmxldHNlbmNyeXB0Lm9yZy9hY21lL2NoYWxsLXYzLzQyMzA4OTU1MzQiLCJraWQiOiJodHRwczovL2FjbWUtc3RhZ2luZy12MDIuYXBpLmxldHNlbmNyeXB0Lm9yZy9hY21lL2FjY3QvNzUxMzkwMzQifQ","signature":"Yl8zPbnat63d9bBPXTeAIFAzvHEG9Rc7nofW-TntfF69nbj5-WtB8glG8dTzBi_1ZeqptKGwfd-wanYJlhhDOv5ZfjX4msdlVYiXlyATt2MqRJQCGoQ11n4dECyk3xcPa89Bf2sqHd4IyeQkgFKBLJCmv-Pz_6UC6ihsne6cVGh61M5vD4oVhSnNqn3MGxgTdYYsENGxT24V-Q1suh5fyqk99QnM28zJsgZlYyHeLkvjmyfbMJ0WRRdZoQoi4r3B9vHKHLjUWwi7WabctjjYaLowaU718b0k2BxjWRrZs7TauADSbt2c9VyJuu9UvGuwLgUHTU1SVCCkijydI03zNA"}
2022/11/06 10:41:59 --- response:
{
  "type": "urn:ietf:params:acme:error:malformed",
  "detail": "No such challenge",
  "status": 404
}
2022/11/06 10:41:59 HTTP HEAD https://acme-staging-v02.api.letsencrypt.org/acme/new-nonce
2022/11/06 10:41:59 HTTP POST https://acme-staging-v02.api.letsencrypt.org/acme/chall-v3/4230895534/n1apyQ
2022/11/06 10:41:59 --- request:
{"payload":"e30","protected":"eyJhbGciOiJSUzI1NiIsIm5vbmNlIjoiODhCODNidlVlUXBmbGVtdllKSjVPNEJ4bEFTb3l0N0JvWFI0X1VBbUdMdDNpYVEiLCJ1cmwiOiJodHRwczovL2FjbWUtc3RhZ2luZy12MDIuYXBpLmxldHNlbmNyeXB0Lm9yZy9hY21lL2NoYWxsLXYzLzQyMzA4OTU1MzQvbjFhcHlRIiwia2lkIjoiaHR0cHM6Ly9hY21lLXN0YWdpbmctdjAyLmFwaS5sZXRzZW5jcnlwdC5vcmcvYWNtZS9hY2N0Lzc1MTM5MDM0In0","signature":"ZYQj2mZ8E8l4aOofSGZFDZ1zrovSS30zFvHxse9NmFcn0VsSazbIbeiyXwvcZHt82PWFsXrcVY1ws3cDmRLtFrx0-3ApFwxb3DMh0m17i2RCAF-LBdI_p4WVvk0Stv0fpiZedcya45fxMp4-qTBPIBRelYZEMWhmkkewqYrCD4EgkhHSNDV5mEQ86jvUhDSOjbgOzmufQI0Ja4D2tRlomDNNsYwSneKH96uSOkG9ub0ZXpLl8_a6MUoP3a1VJzm6M1ho1NEzaHKMU3unqZmciAqkBmBVvmfCrfprPjV8Q33LL_tS9m1k-UE-g80VcrLBZKkY7gY9QUWl6Mpz6WznsQ"}
2022/11/06 10:41:59 --- response:
{
  "type": "urn:ietf:params:acme:error:malformed",
  "detail": "Unable to update challenge :: authorization must be pending",
  "status": 400
}

I'm seeing some challenges coming in for old tokens. You might be sending those. They will get a 404 unless the token matches the currently active challenge. I can remove that check and respond to any token.

Likely just automated crawlers hitting links you've posted here.

4 Likes

Makes sense, rg305.
I've disabled the token check, so now every challenge should work. For example:
http://oscarkilo.com/.well-known/acme-challenge/10RgoQjyBzvBU0M-XsO8PS9eQzmdxQS2VnyIK4Ae7U8

1 Like

That's not the complete challenge URL.

If you look at the actual challenge, you can see the real error why it failed:

https://acme-staging-v02.api.letsencrypt.org/get/chall-v3/4230895534/n1apyQ

(Note that the /get/ path only works [and currently is only required to see the challenge without a GET-as-POST] on the staging API. You should obviously not use /get/ instead of /acme/ in your client, its just for debugging purposes.)

4 Likes

Yes, sorry for the confusion in the last part. I do send a request to the full URL. There are two requests in the last section of my post.

Oh, thanks for the /get trick. That helps a lot.

1 Like

But in the second part you're sending {} again, which makes the ACME server think you want to trigger it a second time, which is not possible if the challenge is already in the invalid state.

The first payload is actually correct: not an empty JSON thingy but an actual empty payload. Just to the incorrect URL. The second uses the correct URL, but just the incorrect payload which makes it an incorrect GET-as-POST request, but a trigger.

4 Likes

Oh, I see. What should I send to get the status properly? I've been struggling to find documentation for this API, and I've been piecing it together by reading other clients.

And now I also need to figure out why the DNS lookup is failing. What is LetsEncrypt expecting to see in the A and AAAA records?

See the POST-as-GET section of RFC 8555 (6.3).

Not a zone with horribly broken DNSSEC, that's for one :stuck_out_tongue: See oscarkilo.com | DNSViz

6 Likes

By the way, while it seems to be possible to poll the challenge, RFC 8555 says in section 7.5.1 one should poll the authz resource:

Usually, the validation process will take some time, so the client
will need to poll the authorization resource to see when it is
finalized. For challenges where the client can tell when the server
has validated the challenge (e.g., by seeing an HTTP or DNS request
from the server), the client SHOULD NOT begin polling until it has
seen the validation request from the server.

Further along 7.5.1 the RFC says to use a POST-as-GET method to poll.

5 Likes

Thanks, Osiris. I clearly have homework to do to figure out what happened to that DNS.

2 Likes