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.
Starting a B2B store is a big undertaking that requires careful planning and execution. W...
By JasonH Sep 23, 2024By investing 30 minutes of your time, you can unlock the potential for increased sales,...
By Jacqui Sep 11, 2024We appreciate the diverse ways you participate in and engage with the Shopify Communi...
By JasonH Sep 9, 2024