{"version":3,"file":"validate-authenticated-session.mjs","sources":["../../../../src/middlewares/validate-authenticated-session.ts"],"sourcesContent":["import {Session, Shopify, InvalidJwtError} from '@shopify/shopify-api';\nimport {Request, Response, NextFunction} from 'express';\n\nimport {redirectToAuth} from '../redirect-to-auth';\nimport {ApiAndConfigParams} from '../types';\nimport {redirectOutOfApp} from '../redirect-out-of-app';\nimport {ensureOfflineTokenIsNotExpired} from '../helpers/index';\nimport {getSessionToken} from '../helpers/get-session-token';\nimport {respondToInvalidSessionToken} from '../helpers/respond-to-invalid-session-token';\n\nimport {ValidateAuthenticatedSessionMiddleware} from './types';\nimport {hasValidAccessToken} from './has-valid-access-token';\nimport {performTokenExchange} from './perform-token-exchange';\n\ntype validateAuthenticatedSessionParams = ApiAndConfigParams;\n\nexport function validateAuthenticatedSession({\n  api,\n  config,\n}: validateAuthenticatedSessionParams): ValidateAuthenticatedSessionMiddleware {\n  return function validateAuthenticatedSession() {\n    return async (req: Request, res: Response, next: NextFunction) => {\n      config.logger.debug('Running validateAuthenticatedSession');\n\n      // One branch, up front: embedded apps that opted into token exchange use\n      // it; everything else uses the legacy Auth Code flow. The two paths are\n      // fully independent below.\n      const useTokenExchange =\n        config.future?.tokenExchange && api.config.isEmbeddedApp;\n\n      if (useTokenExchange) {\n        return validateWithTokenExchange({req, res, next, api, config});\n      }\n\n      return validateWithAuthCodeFlow({req, res, next, api, config});\n    };\n  };\n}\n\ninterface StrategyParams extends ApiAndConfigParams {\n  req: Request;\n  res: Response;\n  next: NextFunction;\n}\n\nasync function validateWithTokenExchange({\n  req,\n  res,\n  next,\n  api,\n  config,\n}: StrategyParams): Promise<void> {\n  const sessionToken = getSessionToken(req);\n\n  if (!sessionToken) {\n    // No session token means no Authorization header, so this is always a\n    // document request and respondToInvalidSessionToken serves the App Bridge\n    // bounce (retryRequest only applies to fetch requests).\n    config.logger.debug('No session token found for token exchange');\n    respondToInvalidSessionToken({\n      api,\n      req,\n      res,\n      message: 'No session token found',\n    });\n    return;\n  }\n\n  await performTokenExchange({req, res, next, api, config, sessionToken});\n}\n\nasync function validateWithAuthCodeFlow({\n  req,\n  res,\n  next,\n  api,\n  config,\n}: StrategyParams): Promise<unknown> {\n  let sessionId: string | undefined;\n  try {\n    sessionId = await api.session.getCurrentId({\n      isOnline: config.useOnlineTokens,\n      rawRequest: req,\n      rawResponse: res,\n    });\n  } catch (error) {\n    config.logger.error(`Error when loading session from storage: ${error}`);\n\n    handleSessionError(req, res, error);\n    return undefined;\n  }\n\n  let session: Session | undefined;\n  if (sessionId) {\n    try {\n      session = await config.sessionStorage.loadSession(sessionId);\n    } catch (error) {\n      config.logger.error(`Error when loading session from storage: ${error}`);\n\n      res.status(500);\n      res.send(error.message);\n      return undefined;\n    }\n  }\n\n  let shop = api.utils.sanitizeShop(req.query.shop as string) || session?.shop;\n\n  if (\n    session &&\n    !config.useOnlineTokens &&\n    config.future?.expiringOfflineAccessTokens\n  ) {\n    try {\n      session = await ensureOfflineTokenIsNotExpired({api, config}, session);\n    } catch (error) {\n      config.logger.error(`Failed to refresh offline access token: ${error}`, {\n        shop: session.shop,\n      });\n    }\n  }\n\n  if (session && shop && session.shop !== shop) {\n    config.logger.debug('Found a session for a different shop in the request', {\n      currentShop: session.shop,\n      requestShop: shop,\n    });\n\n    return redirectToAuth({req, res, api, config});\n  }\n\n  if (session) {\n    config.logger.debug('Request session found and loaded', {\n      shop: session.shop,\n    });\n\n    if (session.isActive(api.config.scopes)) {\n      config.logger.debug('Request session exists and is active', {\n        shop: session.shop,\n      });\n\n      let hasValidToken: boolean;\n      try {\n        hasValidToken = await hasValidAccessToken(api, session);\n      } catch (error) {\n        config.logger.error(`Could not check if session was valid: ${error}`, {\n          shop: session.shop,\n        });\n        hasValidToken = false;\n      }\n\n      if (hasValidToken) {\n        config.logger.debug('Request session has a valid access token', {\n          shop: session.shop,\n        });\n\n        res.locals.shopify = {\n          ...res.locals.shopify,\n          session,\n        };\n        return next();\n      }\n    }\n  }\n\n  const bearerPresent = req.headers.authorization?.match(/Bearer (.*)/);\n  if (bearerPresent) {\n    if (!shop) {\n      shop = await setShopFromSessionOrToken(api, session, bearerPresent[1]);\n    }\n  }\n\n  const redirectUri = `${config.auth.path}?shop=${shop}`;\n  config.logger.info(`Session was not valid. Redirecting to ${redirectUri}`, {\n    shop,\n  });\n\n  return redirectOutOfApp({api, config})({\n    req,\n    res,\n    redirectUri,\n    shop: shop!,\n  });\n}\n\nfunction handleSessionError(_req: Request, res: Response, error: Error) {\n  switch (true) {\n    case error instanceof InvalidJwtError:\n      res.status(401);\n      res.send(error.message);\n      break;\n    default:\n      res.status(500);\n      res.send(error.message);\n      break;\n  }\n}\n\nasync function setShopFromSessionOrToken(\n  api: Shopify,\n  session: Session | undefined,\n  token: string,\n): Promise<string | undefined> {\n  let shop: string | undefined;\n\n  if (session) {\n    shop = session.shop;\n  } else if (api.config.isEmbeddedApp) {\n    const payload = await api.session.decodeSessionToken(token);\n    shop = payload.dest.replace('https://', '');\n  }\n  return shop;\n}\n"],"names":[],"mappings":"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"}