@shopify/shopify-app-react-router
Version:
Shopify React Router - to simplify the building of Shopify Apps with React Router
101 lines (97 loc) • 4.07 kB
JavaScript
var factory$1 = require('../../../clients/admin/factory.js');
var factory = require('../../../clients/storefront/factory.js');
function authenticateAppProxyFactory(params) {
const { api, config, logger } = params;
return async function authenticate(request) {
const url = new URL(request.url);
const shop = url.searchParams.get('shop');
logger.info('Authenticating app proxy request', { shop });
if (!(await validateAppProxyHmac(params, url))) {
logger.info('App proxy request has invalid signature', { shop });
throw new Response(undefined, {
status: 400,
statusText: 'Bad Request',
});
}
const sessionId = api.session.getOfflineId(shop);
const session = await config.sessionStorage.loadSession(sessionId);
if (!session) {
logger.debug('Could not find offline session, returning empty context', {
shop,
...Object.fromEntries(url.searchParams.entries()),
});
const context = {
liquid,
session: undefined,
admin: undefined,
storefront: undefined,
};
return context;
}
const context = {
liquid,
session,
admin: factory$1.adminClientFactory({ params, session }),
storefront: factory.storefrontClientFactory({ params, session }),
};
return context;
};
}
const liquid = (body, initAndOptions) => {
const processedBody = processLiquidBody(body);
if (typeof initAndOptions !== 'object') {
return new Response(processedBody, {
status: initAndOptions || 200,
headers: {
'Content-Type': 'application/liquid',
},
});
}
const { layout, ...responseInit } = initAndOptions || {};
const responseBody = layout === false ? `{% layout none %} ${processedBody}` : processedBody;
const headers = new Headers(responseInit.headers);
headers.set('Content-Type', 'application/liquid');
return new Response(responseBody, {
...responseInit,
headers,
});
};
async function validateAppProxyHmac(params, url) {
const { api, logger } = params;
try {
let searchParams = new URLSearchParams(url.search);
if (!searchParams.get('index')) {
searchParams.delete('index');
}
let isValid = await api.utils.validateHmac(Object.fromEntries(searchParams.entries()), { signator: 'appProxy' });
if (!isValid) {
const cleanPath = url.pathname
.replace(/^\//, '')
.replace(/\/$/, '')
.replaceAll('/', '.');
const data = `routes%2F${cleanPath}`;
searchParams = new URLSearchParams(`?_data=${data}&${searchParams.toString().replace(/^\?/, '')}`);
isValid = await api.utils.validateHmac(Object.fromEntries(searchParams.entries()), { signator: 'appProxy' });
if (!isValid) {
const searchParams = new URLSearchParams(`?_data=${data}._index&${url.search.replace(/^\?/, '')}`);
isValid = await api.utils.validateHmac(Object.fromEntries(searchParams.entries()), { signator: 'appProxy' });
}
}
return isValid;
}
catch (error) {
const shop = url.searchParams.get('shop');
logger.info(error.message, { shop });
throw new Response(undefined, { status: 400, statusText: 'Bad Request' });
}
}
function processLiquidBody(body) {
return (body
// Add trailing slashes to relative form action URLs
.replaceAll(/<(form[^>]+)action="(\/[^"?]+)(\?[^"]+)?">/g, '<$1action="$2/$3">')
// Add trailing slashes to relative link href URLs
.replaceAll(/<(a[^>]+)href="(\/[^"?]+)(\?[^"]+)?">/g, '<$1href="$2/$3">'));
}
exports.authenticateAppProxyFactory = authenticateAppProxyFactory;
//# sourceMappingURL=authenticate.js.map
;