node.jsshopifyshopify-appshopify-apishopify-api-node

Shopify Apps with NodeJS problem "Error: Failed to parse session token '******' jwt expired"


Greetings I have a problem every time when I want to make an Admin REST API call to Shopify I get this problem "Error: Failed to parse session token '****' jwt expired" I see some code examples on the net I have my own custom session storage for accessToken and shop but every time when I try to call my own route from front-end and get more details about the shop I get this problem here is code example can anyone help me?

server.js

import "@babel/polyfill";
import dotenv from "dotenv";
import "isomorphic-fetch";
import createShopifyAuth, { verifyRequest } from "@shopify/koa-shopify-auth";
import Shopify, { ApiVersion } from "@shopify/shopify-api";
import Koa from "koa";
import next from "next";
import Router from "koa-router";

const helmet = require("koa-helmet");
const compress = require("koa-compress");
const cors = require("koa-cors");
const logger = require("koa-logger");
const bodyParser = require("koa-bodyparser");
import axios from "axios";

import { storeCallback, loadCallback, deleteCallback } from "./custom-session";
const sequelize = require("./database/database");
const { Shopify_custom_session_storage } = require("./../models/sequelizeModels");
// import apiRouter from "./../routers/apiRouter";


dotenv.config();
const port = parseInt(process.env.PORT, 10) || 8081;
const dev = process.env.NODE_ENV !== "production";
const app = next({
  dev,
});
const handle = app.getRequestHandler();

Shopify.Context.initialize({
  API_KEY: process.env.SHOPIFY_API_KEY,
  API_SECRET_KEY: process.env.SHOPIFY_API_SECRET,
  SCOPES: process.env.SCOPES.split(","),
  HOST_NAME: process.env.HOST.replace(/https:\/\/|\/$/g, ""),
  API_VERSION: ApiVersion.October20,
  IS_EMBEDDED_APP: true,
  // This should be replaced with your preferred storage strategy
  SESSION_STORAGE: new Shopify.Session.CustomSessionStorage(storeCallback, loadCallback, deleteCallback)
});

sequelize.sync()
.then(() => {
  app.prepare().then(async () => {
    const server = new Koa();
    const router = new Router();

    server.keys = [Shopify.Context.API_SECRET_KEY];
    server.use(
      createShopifyAuth({
        async afterAuth(ctx) {
          // Access token and shop available in ctx.state.shopify
          const { shop, accessToken, scope } = ctx.state.shopify;
          const host = ctx.query.host;

          // Getting users data from database and saving it to variable //
          try {
            await Shopify_custom_session_storage.findAll({
                raw: true,
                where:{
                  shop: shop
                },
                limit:1
              });
          } catch(err) {
            console.log(err);
            throw err;
          }
        //  End of Getting users data from database and saving it to variable //

          const response = await Shopify.Webhooks.Registry.register({
            shop,
            accessToken,
            path: "/webhooks",
            topic: "APP_UNINSTALLED",
            webhookHandler: async (topic, shop, body) =>{
              return Shopify_custom_session_storage.destroy({
                where: {
                  shop: shop
                }
              })
              .then(result => {
                return true;
              })
              .catch(err => {
                if(err) throw err;
                return false;
              });
            }
          });

          if (!response.success) {
            console.log(
              `Failed to register APP_UNINSTALLED webhook: ${response.result}`
            );
          }

          // Redirect to app with shop parameter upon auth
          ctx.redirect(`/?shop=${shop}&host=${host}`);
        },
      })
    );

    const handleRequest = async (ctx) => {
      await handle(ctx.req, ctx.res);
      ctx.respond = false;
      ctx.res.statusCode = 200;
    };

    router.post("/webhooks", async (ctx) => {
      try {
        await Shopify.Webhooks.Registry.process(ctx.req, ctx.res);
        console.log(`Webhook processed, returned status code 200`);
      } catch (error) {
        console.log(`Failed to process webhook: ${error}`);
      }
    });

    router.post("/graphql", verifyRequest({ returnHeader: true }),  async (ctx, next) => {
        await Shopify.Utils.graphqlProxy(ctx.req, ctx.res);
      }
    );

    // Our Routes //
    router.get("/getProducts", verifyRequest({ returnHeader: true }), async (ctx) => {
      try{
        const session = await Shopify.Utils.loadCurrentSession(ctx.req, ctx.res);
        const client = new Shopify.Clients.Rest(session.shop, session.accessToken);
        
        console.log(session);
      }catch(err) {
        console.log(err);
        throw new Error(err);
      }
    });
    // End of Our Routes //

    router.get("(/_next/static/.*)", handleRequest); // Static content is clear
    router.get("/_next/webpack-hmr", handleRequest); // Webpack content is clear
    router.get("(.*)", async (ctx) => {
      const shop = ctx.query.shop;
      try {
       let user = await Shopify_custom_session_storage.findAll({
            raw: true,
            where:{
              shop: shop
            },
            limit:1
          });
        // This shop hasn't been seen yet, go through OAuth to create a session
        if (user[0].shop == undefined) {
          ctx.redirect(`/auth?shop=${shop}`);
        } else {
          await handleRequest(ctx);
        }
      } catch(err) {
        console.log(err);
        throw err;
      }
    });

    server.use(router.allowedMethods());
    server.use(router.routes());

    // Setting our installed dependecies //
    server.use(bodyParser());
    server.use(helmet());
    server.use(cors());
    server.use(compress());
    server.use(logger());
    // End of Setting our installed dependecies //
    server.listen(port, () => {
      console.log(`> Ready on http://localhost:${port}`);
    });
  });
})
.catch((err) => {
  if(err) throw err;
  return process.exit(1);
})

_app.js

import ApolloClient from "apollo-boost";
import { ApolloProvider } from "react-apollo";
import App from "next/app";
import { AppProvider } from "@shopify/polaris";
import { Provider, useAppBridge } from "@shopify/app-bridge-react";
import { authenticatedFetch, getSessionToken } from "@shopify/app-bridge-utils";
import { Redirect } from "@shopify/app-bridge/actions";
import "@shopify/polaris/dist/styles.css";
import translations from "@shopify/polaris/locales/en.json";
import axios from 'axios';

function userLoggedInFetch(app) {
  const fetchFunction = authenticatedFetch(app);

  return async (uri, options) => {
    const response = await fetchFunction(uri, options);

    if (
      response.headers.get("X-Shopify-API-Request-Failure-Reauthorize") === "1"
    ) {
      const authUrlHeader = response.headers.get(
        "X-Shopify-API-Request-Failure-Reauthorize-Url"
      );

      const redirect = Redirect.create(app);
      redirect.dispatch(Redirect.Action.APP, authUrlHeader || `/auth`);
      return null;
    }

    return response;
  };
}

function MyProvider(props) {
  const app = useAppBridge();

  const client = new ApolloClient({
    fetch: userLoggedInFetch(app),
    fetchOptions: {
      credentials: "include",
    },
  });


  const axios_instance = axios.create();
  // Intercept all requests on this Axios instance
  axios_instance.interceptors.request.use(function (config) {
  return getSessionToken(app) // requires a Shopify App Bridge instance
    .then((token) => {
      // Append your request headers with an authenticated token
      config.headers["Authorization"] = `Bearer ${token}`;
      return config;
    });
});

  const Component = props.Component;

  return (
    <ApolloProvider client={client}>
      <Component {...props} axios_instance={axios_instance}/>
    </ApolloProvider>
  );
}

class MyApp extends App {
  render() {
    const { Component, pageProps, host } = this.props;
    return (
      <AppProvider i18n={translations}>
        <Provider
          config={{
            apiKey: API_KEY,
            host: host,
            forceRedirect: true,
          }}
        >
          <MyProvider Component={Component} {...pageProps} />
        </Provider>
      </AppProvider>
    );
  }
}

MyApp.getInitialProps = async ({ ctx }) => {
  return {
    host: ctx.query.host,
  };
};

export default MyApp;

index.js

import { Heading, Page, Button } from "@shopify/polaris";


function Index(props){
      async function getProducts(){
        const res = await props.axios_instance.get("/products");
        return res;
      }

      async function handleClick() {
        const result = await getProducts();
        console.log(result);
      }

    return (
      <Page>
        <Heading>Shopify app with Node and React </Heading>
        <Button onClick={handleClick}>Get Products</Button>
      </Page>
    );
}

export default Index;

Solution

  • I found the solution for "Error: Failed to parse session token '******' jwt expired" the problem was Computer Time was not synchronized, check the computer time and synchronized it, for my example, I'm on Kali Linux and I search it how to synchronize time on Kali Linux and follow that tutorial when you finally synchronize your time restart your application server and try again. That's it so dump I lost 4 days on this.