Decoding JWT body from webhooks

Hello experts!
I was able to install n8n.io on my VPS finally after lot of head scratching efforts. One of the applications I want to integrate, namely Wix, sends all the info in the form of a JWT each time it calls the webhook. Inherently I dont see any way as to how n8n decodes the JWT and extracts the information from that. Is there any way, or should I request a feature to integrate Wix sites also in n8n.io?
Thanks for your responses in advance!

Hi @Dhyaneshwar_Ra, thanks so much for reaching out. Glad to hear you got n8n installed.

I assume you are referring to the tokens received as part of these webhooks? You could decode JWTs using pure JavaScript, for example in a Function Item node. You can find a sample JS snippet here on Stack Overflow.

I quickly tested this with the example JWT provided by Wix using the below workflow and it was working fine for me:

{
  "nodes": [
    {
      "parameters": {
        "values": {
          "number": [],
          "string": [
            {
              "name": "jwt",
              "value": "eyJraWQiOiJxRzFrRDJkeiIsImFsZyI6IlJTMjU2In0.eyJkYXRhIjp7IlNIQTI1NiI6IjNmZDA1ZGZlNDI5ODM3ZGE4NmNiYzcxMDE5MGM5YTY3Mjk2MjAzYmJkNGJkMzE2MGFiMGZmMDdiNjU5YjAxNjAifSwiaWF0IjoxNTUwOTM2NzMxLCJleHAiOjE1NTEyMzY3MzF9.JSRB5MbSNQEXd3we4SJR9voXTIePHlVGSGOb6OXV2v7oHBfRxaisE-ZIdNDMW2Wyy_u48VbKOUxOMdaBGRbP9Vy8S7AuXwixswBYqBS-CG2VffHVAbuijTxUkRzu7Fp29xfC14nDOdF_-aOS5morA_4j-Vbcju3ZwJsk23XLvqLuNmjCgces5QHqYDYazhX8oIqncfEHr1ZJadSFrFZeDhwQmwUGr6xwW8pNi5EJqby1sOAe8r7I3OnYG6qSWrnUHaHfSNJxEzZGST-oFJhaWSc2jGJ8ZyOhtr6UA-j6zdcqEuJBpA_YFpL23eI5vDCkVs6hSOtQ8FkiyFPy07OFzQ"
            }
          ]
        },
        "options": {}
      },
      "name": "Set",
      "type": "n8n-nodes-base.set",
      "typeVersion": 1,
      "position": [
        450,
        300
      ]
    },
    {
      "parameters": {
        "functionCode": "const token = item.jwt;\n\nconst base64Url = token.split('.')[1];\nconst base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');\nconst buff = new Buffer(base64, 'base64');\nconst payloadinit = buff.toString('ascii');\nconst payload = JSON.parse(payloadinit);\nconsole.log(payload);\n\n\nreturn {\n  jwt_content: payload\n}"
      },
      "name": "FunctionItem",
      "type": "n8n-nodes-base.functionItem",
      "typeVersion": 1,
      "position": [
        650,
        300
      ]
    }
  ],
  "connections": {
    "Set": {
      "main": [
        [
          {
            "node": "FunctionItem",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

Hope this helps! Give me a shout if you have any further queries on this.

2 Likes

Thank you so much! Sorry I didn’t reply earlier…
I will try this out and get back!

Hello! I tried this out and this worked well. But again the data that is returned is not in a completely human readable format, nor is it in form of a proper JSON array.
There are a lot of slashes and double quotes that come inbetween the array items and contents… I hope you would have a solution for the same too!
I am attaching the decoded sample JWT from the webhooks below:

[
{
"jwt_content": {
"data": "{"data":"{\"data\":{\"contactId\":\"6232fddc-9b3f-48c0-a921-dc0f158451d3\",\"formName\":\"Contacts Form\",\"submissionTime\":\"2019-10-30T10:09:55.667Z\",\"submissionData\":[{\"fieldName\":\"First Name\",\"fieldValue\":\"John\"},{\"fieldName\":\"Last Name\",\"fieldValue\":\"Doe\"},{\"fieldName\":\"Email\",\"fieldValue\":\"[email protected]\"},{\"fieldName\":\"Phone\",\"fieldValue\":\"346456456\"},{\"fieldName\":\"Message\",\"fieldValue\":\"dfgdfgdfgdf\"}]},\"instanceId\":\"dfecbee2-8721-4199-8d51-9b317c61023a\",\"eventType\":\"com.wixpress.formbuilder.api.v1.FormSubmittedEvent\"}","iat":1572430195,"exp":1576030195}",
"iat": 1635325137,
"exp": 1638925137
}
}
]

The token is decoded but still the decoded data needs to be formatted much more. My ultimate aim is to extract the corresponding data for each field (as you can see above) and insert it into a Google Sheet spreadsheet.
Expecting your indispensable help!
Thank you!

Hi @Dhyaneshwar_Ra, many thanks for sharing! So you have a jwt_content.data field containing some "“stringified” JSON?

I am asking because it seems like the data has suffered a bit when it was copied into the forum. You can use the “Preformatted text” option when adding code to your forum post, this should prevent the forum from applying any formatting itself:

image

So assuming your jwt_content.data field contains stringified JSON? If so, you could use JavaScripts JSON.parse() method to transform such a String into JSON. This would work in either the Function or Function Item node or in Expressions.

For example a Function Item node parsing the String from the data field and writing it into a wixData field could look like so:

{
  "nodes": [
    {
      "parameters": {
        "functionCode": "item.wixData = JSON.parse(item.data);\n\nreturn item;"
      },
      "name": "FunctionItem",
      "type": "n8n-nodes-base.functionItem",
      "typeVersion": 1,
      "position": [
        650,
        300
      ]
    }
  ],
  "connections": {}
}

Sorry for the inconvenience.

[
{
"jwt_content": {
"data": "{"data":"{\"data\":{\"contactId\":\"6232fddc-9b3f-48c0-a921-dc0f158451d3\",\"formName\":\"Contacts Form\",\"submissionTime\":\"2019-10-30T10:09:55.667Z\",\"submissionData\":[{\"fieldName\":\"First Name\",\"fieldValue\":\"John\"},{\"fieldName\":\"Last Name\",\"fieldValue\":\"Doe\"},{\"fieldName\":\"Email\",\"fieldValue\":\"[email protected]\"},{\"fieldName\":\"Phone\",\"fieldValue\":\"346456456\"},{\"fieldName\":\"Message\",\"fieldValue\":\"dfgdfgdfgdf\"}]},\"instanceId\":\"dfecbee2-8721-4199-8d51-9b317c61023a\",\"eventType\":\"com.wixpress.formbuilder.api.v1.FormSubmittedEvent\"}","iat":1572430195,"exp":1576030195}",
"iat": 1635340030,
"exp": 1638940030
}
}
]

This is how the JWT is decoded, and shows to me when viewed as JSON. In Table view I can see triple slashes but in JSON I see single slash.
I am not able to properly extract the array.
I want to extract the relevant fields and append them into Gsheets.
Kindly help!
Thank you!

Hi @Dhyaneshwar_Ra, I was trying to set up a Wix account myself to see this first hand. When creating an automation (on the Wix dashboard under CRM Tools → Automations) I was able to set up a webhook like this:

Now when looking at this webhook payload in n8n, it sends over fields that can easily be accessed in n8n without having to decode JWT or stringified JSON:

Would this possibly a more suitable solution for you? These fields can then be accessed through the expression editor UI with a few simple clicks.

If that’s not for you and you want to continue working with your data structure, I’d suggest drilling down step by step. E.g. seeing you have an item with a jwt_content.data field, you could see if a Function Item node running code like this would already do the trick (possibly not tbh, the string you have provided doesn’t look like properly stringified JSON, so this might need some more custom work):

item.wixData = JSON.parse(jwt_content.data);
return item;

This seems very very doable but it is limited to only 2 automations. So now I have to sacrifice one of the automations. Main goal is achieved but still I want to extract the data as a JSON array and do stuff with it…

I was able to seperately extract the full data part, without the slashes too. But there seems to be several ‘unexpected token errors’ and I have no idea how to solve them :frowning:

Here is one example:

[
"{"data":"{"contactId":"01b73ba0-69ff-4279-8450-ffff7fec6ba5","formName":" Signup Form","submissionTime":"2021-10-27T15:07:21.893Z","submissionData":[{"fieldName":"FIRST NAME","fieldValue":"test n8n2"},{"fieldName":"LAST NAME","fieldValue":"test"},{"fieldName":"EMAIL","fieldValue":"[email protected]"},{"fieldName":"WHATSAPP NUMBER","fieldValue":"8849499494"},{"fieldName":" COLLEGE (Name as per NMC site, alphabetic order)","fieldValue":"All India Institute of Medical Sciences-Bhubaneswar"},{"fieldName":"BATCH","fieldValue":"2017 Batch"},{"fieldName":"ADDRESS","fieldValue":"sdsfrgsgerbfd"},{"fieldName":"BLOOD GROUP","fieldValue":"A2-ve"}]}","instanceId":"04b00967-8567-4161-b159-527f290794ef","eventType":"com.wixpress.formbuilder.api.v1.FormSubmittedEvent"}"
]

If you copy and paste the above piece of code in a JSON parser like http://json.parser.online.fr/ , it shows the JS Eval errors at several places. Is there any way to overcome those errors, format the piece of data on the fly?

Thank you!

As you have noticed when testing this against external validators, this is hardly valid JSON so parsing will be a major pain. Though I wonder if all the nested data fields that might or might not be stringified might just cause the payload to be displayed in not quite the correct way.

Do you have a link to the Wix documentation explaining how to enable the kind of webhook you are trying to parse so I can see this first hand?

https://dev.wix.com/api/rest/marketing/wix-forms/form-submitted-(lead-generated)-webhook
This is the API reference to the webhook

Thanks for the link @Dhyaneshwar_Ra. So this was actually easier then I thought, but not quite straightforward seeing stuff is nested multiple times inside itself here. Nevertheless, the original snippet from my first reply was quite close to the actual solution, it just needs a bit of additional JSON.parse() magic :wink:

So you’d only need to adjust the Function Item code to something like this in order to extract data from these webhooks:

const token = item.body;

const base64Url = token.split('.')[1];
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
const buff = new Buffer(base64, 'base64');
const payloadinit = buff.toString('ascii');
const payload = JSON.parse(payloadinit);
const jwt = JSON.parse(payload.data);
const jwt_data = JSON.parse(jwt.data).data;

return {
  first_name: jwt_data.submissionData.find(e => e.fieldName == 'First Name').fieldValue,
  last_name: jwt_data.submissionData.find(e => e.fieldName == 'Last Name').fieldValue,
  email: jwt_data.submissionData.find(e => e.fieldName == 'Email').fieldValue,
  jwt: jwt_data
}

So essentially first JSON.parse() the data from the (already parsed) payload, then parse the data field from the already parsed previous data field and select its data field (that’s a lot of data right there).

The above example code also extracts fields for the first name, last name, and email. You can easily copy and adjust the respective lines it to include additional text fields as needed.

Hope this helps!

1 Like

Excellent!!
Now I can use the returned items i.e.

return {
  first_name: jwt_data.submissionData.find(e => e.fieldName == 'First Name').fieldValue,
  last_name: jwt_data.submissionData.find(e => e.fieldName == 'Last Name').fieldValue,
  email: jwt_data.submissionData.find(e => e.fieldName == 'Email').fieldValue,
  jwt: jwt_data
}

first_name , email and such fields, in subsequent nodes, correct? Also, the field name should match the title as it is given from the webhook, correct?

Yes, the fields coming from my test webhook were named First Name, Last Name etc.:

So if your fields have different names you’d need to adjust these accordingly.

but should I declare anything seperately, because using SET node, I can create a new string and just give the value as it is, from the array… So if I connect this to a GSheets node and want to append new data as a new row then I can use the Set node, declare the variable and directly point it, correct?
Also unrelated, but I am not able to update my n8n using npm command. Any workaround?

Sure, you don’t have to do this and might as well use a Set node (or any other node really) to access the data inside the jwt field once parsed from JSON.

As for your issue with npm you might want to open a separate topic and share the error you are seeing. It might get missed in here.

1 Like

Sure! Thank you very much! You are a godsend!!!

1 Like

You are most welcome, glad to hear this works for you!

So apparently the solution worked for a while but then stopped :frowning:
When I call the webhook with a real-world data as a payload, it says submissionData is undefined and I am not able to extract the needed info as a JSON array.

So on removing all lines except for jwt_token in the return section, I get nothing to be displayed.
But if I ask for the jwt alone to be returned, I get this as the output:

[
{
"jwt": {
"data": "{"contactId":"21455607-893a-496f-bb0e-0e6e059a4486","formName":"COLLEGE COUNCIL APPLICATION","submissionTime":"2021-10-28T09:49:52.289Z","submissionData":[{"fieldName":"YEAR OF JOINING MBBS","fieldValue":"2020"},{"fieldName":"NAME","fieldValue":"testn8n"},{"fieldName":"MOBILE NUMBER","fieldValue":"7738388383"},{"fieldName":"E-MAIL","fieldValue":"[email protected]"},{"fieldName":"Choose your Medical College","fieldValue":"B. J. Govt. Medical College- Pune"},{"fieldName":"Why do you want to be part of","fieldValue":"testtest testingtesttest testingtesttest testing testtest testing testtest testing testtest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testing"},{"fieldName":"Put forth 5 unique ideas for ","fieldValue":"testtest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testing"},{"fieldName":"Why do you think you are apt ","fieldValue":"testtest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testingtesttest testing testtest testing"}]}",
"instanceId": "04b00967-8567-4161-b159-527f290794ef",
"eventType": "com.wixpress.formbuilder.api.v1.FormSubmittedEvent"
}
}
]

Oh I think I solved it. I removed the last .data from your original code

const jwt_data = JSON.parse(jwt.data).data;

became

const jwt_data = JSON.parse(jwt.data);

and everything came as an array

1 Like

Yeah, seems a bit odd. Your latest example has one level of data less than the previous one. Tbh, I have no idea why Wix is doing this or how this could be predicted from scanning their documentation. Great to hear you figured it out :slight_smile: