{"version":3,"file":"authenticate.mjs","sources":["../../../../../../src/server/authenticate/admin/authenticate.ts"],"sourcesContent":["import {JwtPayload, Session} from '@shopify/shopify-api';\n\nimport type {BasicParams} from '../../types';\nimport {AppDistribution} from '../../types';\nimport type {AppConfigArg} from '../../config-types';\nimport {\n  getSessionTokenHeader,\n  ensureCORSHeadersFactory,\n  getSessionTokenFromUrlParam,\n  respondToBotRequest,\n  respondToOptionsRequest,\n  validateSessionToken,\n  getShopFromRequest,\n} from '../helpers';\n\nimport {\n  cancelBillingFactory,\n  requestBillingFactory,\n  requireBillingFactory,\n  checkBillingFactory,\n  createUsageRecordFactory,\n  updateUsageCappedAmountFactory,\n} from './billing';\nimport type {\n  AdminContext,\n  AuthenticateAdmin,\n  EmbeddedAdminContext,\n  NonEmbeddedAdminContext,\n} from './types';\nimport {\n  createAdminApiContext,\n  ensureAppIsEmbeddedIfRequired,\n  ensureSessionTokenSearchParamIfRequired,\n  redirectFactory,\n  renderAppBridge,\n  validateShopAndHostParams,\n} from './helpers';\nimport {AuthorizationStrategy} from './strategies/types';\nimport {scopesApiFactory} from './scope/factory';\n\nexport interface SessionTokenContext {\n  shop: string;\n  sessionId?: string;\n  sessionToken?: string;\n  payload?: JwtPayload;\n}\n\ninterface AuthStrategyParams extends BasicParams {\n  strategy: AuthorizationStrategy;\n}\n\nexport function authStrategyFactory<ConfigArg extends AppConfigArg>({\n  strategy,\n  ...params\n}: AuthStrategyParams): AuthenticateAdmin<ConfigArg> {\n  const {api, logger, config} = params;\n\n  async function respondToBouncePageRequest(request: Request) {\n    const url = new URL(request.url);\n\n    if (url.pathname.endsWith(config.auth.patchSessionTokenPath)) {\n      logger.debug('Rendering bounce page', {\n        shop: getShopFromRequest(request),\n      });\n      throw renderAppBridge({config, logger, api}, request);\n    }\n  }\n\n  async function respondToExitIframeRequest(request: Request) {\n    const url = new URL(request.url);\n\n    if (url.pathname.endsWith(config.auth.exitIframePath)) {\n      const destination = url.searchParams.get('exitIframe')!;\n\n      logger.debug('Rendering exit iframe page', {\n        shop: getShopFromRequest(request),\n        destination,\n      });\n      throw renderAppBridge({config, logger, api}, request, {url: destination});\n    }\n  }\n\n  type AdminContextBase =\n    | EmbeddedAdminContext<ConfigArg>\n    | NonEmbeddedAdminContext<ConfigArg>;\n\n  function createContext(\n    request: Request,\n    session: Session,\n    authStrategy: AuthorizationStrategy,\n    sessionToken?: JwtPayload,\n  ): AdminContext<ConfigArg> {\n    let context: AdminContextBase = {\n      admin: createAdminApiContext(\n        session,\n        params,\n        authStrategy.handleClientError(request),\n      ),\n      billing: {\n        require: requireBillingFactory(params, request, session),\n        check: checkBillingFactory(params, request, session),\n        request: requestBillingFactory(params, request, session),\n        cancel: cancelBillingFactory(params, request, session),\n        createUsageRecord: createUsageRecordFactory(params, request, session),\n        updateUsageCappedAmount: updateUsageCappedAmountFactory(\n          params,\n          request,\n          session,\n        ),\n      },\n\n      session,\n      cors: ensureCORSHeadersFactory(params, request),\n    };\n\n    context = addEmbeddedFeatures(context, request, session, sessionToken);\n    context = addScopesFeatures(context);\n\n    return context as AdminContext<ConfigArg>;\n  }\n\n  function addEmbeddedFeatures(\n    context: AdminContextBase,\n    request: Request,\n    session: Session,\n    sessionToken?: JwtPayload,\n  ) {\n    if (config.distribution === AppDistribution.ShopifyAdmin) {\n      return context;\n    }\n    return {\n      ...context,\n      sessionToken,\n      redirect: redirectFactory(params, request, session.shop),\n    };\n  }\n\n  function addScopesFeatures(context: AdminContextBase) {\n    return {\n      ...context,\n      scopes: scopesApiFactory(params, context.session, context.admin),\n    };\n  }\n\n  return async function authenticateAdmin(request: Request) {\n    try {\n      respondToBotRequest(params, request);\n      respondToOptionsRequest(params, request);\n      await respondToBouncePageRequest(request);\n      await respondToExitIframeRequest(request);\n\n      // If this is a valid request, but it doesn't have a session token header, this is a document request. We need to\n      // ensure we're embedded if needed and we have the information needed to load the session.\n      if (!getSessionTokenHeader(request)) {\n        validateShopAndHostParams(params, request);\n        await ensureAppIsEmbeddedIfRequired(params, request);\n        await ensureSessionTokenSearchParamIfRequired(params, request);\n      }\n\n      logger.info('Authenticating admin request', {\n        shop: getShopFromRequest(request),\n      });\n\n      const {payload, shop, sessionId, sessionToken} =\n        await getSessionTokenContext(params, request);\n\n      logger.debug('Loading session from storage', {shop, sessionId});\n      const existingSession = sessionId\n        ? await config.sessionStorage!.loadSession(sessionId)\n        : undefined;\n\n      const session = await strategy.authenticate(request, {\n        session: existingSession,\n        sessionToken,\n        shop,\n      });\n\n      return createContext(request, session, strategy, payload);\n    } catch (errorOrResponse) {\n      if (errorOrResponse instanceof Response) {\n        logger.debug('Authenticate returned a response', {\n          shop: getShopFromRequest(request),\n        });\n        ensureCORSHeadersFactory(params, request)(errorOrResponse);\n      }\n\n      throw errorOrResponse;\n    }\n  };\n}\n\nasync function getSessionTokenContext(\n  params: BasicParams,\n  request: Request,\n): Promise<SessionTokenContext> {\n  const {api, config, logger} = params;\n\n  const headerSessionToken = getSessionTokenHeader(request);\n  const searchParamSessionToken = getSessionTokenFromUrlParam(request);\n  const sessionToken = (headerSessionToken || searchParamSessionToken)!;\n\n  logger.debug('Attempting to authenticate session token', {\n    shop: getShopFromRequest(request),\n    sessionToken: JSON.stringify({\n      header: headerSessionToken,\n      search: searchParamSessionToken,\n    }),\n  });\n\n  if (config.distribution !== AppDistribution.ShopifyAdmin) {\n    const payload = await validateSessionToken(params, request, sessionToken);\n    const dest = new URL(payload.dest);\n    const shop = dest.hostname;\n\n    logger.debug('Session token is valid - authenticated', {shop, payload});\n    const sessionId = config.useOnlineTokens\n      ? api.session.getJwtSessionId(shop, payload.sub)\n      : api.session.getOfflineId(shop);\n\n    return {shop, payload, sessionId, sessionToken};\n  }\n\n  const url = new URL(request.url);\n  const shop = url.searchParams.get('shop')!;\n\n  const sessionId = await api.session.getCurrentId({\n    isOnline: config.useOnlineTokens,\n    rawRequest: request,\n  });\n\n  return {shop, sessionId, payload: undefined, sessionToken};\n}\n"],"names":[],"mappings":"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"}