403 error Can't do axios request from front to backend (Willing To Pay)

Solved
jayraydev
Visitor
2 1 0

So I've been trying to get this to work but no matter what I do I keep getting a 403 error.

I'm passing the token to the backend but when it hits verifyRequest({ returnHeader: true }) it just keeps giving me a 403 error.

Need help with this ASAP since I already wasted enough time on this.

 

contact me luisgarciacode@gmail.com

_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();
  getSessionToken(app).then(token => console.log('token = ', token))
  
  const authAxios = axios.create();

  authAxios.interceptors.request.use((config) => {
    return getSessionToken(app)
    .then((token) => {
      console.log(token)
      config.headers["Authorization"] = `Bearer ${token}`;
      return config;
    })

  })

  


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

  const Component = props.Component;

  return (
    <ApolloProvider client={client}>
      <Component {...props} authAxios={authAxios} />
    </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;


{/* <AppBridgeWithShopify>
  <GraphqlConnection>
    <Component></Component>
  </GraphqlConnection>
</AppBridgeWithShopify> */}

 

 

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";
import {PrismaClient} from '@prisma/client';
import { storeCallback, loadCallback, deleteCallback } from './custom-sessions.js';
import bodyParser from 'koa-bodyparser';
import slugify from "slugify";


const  {user, faq} = new PrismaClient(); 

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:\/\//, ""),
  API_VERSION: ApiVersion.October20,
  IS_EMBEDDED_APP: true,
  // This should be replaced with your preferred storage strategy
  // SESSION_STORAGE: new Shopify.Session.MemorySessionStorage(),
  SESSION_STORAGE: new Shopify.Session.CustomSessionStorage(
    storeCallback,
    loadCallback,
    deleteCallback
  ),
});

// Storing the currently active shops in memory will force them to re-login when your server restarts. You should
// persist this object in your app.
// const ACTIVE_SHOPIFY_SHOPS = {};

app.prepare().then(async () => {
  const server = new Koa();
  const router = new Router();
  server.use(bodyParser());
  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;
        console.log(ctx.state)
        console.log(ctx.state.shopify)
        const host = ctx.query.host;
        // ACTIVE_SHOPIFY_SHOPS[shop] = scope;

        // const newUser = await user.upsert({
        //   where:{ store: shop},
        //   update: {store: shop, scope: scope, updated_at: new Date().toISOString()},
        //   create: {store: shop, scope: scope, updated_at: new Date().toISOString()}
        // })

        const response = await Shopify.Webhooks.Registry.register({
          shop,
          accessToken,
          path: "/webhooks",
          topic: "APP_UNINSTALLED",
          webhookHandler: async (shop) =>
            await user.delete({where: {shop: shop}}),
        });

        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);
    }
  );

  // FAQ Routes
  router.post(
    "/faq",
    verifyRequest({ returnHeader: true }),
    async (ctx, next) => {
      const {title, description} = ctx.request.body;
      let user_id = await user.findFirst({
        where: { store: ctx.query.shop}
      })
      user_id = user_id.id
      

      const newFaq = await faq.create({
        data: {
          title: title,
          slug: slugify(title, '_'),
          description: description,
          user_id: user_id,
          dynamic: false,
          updated_at: new Date().toISOString()
        },
      })

      return ctx.body = {
        status: 'success',
        data: newFaq
      }
      console.log(newFaq)
    }
  );
  router.put(
    "/faq/:id",
    verifyRequest({ returnHeader: true }),
    async (ctx, next) => {
      await Shopify.Utils.graphqlProxy(ctx.req, ctx.res);
    }
  );
  router.del(
    "/faq/:id",
    verifyRequest({ returnHeader: true }),
    async (ctx, next) => {
      await Shopify.Utils.graphqlProxy(ctx.req, ctx.res);
    }
  );

  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;
    // console.log('ACTIVE_SHOPIFY_SHOPS')
    // console.log(ACTIVE_SHOPIFY_SHOPS)

    const checkShop = await user.findFirst({
      where: { store: shop}
    })

    // This shop hasn't been seen yet, go through OAuth to create a session
    if (checkShop === null) {
      ctx.redirect(`/auth?shop=${shop}`);
    } else {
      await handleRequest(ctx);
    }
  });

  server.use(router.allowedMethods());
  server.use(router.routes());
  server.listen(port, () => {
    console.log(`> Ready on http://localhost:${port}`);
  });
});

 

Accepted Solution (1)
jayraydev
Visitor
2 1 0

This is an accepted solution.

Forget it I figured it out.

The issue was in the custom session storage not working properly.

 

View solution in original post

Replies 2 (2)
jayraydev
Visitor
2 1 0

This is an accepted solution.

Forget it I figured it out.

The issue was in the custom session storage not working properly.

 

zilehuda
Tourist
17 0 2

can you please share the working solution?