@shopify/shopify-app-express
Version:
Shopify Express Middleware - to simplify the building of Shopify Apps with Express
99 lines (96 loc) • 4.15 kB
JavaScript
import { redirectToAuth } from '../redirect-to-auth.mjs';
import { addCSPHeader } from './csp-headers.mjs';
import { validateAuthenticatedSession } from './validate-authenticated-session.mjs';
import { hasValidAccessToken } from './has-valid-access-token.mjs';
function ensureInstalled({ api, config, }) {
return function ensureInstalledOnShop() {
return async (req, res, next) => {
config.logger.debug('Running ensureInstalledOnShop');
if (!api.config.isEmbeddedApp) {
config.logger.warning('ensureInstalledOnShop() should only be used in embedded apps; calling validateAuthenticatedSession() instead');
return validateAuthenticatedSession({ api, config })()(req, res, next);
}
const shop = getRequestShop(api, config, req, res);
if (!shop) {
return undefined;
}
config.logger.debug('Checking if shop has installed the app', { shop });
const sessionId = api.session.getOfflineId(shop);
const session = await config.sessionStorage.loadSession(sessionId);
const exitIframeRE = new RegExp(`^${config.exitIframePath}`, 'i');
if (!session && !req.originalUrl.match(exitIframeRE)) {
config.logger.debug('App installation was not found for shop, redirecting to auth', { shop });
return redirectToAuth({ req, res, api, config });
}
if (api.config.isEmbeddedApp && req.query.embedded !== '1') {
if (await sessionHasValidAccessToken(api, config, session)) {
await embedAppIntoShopify(api, config, req, res, shop);
return undefined;
}
else {
config.logger.info('Found a session, but it is not valid. Redirecting to auth', { shop });
return redirectToAuth({ req, res, api, config });
}
}
addCSPHeader(api, req, res);
config.logger.debug('App is installed and ready to load', { shop });
return next();
};
};
}
function deleteAppInstallationHandler(appInstallations, config) {
return async function (_topic, shop, _body, _webhookId) {
config.logger.debug('Deleting shop sessions', { shop });
await appInstallations.delete(shop);
};
}
function getRequestShop(api, config, req, res) {
if (typeof req.query.shop !== 'string') {
config.logger.error('ensureInstalledOnShop did not receive a shop query argument', { shop: req.query.shop });
res.status(400);
res.send('No shop provided');
return undefined;
}
const shop = api.utils.sanitizeShop(req.query.shop);
if (!shop) {
config.logger.error('ensureInstalledOnShop did not receive a valid shop query argument', { shop: req.query.shop });
res.status(422);
res.send('Invalid shop provided');
return undefined;
}
return shop;
}
async function sessionHasValidAccessToken(api, config, session) {
if (!session) {
return false;
}
try {
return (session.isActive(api.config.scopes) &&
(await hasValidAccessToken(api, session)));
}
catch (error) {
config.logger.error(`Could not check if session was valid: ${error}`, {
shop: session.shop,
});
return false;
}
}
async function embedAppIntoShopify(api, config, req, res, shop) {
let embeddedUrl;
try {
embeddedUrl = await api.auth.getEmbeddedAppUrl({
rawRequest: req,
rawResponse: res,
});
}
catch (error) {
config.logger.error(`ensureInstalledOnShop did not receive a host query argument`, { shop });
res.status(400);
res.send('No host provided');
return;
}
config.logger.debug(`Request is not embedded but app is. Redirecting to ${embeddedUrl} to embed the app`, { shop });
res.redirect(embeddedUrl + req.path);
}
export { deleteAppInstallationHandler, ensureInstalled };
//# sourceMappingURL=ensure-installed-on-shop.mjs.map