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.
User | RANK |
---|---|
9 | |
6 | |
6 | |
5 | |
4 |
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