Our Partner & Developer boards on the community are moving to a brand new home: the .dev community forums! While you can still access past discussions here, for all your future app and storefront building questions, head over to the new forums.

Get JWT Session Token in Post-Purchase Extension

Solved

Get JWT Session Token in Post-Purchase Extension

Kristian-Dev
Shopify Partner
5 1 1

Hi there,

 

I'm having problems with authenticating API Requests to my App Backend. On the backend side I'm using the verifyRequest() Method from the @shopify/koa-shopify-auth package as recommended here: https://shopify.dev/apps/auth/session-tokens/how-session-tokens-work#anatomy-of-a-session-token

The problem I'm facing now is how to authenticate when making requests from my checkout post-purchase extension.

I thought I could use the token, that is sent from Shopify with the inputData object (Reference here). There is a token field that specifies a jwt session token - according to Shopify.

But when I append this token to the request I'm sending to my backend server, the server always responds with code 500 and the message "Session token had invalid API key".

 

Does somebody have an idea how to solve this problem? I am grateful for any kind of tips or solutions!

 

Thanks in advance!

 

Accepted Solution (1)
Kristian-Dev
Shopify Partner
5 1 1

This is an accepted solution.

This is not clean but try to save and pass the token in the ShouldRender step through storage.update(). Then, retrieve it in the Render step through storage.initialData. Hope this helps!

View solution in original post

Replies 7 (7)

cosoare
Shopify Partner
9 1 2

I am facing a similar issue - I decoded the JWT token and it seems to be missing all the required keys as per the spec: https://shopify.dev/api/checkout-extensions/jwt-specification#claims

 

Wondering if anyone has managed to fix this or is this a bug?

cosoare
Kristian-Dev
Shopify Partner
5 1 1

This is an accepted solution.

This is not clean but try to save and pass the token in the ShouldRender step through storage.update(). Then, retrieve it in the Render step through storage.initialData. Hope this helps!

cosoare
Shopify Partner
9 1 2

Thanks for the reply!

 

What I am doing in the ShouldRender step is accessing the token from inputData, which is meant to be a JWT type token. Then I construct the headers based on this:

 

 

 

extend('Checkout::PostPurchase::ShouldRender', async ({inputData, storage}) => {

  const jwt_token = inputData.token;

  let response = await fetch(url, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      Authorization: 'Bearer ' + jwt_token,
    },
    body: ...
  });
});

 

 

 

Making this request to my Rails backend throws an error like this:

 

 

[ShopifyApp::JWT] Failed to validate JWT: [ShopifyApp::JWT::InvalidAudienceError] 'aud' claim does not match api_key

 

 

 Digging through the JWT module I can see this comes from this line here, which leads me to believe something is wrong with the token. Now, inspecting the JS console I log the token and then use the https://jwt.io/ tool to decode the token, which in turn looks like the following & proves it's missing some object keys from the JWT spec, including the aud one which should be set as the API KEY:

 

Token Header is missing type: "JWT"

 

 

{
  "alg": "HS256"
}

 

 

 

Token Data 

 

 

{
  "iss": "shopify",
  "sub": "a3c59a6b3c451c33b90e68871cd944de",
  "input_data": {
    "extensionPoint": "Checkout::PostPurchase::ShouldRender",
    "initialPurchase": {
      "referenceId": "a3c59a6b3c451c33b90e68871cd944de",
      "customerId": 5788644180212,
      "destinationCountryCode": "GB",
      "totalPriceSet": {
        "shopMoney": {
          "amount": "25.98",
          "currencyCode": "GBP"
        },
        "presentmentMoney": {
          "amount": "25.98",
          "currencyCode": "GBP"
        }
      },
      "lineItems": [
        {
          "product": {
            "id": 6636284346612,
            "metafields": [],
            "title": "Fertilizer",
            "variant": {
              "id": 39467655692532,
              "metafields": [],
              "title": ""
            }
          },
          "quantity": 1,
          "totalPriceSet": {
            "shopMoney": {
              "amount": "19.99",
              "currencyCode": "GBP"
            },
            "presentmentMoney": {
              "amount": "19.99",
              "currencyCode": "GBP"
            }
          }
        }
      ]
    },
    "locale": "en",
    "shop": {
      "id": 1234,
      "domain": "mydomain.myshopify.com",
      "metafields": []
    },
    "version": "unstable"
  },
  "iat": 1642930977
}

 

 

 

I made a comparison with the token used for requests inside the Admin app and it's different:

{
  "alg": "HS256",
  "typ": "JWT"
}

 

{
  "iss": "https://myshop.myshopify.com/admin",
  "dest": "https://myshop.myshopify.com",
  "aud": "API_KEY",
  "sub": "71451279604",
  "exp": 1642934204,
  "nbf": 1642934144,
  "iat": 1642934144,
  "jti": "85c64eb0-2169-4bab-8760-4c2ceecdd6a7",
  "sid": "2fa37cd7fad96de5c965e75a8b8f159b11d11659326f93458471571cee7492e8"
}

 

 

 

The question is - what am I doing wrong or is the token specification not correct for the PostPurchase checkout? 

cosoare
ripci
Shopify Partner
5 1 8

Ever figure this out?

jonaswebdev
Shopify Partner
6 0 0

I'm facing the same issue, I assume that Shopify is issuing the JWT with an incomplete Payload.

 

I checked the token from Checkout::PostPurchase::ShouldRender & Checkout::PostPurchase::Render methods, they are different because of payload content, but both have the same issue.

 

I compared the token from Admin Application and Post Purchase Extension.

The token payload from the front-end and back-end of the Admin Application has this structure:

 

{
  "$isOnline": false,
  "jwtPayload": {
    "iss": "...VALUE...",
    "dest": "...VALUE...",
    "aud": "...VALUE...",
    "sub": "...VALUE...",
    "exp": ...VALUE...,
    "nbf": ...VALUE...,
    "iat": ...VALUE...,
    "jti": "...VALUE...",
    "sid": "...VALUE..."
  }
}

 

 

But the token from post-purchase extension has this structure:

 

{
  "$isOnline": false,
  "jwtPayload": {
    "iss": "...VALUE...",
    "sub": "...VALUE...",
    "input_data": { ... },
    "iat": ...VALUE...
  }
}

 

 

So, it's missing these attributes: dest, aud, exp, nbf, jti, sid.

 

For my scenario, an application using PHP/Laravel with Shopify APl package  ("shopify/shopify-api": "^5.0"), the "dest" attribute is essential for Auth process, then I receives an Exception on the back-end, according the code below:

Screen Shot 2023-05-26 at 16.23.19.png

 

 

I tried to contact Shopify PLUS Support, but with no success, they asked me to contact Partner Support (my development Store is from there) OR directly with the "team" on GitHub, where already there is a similar issue reported (not on shopify-api-php package project), but no response.

 

Anyway, I don't believe that solves the problem, once the problem is on the JWT issue, that is missing some attributes on Payload. So Shopify should check their side and make sure that JWT tokens (payload) are being issued properly.

 

I will try to create a fix and send a PR on GitHub or pray for Shopify Dev Team have time to check this issue for us 🙂 

Krithin_Jay
Shopify Partner
2 0 0

Hi @jonaswebdev, has this issue been solved yet, I'm facing the same issue.

Krithin_Jay
Shopify Partner
2 0 0

This is a long time later, but I found out that the token you're using is not a session token! It is a token that just represents the contents of `inputData`.