App reviews, troubleshooting, and recommendations
Hey,
Currently trying to implement a custom session storage using MySQL.
After I add the app it throws a missing access token when creating graphQL client
InternalServerError: Missing access token when creating GraphQL client
Here is my custom session code
import { Session} from '@shopify/shopify-api/dist/auth/session'
const mysql = require('mysql');
const connection = mysql.createConnection({
host:'[host]',
user:'[user]',
password:'[password]',
database:'[database]'
})
console.log('is connecting')
connection.connect();
let domain_id = '';
export async function storeCallback(session){
try{
console.log("calls storeCallback")
let data = session;
console.log("data " +JSON.stringify(data))
data.onlineAccessInfo = JSON.stringify(session.onlineAccessInfo)
if(data.id.indexOf(`${data.shop}`)> -1){
console.log("data.id got called")
domain_id = data.id
}
let connectionQuery = new Promise((resolve, reject) =>{
connection.query(`INSERT INTO shop (shop_url, session_id, domain_id, access_token, state, isOnline, onlineAccessinfo, scope) VALUES ('${data.shop}','${data.id}','${domain_id}','${data.accessToken}','${data.state}','${data.isOnline}','${data.onlineAccessInfo}','${data.scope}') ON DUPLICATE KEY UPDATE access_token='${data.accessToken}', state='${data.state}', session_id='${data.id}',domain_id='${domain_id}',scope='${data.scope}',onlineAccessInfo='${data.onlineAccessInfo}'`,
function(error, results, fields){
if(error) throw error;
resolve();
}
);
});
await connectionQuery
return true;
}catch(e){
console.log("got this error")
throw new Error(e);
}
}
export async function loadCallback(id){
try{
let session = new Session(id);
let query = new Promise((resolve, reject) =>{
connection.query(`SELECT * FROM shop WHERE session_id ='${id}' OR domain_id='${id}' LIMIT 1`,
function(error, results, fields){
if (error) throw error;
session.shop = results[0].shop_url;
session.state = results[0].state;
session.scope = results[0].scope;
session.isOnline = results[0].isOnline == 'true' ? true:false;
session.onlineAccessInfo = results[0].onlineAccessInfo;
session.accessToken = results[0].accessToken;
const date = new Date();
date.setDate(date.getDate() + 1);
session.expires = date;
if(session.expires && typeof session.exipres === 'string'){
session.expires = new Date(session.expires);
}
resolve();
}
);
});
await query;
return session;
}catch(err){
throw new Error(err)
}
}
export async function deleteCallback(id){
try{
return false;
}catch(e){
throw new Error(e)
}
}
And here is my 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 {storeCallback,deleteCallback,loadCallback} from './sessiondb.js'
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.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.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("IM TESTING THIS"+ shop,accessToken,scope);
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.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("(.*)", verifyRequest(),handleRequest);
router.get("(.*)", async (ctx) => {
const shop = ctx.query.shop;
console.log("get request "+ shop)
// This shop hasn't been seen yet, go through OAuth to create a session
if (ACTIVE_SHOPIFY_SHOPS[shop] === undefined) {
console.log("its getting redirected")
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}`);
});
});
What am I missing?
Solved! Go to the solution
This is an accepted solution.
My bad, it was just a naming thing.
accessToken was named access_token in my db
This is an accepted solution.
My bad, it was just a naming thing.
accessToken was named access_token in my db
I think your app could be vulnerable to SQL injection because you are using string concatenation to construct the query. If the shop URL contained a single quote you could escape out of the query.
We recently spoke with Zopi developers @Zopi about how dropshipping businesses can enha...
By JasonH Oct 23, 2024A big shout out to all of the merchants who participated in our AMA with 2H Media: Holi...
By Jacqui Oct 21, 2024We want to take a moment to celebrate the incredible ways you all engage with the Shopi...
By JasonH Oct 15, 2024