Embedded app fails OAuth when deployed

jeffgolota
Visitor
1 0 0

Hi, I'm an intern that is currently developing an embedded Shopify app for the company I'm working for and during development on local testing it works fine but once I pushed the final changes to the repository and the company deployed the app, the installation fails and gives an "Invalid OAuth callback" error when I try to install it on a test store with the deployed URL listed in the app details.

The app gets a list of products and their information

The app is deployed on an EC2 Instance, a Linux system with NPM if that is relevant to solve the problem.

The app's domain is https://shopify-app.stage.iqm.services/

If any other information is needed I can provide

My server.js file is shown below.

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";

dotenv.config();
var token = '';
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(),
});

// 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();
  var url = '';
  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;
        console.log("ACCESS TOKEN = " + accessToken);
        setToken(accessToken);

        const host = ctx.query.host;
        ACTIVE_SHOPIFY_SHOPS[shop] = scope;
        const response = await Shopify.Webhooks.Registry.register({
          shop,
          accessToken,
          path: "/webhooks",
          topic: "APP_UNINSTALLED",
          webhookHandler: async (topic, shop, body) =>
            delete ACTIVE_SHOPIFY_SHOPS[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.get("/", async (ctx) => {
    const shop = ctx.query.shop;
    url = shop;
    // This shop hasn't been seen yet, go through OAuth to create a session
    if (ACTIVE_SHOPIFY_SHOPS[shop] === undefined) {
      ctx.redirect(`/auth?shop=${shop}`);
    } else {
      await handleRequest(ctx);
    }
  });

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

  router.get("(/_next/static/.*)", handleRequest); // Static content is clear
  router.get("/_next/webpack-hmr", handleRequest); // Webpack content is clear
  router.get('/api/products', async (ctx) => {
    try {
      var token = getToken();
      let productsURL = "https://" + url + "/admin/api/2021-04/products.json";
      const results = await fetch(productsURL, {
        headers: {
          "X-Shopify-Access-Token": token,

        },
      })
        .then(response => response.json())
        .then(json => {

          return [json, url];
        });
      ctx.body = {
        status: 'success',
        data: results
      };
    } catch (err) {
      console.log("Fetch Failed");
      console.log(err)
    }
  })
  router.get('/shopname', async (ctx) => {
    return url;
  })
  router.get("(.*)", verifyRequest(), handleRequest); // Everything else must have sessions

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

function getToken() {
  return token;
}
function setToken(newToken) {
  token = newToken;
}
Replies 6 (6)

Avi_Ben_Zaken
Shopify Partner
18 0 6
Click to expand...
Hey, have a similar issue, have you pinned the problem?
mamoumb
Shopify Partner
6 0 0

any answer? same case as well..

test6A
Shopify Partner
23 0 12

Any solutions? I'm facing the same issue

mamoumb
Shopify Partner
6 0 0

My issue was that 'next' was not properly set up in dev nor in production. In production environment I had to set up next.config.js so that api Key and Host url can be provided. I also had to feed my Provider config (_app.js) with api key,host and shop
(shop must me provided to AppBridge e.g "<MyProvider Component={Component} {...pageProps} shop={shop} />") parameters..

SaphiraDev
Shopify Partner
15 2 6

Also have the exact same issue. If anyone has a solution it would be great. My app works in development but not in production for some reason with the auth.

Professional Front-End Developer | Available for work | Contact at: saphiradev@gmail.com
SaphiraDev
Shopify Partner
15 2 6

Okay figured out my issue. I was using the wrong API secret on the server, I accidentally copied the one for my development app instead. 

Professional Front-End Developer | Available for work | Contact at: saphiradev@gmail.com