0

I am trying to set up webhooks for Authy OneTouch push authentication. I manage to successfully register to one_touch_request_responded events, save the webhook signing key from the subscription call, but I have still not managed to verify the requests: any attacker could forge fake requests and easily bypass the 2FA check.

The API documentation is quite confusing to me, given the callback requests don't match the format mentioned (especially they don't have the X-Authy-Signature header), and only have the following headers:

{
  host: 'XXX.ngrok.io',
  'user-agent': 'Authy-api-webhooks/1.0',
  'content-length': '2211',
  'accept-encoding': 'gzip',
  'content-type': 'application/json',
  'x-forwarded-for': '3.89.35.175',
  'x-forwarded-proto': 'http'
}

I have also tried to verify the signature of the JWT token, still to no avail (incoming POST request: {"body":"a_jwt_token"} ): I alway get an invalid signature (same using https://jwt.io/).

const jwt = require("jsonwebtoken");
jwt.verify(req.body.body, Buffer.from(MY_SECRET_KEY, "base64"), { algorithm: ["HS256"] });

What is the proper way of checking the authenticity of the webhook POST callbacks?

Thanks!

jodoox
  • 821
  • 2
  • 7
  • 22

1 Answers1

1

Twilio developer evangelist here.

It looks to me as though you've found the Authy webhooks session that you can subscribe to in order to get updated about various parts of your users' usage of the Authy APIs.

In order to get webhooks for OneTouch push notifications, you should set your webhook URL in the Twilio console under the push notifications settings for the Authy application.

Navigate to the Twilio console, then to the Authy section, choose your Authy app and then open the Push Authentication settings. In there you will find an input for the webhook Endpoint/URL, this is where you set your webhook URL.

Once you have set the webhook URL, you will find webhook events coming through to your application for push authentication approvals and denials. Those requests will also come with the X-Authy-Signature-Nonce and X-Authy-Signature headers and you will be able to re-create the signature using the method explained here.

Sorry that got confusing, hopefully this clears it up for you.

philnash
  • 70,667
  • 10
  • 60
  • 88
  • Thanks @philnash. I had set the webhook URL through the API rather than via the Twilio console, is it any different? I do see the webhook events coming through to my application, I am using a ngrok proxy and as mentioned the requests don't have the X-`Authy-Signature-Nonce` and `X-Authy-Signature` headers. Is this something that would be fixed by registering via the Twilio console, not the API? – jodoox Sep 28 '20 at 08:56
  • Yes, I believe it would. That's how I've set up webhooks before and it worked for me through the console. – philnash Sep 28 '20 at 12:38
  • Actually I now realize that these are two separate ways of registering to the webhooks: - twilio console: inbound requests as plain json with `X-Authy-Signature-Nonce` headers, and I can succesfully verify the requests. - webhook registration: inbound requests as JWT tokens, no custom headers but I can't seem to verify them. I would still rather use this path given that I can specify the callback url directly from the registration process (my callback URL would change regularly). Really weird – jodoox Oct 12 '20 at 09:41
  • Ah, ok, to verify the JWT you need the API signing key. Is that what you’re working with? The details are on this page: https://www.twilio.com/docs/authy/api/webhooks#verify-callbacks – philnash Oct 12 '20 at 10:06
  • Yes indeed. I do get the signing key upon registration, but they don't seem to be valid. Eg pasting the request body + signing key into `https://jwt.io/`, I get an invalid signature message – jodoox Oct 12 '20 at 10:47
  • What do you mean when you say you get it on registration? The docs there say to use the API Signing Key from the console: https://www.twilio.com/docs/authy/api/webhooks#webhooks-api-keys – philnash Oct 12 '20 at 10:50
  • Thanks for your help. I had actually tried using both the signing key from the console and the one returned from the registration call, with no success. Also, in the `Response` template following the link you just provided: `signing_key: The key to verify JWT response received by the callback url`. I don't think the API doc is really clear. – jodoox Oct 12 '20 at 10:53
  • 1
    I’m afraid I’m a bit lost then. It must be one of those signing keys! Perhaps you can update the question with the code you’re currently using to validate the JWT? That might make it easier to see what’s going on. – philnash Oct 12 '20 at 11:00
  • I don't know why and how I lost so much time on this. Using `jsonwebtoken.verify(req.body, signing_key)` actually works as expected, I don't know what went wrong in the first place with this snippet, and also why I could not get jwt.io to verify the tokens. Anyway. – jodoox Oct 15 '20 at 15:05
  • Huh, weird! I'm glad you got it sorted in the end though! – philnash Oct 15 '20 at 22:38