UNPKG

@shopify/shopify-app-react-router

Version:

Shopify React Router - to simplify the building of Shopify Apps with React Router

1 lines 11.9 kB
{"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 === 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 === 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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"}