@shopify/shopify-app-remix
Version:
Shopify Remix - to simplify the building of Shopify Apps with Remix
130 lines (126 loc) • 5.33 kB
JavaScript
;
var shopifyApi = require('@shopify/shopify-api');
var respondToInvalidSessionToken = require('../../helpers/respond-to-invalid-session-token.js');
var getShopFromRequest = require('../../helpers/get-shop-from-request.js');
var invalidateAccessToken = require('../../helpers/invalidate-access-token.js');
require('isbot');
require('@remix-run/server-runtime');
var handleClientError = require('../helpers/handle-client-error.js');
var triggerAfterAuthHook = require('../helpers/trigger-after-auth-hook.js');
require('../../../types.js');
var ensureOfflineTokenIsNotExpired = require('../../../helpers/ensure-offline-token-is-not-expired.js');
class TokenExchangeStrategy {
api;
config;
logger;
constructor({ api, config, logger }) {
this.api = api;
this.config = config;
this.logger = logger;
}
async respondToOAuthRequests(_request) { }
async authenticate(request, sessionContext) {
const { api, config, logger } = this;
const { shop, session, sessionToken } = sessionContext;
if (!sessionToken)
throw new shopifyApi.InvalidJwtError();
if (!session ||
!session.isActive(undefined, ensureOfflineTokenIsNotExpired.WITHIN_MILLISECONDS_OF_EXPIRY)) {
logger.info('No valid session found', { shop });
logger.info('Requesting offline access token', { shop });
const { session: offlineSession } = await this.exchangeToken({
request,
sessionToken,
shop,
requestedTokenType: shopifyApi.RequestedTokenType.OfflineAccessToken,
});
await config.sessionStorage.storeSession(offlineSession);
let newSession = offlineSession;
if (config.useOnlineTokens) {
logger.info('Requesting online access token', { shop });
const { session: onlineSession } = await this.exchangeToken({
request,
sessionToken,
shop,
requestedTokenType: shopifyApi.RequestedTokenType.OnlineAccessToken,
});
await config.sessionStorage.storeSession(onlineSession);
newSession = onlineSession;
}
logger.debug('Request is valid, loaded session from session token', {
shop: newSession.shop,
isOnline: newSession.isOnline,
});
try {
await this.handleAfterAuthHook({ api, config, logger }, newSession, request, sessionToken);
}
catch (errorOrResponse) {
if (errorOrResponse instanceof Response) {
throw errorOrResponse;
}
throw new Response(undefined, {
status: 500,
statusText: 'Internal Server Error',
});
}
return newSession;
}
return session;
}
handleClientError(request) {
const { api, config, logger } = this;
return handleClientError.handleClientErrorFactory({
request,
onError: async ({ session, error }) => {
if (error.response.code === 401) {
logger.debug('Responding to invalid access token', {
shop: getShopFromRequest.getShopFromRequest(request),
});
await invalidateAccessToken.invalidateAccessToken({ config, logger }, session);
respondToInvalidSessionToken.respondToInvalidSessionToken({
params: { config, api, logger },
request,
});
}
},
});
}
async exchangeToken({ request, shop, sessionToken, requestedTokenType, }) {
const { api, config, logger } = this;
try {
return await api.auth.tokenExchange({
sessionToken,
shop,
requestedTokenType,
expiring: config.future.expiringOfflineAccessTokens,
});
}
catch (error) {
if (error instanceof shopifyApi.InvalidJwtError ||
(error instanceof shopifyApi.HttpResponseError &&
error.response.code === 400 &&
error.response.body?.error === 'invalid_subject_token')) {
throw respondToInvalidSessionToken.respondToInvalidSessionToken({
params: { api, config, logger },
request,
retryRequest: true,
});
}
throw new Response(undefined, {
status: 500,
statusText: 'Internal Server Error',
});
}
}
async handleAfterAuthHook(params, session, request, sessionToken) {
const { config } = params;
await config.idempotentPromiseHandler.handlePromise({
promiseFunction: () => {
return triggerAfterAuthHook.triggerAfterAuthHook(params, session, request, this);
},
identifier: sessionToken,
});
}
}
exports.TokenExchangeStrategy = TokenExchangeStrategy;
//# sourceMappingURL=token-exchange.js.map