Join us for an upcoming Shopify Partner webinar on February 27, 2024. Discover the latest Checkout Extensibility features, and deep dive on improvements to Shopify Functions and Web Pixels. Register now for either the 10am EST or 2pm EST sessions.
Solved

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

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?