@agnostack/next-shopify
Version:
Please contact agnoStack via info@agnostack.com for any questions
159 lines • 9.58 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.handleCallback = void 0;
const shopify_api_1 = require("@shopify/shopify-api");
const utils_1 = require("../../utils");
const shared_1 = require("../../../shared");
/* NOTE: typically handling:
* "/api/auth/online/callback"
* "/api/auth/offline/callback"
* "/api/auth/online/callback/reauth"
* "/api/auth/offline/callback/reauth"
*/
// TODO: delete this for legacy auth
const handleCallback = (serverRuntimeConfig, data) => {
const { API_PATHS, PAGE_PATHS, PAYMENT_CONFIG, } = serverRuntimeConfig;
const { returnResponse = true } = data !== null && data !== void 0 ? data : {};
const prepareWebhookHandlers = (0, utils_1.getPrepareWebhookHandlers)(serverRuntimeConfig);
return (req, res, params) => __awaiter(void 0, void 0, void 0, function* () {
var _a, _b;
const errorPath = PAGE_PATHS[shared_1.PAGE_ROUTE_NAMES.ERROR];
const { isOnline, reAuth } = params !== null && params !== void 0 ? params : {};
const { shop, host, state } = (_a = req.query) !== null && _a !== void 0 ? _a : {};
const authPayload = { rawRequest: req, rawResponse: res };
const handleResponse = (body, statusCode = 307) => {
if (returnResponse) {
return res.redirect(statusCode, body);
}
return { statusCode, body };
};
if ((0, shared_1.stringEmpty)(shop) || (0, shared_1.stringEmpty)(host)) {
// HMMM: why is this a 303 (rather than a 302/307 or or anything)??
return handleResponse(`${errorPath}?code=${shared_1.ERROR_CODES.INVALID_CALLBACK}`, 303);
}
let redirectUrl = `${errorPath}?code=invalid_callback_response`;
let shopify;
try {
const { shopify: _shopify, loadAppData, storeSession: encryptStoreSession, ensureBilling: handleEnsureBilling, } = yield (0, utils_1.getShopifyHelpers)(serverRuntimeConfig, { shop });
shopify = _shopify;
const { session: _session } = yield shopify.auth.callback(authPayload);
const session = _session === null || _session === void 0 ? void 0 : _session.toObject();
const offlineAuthRoute = API_PATHS[shared_1.API_ROUTE_NAMES.AUTH_OFFLINE];
const shouldProcessOfflineAuth = (isOnline && offlineAuthRoute);
const shouldRegisterWebhooks = (!(0, shared_1.isTrue)(session === null || session === void 0 ? void 0 : session.isOnline) || !offlineAuthRoute);
const shouldCheckBilling = PAYMENT_CONFIG === null || PAYMENT_CONFIG === void 0 ? void 0 : PAYMENT_CONFIG.required;
// NOTE: handleEnsureBilling below handles calling encryptStoreSession for offline session when shouldCheckBilling
if (isOnline || !shouldCheckBilling) {
yield encryptStoreSession(session);
}
// NOTE: return early to process offline auth & callback (as webhooks cannot use the online one to make API calls)
if (shouldProcessOfflineAuth) {
// TODO: explore objectToQuerystring
const queryParams = new URLSearchParams({ host, shop, state, reAuth, isOnline: false }).toString();
return handleResponse(`${offlineAuthRoute}?${queryParams}`);
}
if (shouldRegisterWebhooks) {
let appId;
try {
const { appInfo } = yield loadAppData();
appId = (0, utils_1.normalizeShopifyId)(appInfo === null || appInfo === void 0 ? void 0 : appInfo.id).id;
// eslint-disable-next-line no-empty
}
catch (_ignore) {
console.info('Ignoring error loading appData', _ignore);
}
const webhookHandlers = prepareWebhookHandlers({ appId });
shopify.webhooks.addHandlers(webhookHandlers);
// NOTE: webhooks cannot use an online session to make API calls to Shopify (but not all webooks may be doing that for all apps)
yield shopify.webhooks.register({ session });
}
const embeddedAppUrl = yield shopify.auth.getEmbeddedAppUrl(authPayload);
// NOTE this is the final/successful return URL
redirectUrl = `${embeddedAppUrl}${(0, shared_1.ensureLeadingSlash)(PAGE_PATHS[shared_1.PAGE_ROUTE_NAMES.SUCCESS])}`;
if (shouldCheckBilling) {
// NOTE: handleEnsureBilling handles calling encryptStoreSession for offline session when shouldCheckBilling
const confirmationUrl = yield handleEnsureBilling(session, { reAuth });
if ((0, shared_1.stringNotEmpty)(confirmationUrl)) {
redirectUrl = confirmationUrl;
}
}
// NOTE confirm that state can be appended to the confirmationUrl,
// otherwise we can append to the redirectUrl above the billing check
redirectUrl = (0, shared_1.appendQuery)(redirectUrl, new URLSearchParams({ state }).toString());
}
catch (error) {
console.error('Error handling callback route', error);
const errorMessage = 'Error handling callback route';
let baseErrorUrl = errorPath;
if (shopify) {
try {
const embeddedAppUrl = yield shopify.auth.getEmbeddedAppUrl(authPayload);
if ((0, shared_1.stringNotEmpty)(embeddedAppUrl)) {
baseErrorUrl = `${embeddedAppUrl}${(0, shared_1.ensureLeadingSlash)(errorPath)}`;
}
// eslint-disable-next-line no-empty
}
catch (_ignore) { }
}
switch (true) {
// TODO implement a custom SessionNotFound error
case error instanceof shopify_api_1.InvalidHmacError: {
console.warn(`${errorMessage}. InvalidHmac Error ${error.message}`);
// HMMMM: not sure
const authOnlinePath = API_PATHS[shared_1.API_ROUTE_NAMES.AUTH_ONLINE];
const authPath = isOnline
? authOnlinePath
: API_PATHS[shared_1.API_ROUTE_NAMES.AUTH_OFFLINE] || authOnlinePath;
// TODO: explore objectToQuerystring
const queryParams = new URLSearchParams(Object.assign({ host, shop }, state && { state })).toString();
redirectUrl = `${authPath}?${queryParams}`;
break;
}
case error instanceof shopify_api_1.CookieNotFound: {
console.warn(`${errorMessage}. This may occur if Cookie is already deleted. CookieNotFound Error ${error.message}`);
// HMMMM: not sure
const authOnlinePath = API_PATHS[shared_1.API_ROUTE_NAMES.AUTH_ONLINE];
const authPath = isOnline
? authOnlinePath
: API_PATHS[shared_1.API_ROUTE_NAMES.AUTH_OFFLINE] || authOnlinePath;
// TODO: explore objectToQuerystring
const queryParams = new URLSearchParams(Object.assign({ host, shop }, state && { state })).toString();
redirectUrl = `${authPath}?${queryParams}`;
break;
}
case error instanceof shopify_api_1.BillingError: {
console.error(`${errorMessage}. Billing Error ${error.message}`, error.errorData);
redirectUrl = `${baseErrorUrl}?code=${shared_1.ERROR_CODES.BILLING}`;
break;
}
case error instanceof shopify_api_1.GraphqlQueryError: {
const errorMessages = (0, shared_1.ensureArray)((_b = error.response) === null || _b === void 0 ? void 0 : _b.errors).map(({ message }) => (0, shared_1.ensureString)(message));
console.error(`${errorMessage}. GraphqlQuery Error ${errorMessages.join('. ')}`);
redirectUrl = `${baseErrorUrl}?code=${shared_1.ERROR_CODES.QUERY}`;
break;
}
case error instanceof shopify_api_1.InvalidOAuthError: {
console.error(`${errorMessage}. InvalidOAuth Error`, error);
redirectUrl = `${baseErrorUrl}?code=${shared_1.ERROR_CODES.OAUTH}`;
break;
}
default: {
console.error(errorMessage, error);
redirectUrl = `${baseErrorUrl}?code=${shared_1.ERROR_CODES.CALLBACK}`;
}
}
}
return handleResponse(redirectUrl);
});
};
exports.handleCallback = handleCallback;
//# sourceMappingURL=callback.js.map