UNPKG

@shopify/shopify-app-express

Version:

Shopify Express Middleware - to simplify the building of Shopify Apps with Express

143 lines (121 loc) 3.93 kB
import {Session, Shopify, InvalidJwtError} from '@shopify/shopify-api'; import {Request, Response, NextFunction} from 'express'; import {redirectToAuth} from '../redirect-to-auth'; import {ApiAndConfigParams} from '../types'; import {redirectOutOfApp} from '../redirect-out-of-app'; import {ValidateAuthenticatedSessionMiddleware} from './types'; import {hasValidAccessToken} from './has-valid-access-token'; type validateAuthenticatedSessionParams = ApiAndConfigParams; export function validateAuthenticatedSession({ api, config, }: validateAuthenticatedSessionParams): ValidateAuthenticatedSessionMiddleware { return function validateAuthenticatedSession() { return async (req: Request, res: Response, next: NextFunction) => { config.logger.debug('Running validateAuthenticatedSession'); let sessionId: string | undefined; try { sessionId = await api.session.getCurrentId({ isOnline: config.useOnlineTokens, rawRequest: req, rawResponse: res, }); } catch (error) { config.logger.error( `Error when loading session from storage: ${error}`, ); handleSessionError(req, res, error); return undefined; } let session: Session | undefined; if (sessionId) { try { session = await config.sessionStorage.loadSession(sessionId); } catch (error) { config.logger.error( `Error when loading session from storage: ${error}`, ); res.status(500); res.send(error.message); return undefined; } } let shop = api.utils.sanitizeShop(req.query.shop as string) || session?.shop; if (session && shop && session.shop !== shop) { config.logger.debug( 'Found a session for a different shop in the request', {currentShop: session.shop, requestShop: shop}, ); return redirectToAuth({req, res, api, config}); } if (session) { config.logger.debug('Request session found and loaded', { shop: session.shop, }); if (session.isActive(api.config.scopes)) { config.logger.debug('Request session exists and is active', { shop: session.shop, }); if (await hasValidAccessToken(api, session)) { config.logger.debug('Request session has a valid access token', { shop: session.shop, }); res.locals.shopify = { ...res.locals.shopify, session, }; return next(); } } } const bearerPresent = req.headers.authorization?.match(/Bearer (.*)/); if (bearerPresent) { if (!shop) { shop = await setShopFromSessionOrToken( api, session, bearerPresent[1], ); } } const redirectUri = `${config.auth.path}?shop=${shop}`; config.logger.info( `Session was not valid. Redirecting to ${redirectUri}`, {shop}, ); return redirectOutOfApp({api, config})({ req, res, redirectUri, shop: shop!, }); }; }; } function handleSessionError(_req: Request, res: Response, error: Error) { switch (true) { case error instanceof InvalidJwtError: res.status(401); res.send(error.message); break; default: res.status(500); res.send(error.message); break; } } async function setShopFromSessionOrToken( api: Shopify, session: Session | undefined, token: string, ): Promise<string | undefined> { let shop: string | undefined; if (session) { shop = session.shop; } else if (api.config.isEmbeddedApp) { const payload = await api.session.decodeSessionToken(token); shop = payload.dest.replace('https://', ''); } return shop; }