Development discussions around Shopify APIs
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}`);
});
});
Solved! Go to the solution
This is an accepted solution.
Forget it I figured it out.
The issue was in the custom session storage not working properly.
This is an accepted solution.
Forget it I figured it out.
The issue was in the custom session storage not working properly.
can you please share the working solution?
User | RANK |
---|---|
51 | |
11 | |
8 | |
6 | |
6 |
Thanks to all who participated in our AMA with 2H Media on planning your 2023 marketing bu...
By Jacqui Mar 30, 2023Thanks to all Community members that participated in our inaugural 2 week AMA on the new E...
By Jacqui Mar 10, 2023Upskill and stand out with the new Shopify Foundations Certification program
By SarahF_Shopify Mar 6, 2023