I have used the “simple-koa-shopify-auth”: "^2.0.0"and “@shopify/shopify-api”: "^5.0.1"after upgrade version of koa getting this error :
Refused to frame ‘https://pos-text-message.myshopify.com/’ because an ancestor violates the following Content Security Policy directive: “frame-ancestors ‘none’”.
Here is my server code
require('isomorphic-fetch');
const dotenv = require('dotenv');
const next = require('next');
// Koa-related
const http = require('http');
const Koa = require('koa');
const cors = require('@koa/cors');
const socket = require('socket.io');
const bodyParser = require('koa-bodyparser');
//const vhost = require('koa-virtual-host');
// const {
// default: shopifyAuth,
// verifyRequest,
// } = require('@shopify/koa-shopify-auth');
//const verifyRequestone = require("./middleware/verify-request.js");
const { createShopifyAuth, verifyRequest } = require("simple-koa-shopify-auth");
const { default: Shopify, ApiVersion } = require('@shopify/shopify-api');
// const {
// default: createShopifyAuth,
// verifyRequest,
// } = require('@shopify/koa-shopify-auth');
const session = require('koa-session');
const { default: graphQLProxy } = require('@shopify/koa-shopify-graphql-proxy');
const Router = require('koa-router');
// Server-related
const registerShopifyWebhooks = require('./server/registerShopifyWebhooks');
const getShopInfo = require('./server/getShopInfo');
const { configureURLForwarderApp } = require('./routes/urlForwarderApp');
// Mongoose-related
const mongoose = require('mongoose');
const Shop = require('./models/shop');
// Routes
const combinedRouters = require('./routes');
const jobRouter = require('./routes/jobs');
// Twilio-related
const twilio = require('twilio');
// Mixpanel
const Mixpanel = require('mixpanel');
const { eventNames } = require('./constants/mixpanel');
const { createDefaultSegments } = require('./modules/segments');
const { createDefaultTemplates } = require('./modules/templates');
const { createDefaultAutomations } = require('./modules/automations');
// Server Files
const { registerJobs } = require('./server/agenda');
const { EventsQueueConsumer } = require('./server/jobs/eventsQueueConsumer');
const getShopOrderCount = require('./server/getShopOrderCount');
const jwt_decode = require("jwt-decode");
// Access env variables
dotenv.config();
// Constants
const {
APP_VERSION_UPDATE_DATE_RAW,
MONGODB_URI,
PROD_SHOPIFY_API_KEY,
PROD_SHOPIFY_API_SECRET_KEY,
STAGING_SHOPIFY_API_KEY,
STAGING_SHOPIFY_API_SECRET_KEY,
TWILIO_ACCOUNT_SID,
TWILIO_AUTH_TOKEN,
TWILIO_PHONE_NUMBER,
TWILIO_TOLL_FREE_PHONE_NUMBER,
URL_FORWARDER_HOST,
QA_MIXPANEL_TOKEN,
PROD_MIXPANEL_TOKEN,
ENABLE_JOBS,
ENABLE_QUEUE_CONSUMERS,
} = process.env;
// Server initialization
const port = parseInt(process.env.PORT, 10) || 3000;
const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev });
const handle = app.getRequestHandler();
const SHOPIFY_API_KEY = dev ? STAGING_SHOPIFY_API_KEY : PROD_SHOPIFY_API_KEY;
const SHOPIFY_API_SECRET_KEY = dev
? STAGING_SHOPIFY_API_SECRET_KEY
: PROD_SHOPIFY_API_SECRET_KEY;
const APP_VERSION_UPDATE_DATE = new Date(APP_VERSION_UPDATE_DATE_RAW);
const MIXPANEL_TOKEN = dev ? QA_MIXPANEL_TOKEN : PROD_MIXPANEL_TOKEN;
// Mongo DB set up
mongoose.connect(MONGODB_URI, { useNewUrlParser: true });
mongoose.set('useFindAndModify', false);
const db = mongoose.connection;
db.on('error', console.error.bind(console, 'MongoDB connection error:'));
// initializes the library
Shopify.Context.initialize({
API_KEY: process.env.PROD_SHOPIFY_API_KEY,
API_SECRET_KEY: process.env.PROD_SHOPIFY_API_SECRET_KEY,
SCOPES: [
'read_orders',
'write_orders',
'read_products',
'write_products',
'read_customers',
'write_customers',
'write_draft_orders',
'read_draft_orders',
'read_script_tags',
'write_script_tags',
],
HOST_NAME: process.env.PROD_SERVER_URL,
API_VERSION: ApiVersion.April22,
IS_EMBEDDED_APP: true,
SESSION_STORAGE: new Shopify.Session.MemorySessionStorage(),
});
const ACTIVE_SHOPIFY_SHOPS = {};
app.prepare().then(() => {
const router = new Router();
const server = new Koa();
//const {shop, accessToken} = ctx.state.shopify;
server.use(session({ secure: true, sameSite: 'none' }, server));
const httpServer = http.createServer(server.callback());
const io = socket(httpServer);
server.context.io = io;
// Start queue consumers if required
if (ENABLE_QUEUE_CONSUMERS == 'true') {
EventsQueueConsumer.run().catch((err) => {
console.log(`Error running Events Queue Consumer: ${err}`);
});
}
// Bind mixpanel to server context
const mixpanel = Mixpanel.init(MIXPANEL_TOKEN);
server.context.mixpanel = mixpanel;
// Twilio config
const twilioClient = new twilio(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN);
const twilioSend = (body, to, from, mediaUrl = []) => {
return twilioClient.messages
.create({
body,
to,
from: from || TWILIO_PHONE_NUMBER,
mediaUrl,
})
.then((twilioMessagesRes) => {
return { delivered: true };
})
.catch((twilioMessagesErr) => {
console.log('Error sending twilio message from API: twilioMessagesErr');
return { delivered: false, error: twilioMessagesErr };
});
};
server.context.twilioSend = twilioSend;
io.on('connection', function (socket) {
socket.on('registerShop', () => {
let ctx = server.createContext(
socket.request,
new http.OutgoingMessage()
);
//const { shop } = ctx.session;
const shop = ctx.query.shop;
//const {shop, accessToken} = ctx.state.shopify;
Shop.findOne({ shopifyDomain: shop }).then((userShop) => {
if (userShop) {
socket.join(`shop:${userShop._id}`);
// Initialize mixpanel user
ctx.mixpanel.people.set(userShop._id, {
$first_name: userShop.shopifyDomain,
$last_name: '',
shopifyDomain: userShop.shopifyDomain,
});
}
});
});
});
server.keys = [SHOPIFY_API_SECRET_KEY];
// if (!URL_FORWARDER_HOST) {
// console.warn('URL_FORWARDER_HOST is not set and will not function.');
// } else {
// server.use(vhost(URL_FORWARDER_HOST, configureURLForwarderApp()));
// }
// Initiate agenda for jobs
if (ENABLE_JOBS === 'true') {
(async function () {
await registerJobs();
})();
}
server.use(cors());
//newauth online
// User auth route (get online session token)
server.use(
createShopifyAuth({
accessMode: "online",
authPath: "/auth",
async afterAuth(ctx) {
const { shop } = ctx.state.shopify;
ACTIVE_SHOPIFY_SHOPS[shop] = true;
// Check if the app is installed
// NOTE: You can replace with your own function to check if the shop is installed, or you can just remove it, but this is an extra check that can help prevent auth issues
//if (isShopActive(shop)) {
// Redirect to app
ctx.redirect(`/?shop=${shop}`);
// } else {
// // Redirect to installation endpoint to get permanent access token
// ctx.redirect(`/install/auth/?shop=${shop}`);
// }
},
})
);
//new auth ooffline
// Installation route (get offline, permanent access token)
// server.use(
// createShopifyAuth({
// accessMode: "offline",
// //authPath: "/install/auth",
// async afterAuth(ctx) {
// const { shop, accessToken } = ctx.state.shopify;
// if (!accessToken) {
// // This can happen if the browser interferes with the auth flow
// ctx.response.status = 500;
// ctx.response.body = "Failed to get access token! Please try again.";
// return;
// }
// // Redirect to user auth endpoint, to get user's online token
// ctx.redirect(`/auth?shop=${shop}`);
// },
// })
// );
//koa createShopifyAuth
console.log('step1',SHOPIFY_API_KEY,'step2',SHOPIFY_API_SECRET_KEY);
// server.use(
// createShopifyAuth({
// apiKey: SHOPIFY_API_KEY,
// secret: SHOPIFY_API_SECRET_KEY,
// accessMode: 'offline',
// scopes: [
// 'read_orders',
// 'write_orders',
// 'read_products',
// 'write_products',
// 'read_customers',
// 'write_customers',
// 'write_draft_orders',
// 'read_draft_orders',
// 'read_script_tags',
// 'write_script_tags',
// ],
// async afterAuth(ctx) {
// const {shop, accessToken} = ctx.state.shopify;
// ACTIVE_SHOPIFY_SHOPS[shop] = true;
// // Register Shopify webhooks
// await registerShopifyWebhooks(accessToken, shop);
// // Gather base shop info on shop
// const [shopInfo, orderCount] = await Promise.all([
// getShopInfo(accessToken, shop),
// getShopOrderCount(accessToken, shop),
// ]);
// // If user doesn't already exist, hasn't approved a subscription,
// // or does not have the latest app version, force them to approve
// // the app payment/usage pricing screen
// let userShop = await Shop.findOne({ shopifyDomain: shop });
// const shouldUpdateApp =
// !userShop ||
// !userShop.appLastUpdatedAt ||
// new Date(userShop.appLastUpdatedAt) < APP_VERSION_UPDATE_DATE;
// const existingShopName =
// userShop && userShop.shopName
// ? userShop.shopName
// : shopInfo && shopInfo.name;
// // Load 25 cents into new accounts
// if (!userShop) {
// // Track new install
// userShop = await Shop.findOneAndUpdate(
// { shopifyDomain: shop },
// {
// accessToken: accessToken,
// appLastUpdatedAt: new Date(),
// shopSmsNumber: TWILIO_PHONE_NUMBER,
// tollFreeNumber: TWILIO_TOLL_FREE_PHONE_NUMBER,
// smsNumberIsPrivate: false,
// loadedFunds: 0.5,
// shopName: existingShopName,
// quarterlyOrderCount: orderCount,
// guidedToursCompletion: {
// dashboard: null,
// conversations: null,
// templates: null,
// subscribers: null,
// segments: null,
// campaigns: null,
// analytics: null,
// automations: null,
// },
// },
// { new: true, upsert: true, setDefaultsOnInsert: true }
// ).exec();
// ctx.mixpanel.track(eventNames.INSTALLED_APP, {
// distinct_id: userShop._id,
// shopifyDomain: shop,
// accessToken,
// shopSmsNumber: TWILIO_PHONE_NUMBER,
// tollFreeNumber: TWILIO_TOLL_FREE_PHONE_NUMBER,
// });
// // Create default templates and segments
// createDefaultSegments(userShop);
// createDefaultTemplates(userShop);
// } else if (shouldUpdateApp) {
// const existingShopSmsNumber =
// userShop.shopSmsNumber || TWILIO_PHONE_NUMBER;
// const existingTollFreeSmsNumber =
// userShop.tollFreeNumber || TWILIO_TOLL_FREE_PHONE_NUMBER;
// const existingNumberIsPrivate = !!userShop.smsNumberIsPrivate;
// userShop = await Shop.findOneAndUpdate(
// { shopifyDomain: shop },
// {
// accessToken: accessToken,
// appLastUpdatedAt: new Date(),
// shopSmsNumber: existingShopSmsNumber,
// tollFreeNumber: existingTollFreeSmsNumber,
// smsNumberIsPrivate: existingNumberIsPrivate,
// shopName: existingShopName,
// quarterlyOrderCount: orderCount,
// },
// { new: true, upsert: true, setDefaultsOnInsert: true }
// ).exec();
// } else {
// userShop = await Shop.findOneAndUpdate(
// { shopifyDomain: shop },
// {
// accessToken: accessToken,
// shopName: existingShopName,
// quarterlyOrderCount: orderCount,
// },
// { new: true, upsert: true, setDefaultsOnInsert: true }
// ).exec();
// }
// ctx.mixpanel &&
// ctx.mixpanel.people.set(shop._id, {
// quarterlyOrders: orderCount,
// });
// // Redirect user to app home page
// //ctx.redirect('/');
// ctx.redirect(`/?shop=${shop}`);
// },
// })
// );
server.use(graphQLProxy({ version: ApiVersion.April22 }));
server.use(bodyParser());
server.use(jobRouter.routes());
router.get('/healthcheck', async (ctx) => {
var decoded = jwt_decode(ctx.req.headers.authorization.replace('Bearer ', ''));
//const { shop } = decoded.dest.replace('https://', '');
ctx.res.statusCode = 200;
ctx.body = { decoded };
return { decoded };
});
// router.get('/', verifyRequest(), async (ctx) => {
// // Check if user should be redirected anywhere, e.g. welcome page, onboarding flow, etc
// const { shop } = ctx.session;
// const userShop = await Shop.findOne({ shopifyDomain: shop });
// if (!userShop.email || !userShop.country || !userShop.adminPhoneNumber) {
// app.render(ctx.req, ctx.res, '/welcome', ctx.query);
// } else if (
// !userShop.onboardingVersionCompleted ||
// userShop.onboardingVersionCompleted < 1.0
// ) {
// app.render(ctx.req, ctx.res, '/onboarding/get-started', ctx.query);
// } else {
// await handle(ctx.req, ctx.res);
// }
// ctx.respond = false;
// ctx.res.statusCode = 200;
// });
router.get('/', async (ctx) => {
const shop = ctx.query.shop;
//const {shop, accessToken} = ctx.state.shopify;
const userShop = await Shop.findOne({ shopifyDomain: shop });
// If 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 {
// Load app skeleton. Don't include sensitive information here!
// ctx.body = '';
//ctx.body = { userShop };
if (!userShop.email || !userShop.country || !userShop.adminPhoneNumber) {
app.render(ctx.req, ctx.res, '/welcome', ctx.query);
} else {
await handle(ctx.req, ctx.res);
}
// else if (
// !userShop.onboardingVersionCompleted ||
// userShop.onboardingVersionCompleted < 1.0
// ) {
// app.render(ctx.req, ctx.res, '/onboarding/get-started', ctx.query);
// } else {
// await handle(ctx.req, ctx.res);
// }
// ctx.respond = false;
// ctx.res.statusCode = 200;
}
});
router.get('*', verifyRequest(), async (ctx) => {
//router.get('*', async (ctx) => {
if (ctx.url.includes('/?')) {
if (ctx.url.substring(0, 2) != '/?') {
ctx.url = ctx.url.replace('/?', '?'); // Remove trailing slash before params
ctx.url = ctx.url.replace(/\/\s*$/, ''); // Remove trailing slash at the end
}
}
await handle(ctx.req, ctx.res);
ctx.respond = false;
ctx.res.statusCode = 200;
});
server.use(combinedRouters());
server.use(router.allowedMethods());
server.use(router.routes());
httpServer.listen(port, () => {
console.log(`> Ready on http://localhost:${port}`);
});
});
i am following below link suggested by shopify
https://github.com/TheSecurityDev/simple-koa-shopify-auth