@shopify/shopify-app-remix 4.2.1 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/server/authenticate/admin/authenticate.js +1 -3
- package/dist/cjs/server/authenticate/admin/authenticate.js.map +1 -1
- package/dist/cjs/server/authenticate/admin/helpers/validate-redirect-url.js +1 -1
- package/dist/cjs/server/authenticate/admin/helpers/validate-redirect-url.js.map +1 -1
- package/dist/cjs/server/authenticate/public/appProxy/authenticate.js +9 -3
- package/dist/cjs/server/authenticate/public/appProxy/authenticate.js.map +1 -1
- package/dist/cjs/server/authenticate/webhooks/authenticate.js +0 -1
- package/dist/cjs/server/authenticate/webhooks/authenticate.js.map +1 -1
- package/dist/cjs/server/shopify-app.js +1 -1
- package/dist/cjs/server/shopify-app.js.map +1 -1
- package/dist/cjs/server/test-helpers/test-config.js.map +1 -1
- package/dist/cjs/server/version.js +1 -1
- package/dist/cjs/server/version.js.map +1 -1
- package/dist/esm/server/authenticate/admin/authenticate.mjs +1 -3
- package/dist/esm/server/authenticate/admin/authenticate.mjs.map +1 -1
- package/dist/esm/server/authenticate/admin/helpers/validate-redirect-url.mjs +1 -1
- package/dist/esm/server/authenticate/admin/helpers/validate-redirect-url.mjs.map +1 -1
- package/dist/esm/server/authenticate/public/appProxy/authenticate.mjs +9 -3
- package/dist/esm/server/authenticate/public/appProxy/authenticate.mjs.map +1 -1
- package/dist/esm/server/authenticate/webhooks/authenticate.mjs +0 -1
- package/dist/esm/server/authenticate/webhooks/authenticate.mjs.map +1 -1
- package/dist/esm/server/shopify-app.mjs +1 -1
- package/dist/esm/server/shopify-app.mjs.map +1 -1
- package/dist/esm/server/test-helpers/test-config.mjs.map +1 -1
- package/dist/esm/server/version.mjs +1 -1
- package/dist/esm/server/version.mjs.map +1 -1
- package/dist/ts/server/authenticate/admin/authenticate.d.ts.map +1 -1
- package/dist/ts/server/authenticate/webhooks/authenticate.d.ts.map +1 -1
- package/dist/ts/server/authenticate/webhooks/types.d.ts +0 -19
- package/dist/ts/server/authenticate/webhooks/types.d.ts.map +1 -1
- package/dist/ts/server/config-types.d.ts +1 -1
- package/dist/ts/server/shopify-app.d.ts.map +1 -1
- package/dist/ts/server/test-helpers/test-config.d.ts.map +1 -1
- package/dist/ts/server/version.d.ts +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +11 -12
- package/src/server/__test-helpers/const.ts +1 -1
- package/src/server/__test-helpers/expect-admin-api-client.ts +1 -1
- package/src/server/__test-helpers/expect-storefront-api-client.ts +1 -1
- package/src/server/__test-helpers/mock-graphql-request.ts +2 -2
- package/src/server/__test-helpers/request-mock.ts +1 -1
- package/src/server/__test-helpers/setup-valid-request.ts +1 -2
- package/src/server/__test-helpers/test-config.ts +1 -1
- package/src/server/authenticate/admin/authenticate.ts +3 -6
- package/src/server/authenticate/admin/helpers/validate-redirect-url.ts +1 -1
- package/src/server/authenticate/public/appProxy/authenticate.ts +9 -12
- package/src/server/authenticate/public/customer-account/authenticate.public.customerAccount.doc.example.ts +2 -1
- package/src/server/authenticate/webhooks/authenticate.ts +0 -1
- package/src/server/authenticate/webhooks/authenticate.webhooks.doc.example.ts +6 -6
- package/src/server/authenticate/webhooks/types.ts +1 -22
- package/src/server/config-types.ts +1 -1
- package/src/server/shopify-app.ts +2 -4
- package/src/server/test-helpers/test-config.ts +3 -3
- package/src/server/version.ts +1 -1
|
@@ -92,10 +92,8 @@ function authStrategyFactory({ strategy, ...params }) {
|
|
|
92
92
|
await ensureAppIsEmbeddedIfRequired.ensureAppIsEmbeddedIfRequired(params, request);
|
|
93
93
|
await ensureSessionTokenSearchParamIfRequired.ensureSessionTokenSearchParamIfRequired(params, request);
|
|
94
94
|
}
|
|
95
|
-
logger.info('Authenticating admin request', {
|
|
96
|
-
shop: getShopFromRequest.getShopFromRequest(request),
|
|
97
|
-
});
|
|
98
95
|
const { payload, shop, sessionId, sessionToken } = await getSessionTokenContext(params, request);
|
|
96
|
+
logger.info('Authenticating admin request', { shop });
|
|
99
97
|
logger.debug('Loading session from storage', { shop, sessionId });
|
|
100
98
|
const existingSession = sessionId
|
|
101
99
|
? await config.sessionStorage.loadSession(sessionId)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authenticate.js","sources":["../../../../../../src/server/authenticate/admin/authenticate.ts"],"sourcesContent":["import {JwtPayload, Session} from '@shopify/shopify-api';\n\nimport type {BasicParams} 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.isEmbeddedApp) {\n return {\n ...context,\n sessionToken,\n redirect: redirectFactory(params, request, session.shop),\n };\n }\n return context;\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 await strategy.respondToOAuthRequests(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.isEmbeddedApp) {\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":["getShopFromRequest","renderAppBridge","request","createAdminApiContext","requireBillingFactory","checkBillingFactory","requestBillingFactory","cancelBillingFactory","createUsageRecordFactory","updateUsageCappedAmountFactory","ensureCORSHeadersFactory","redirectFactory","scopesApiFactory","respondToBotRequest","respondToOptionsRequest","getSessionTokenHeader","validateShopAndHostParams","ensureAppIsEmbeddedIfRequired","ensureSessionTokenSearchParamIfRequired","getSessionTokenFromUrlParam","validateSessionToken"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"authenticate.js","sources":["../../../../../../src/server/authenticate/admin/authenticate.ts"],"sourcesContent":["import {JwtPayload, Session} from '@shopify/shopify-api';\n\nimport type {BasicParams} 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> | 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.isEmbeddedApp) {\n return {\n ...context,\n sessionToken,\n redirect: redirectFactory(params, request, session.shop),\n };\n }\n return context;\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 await strategy.respondToOAuthRequests(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 const {payload, shop, sessionId, sessionToken} =\n await getSessionTokenContext(params, request);\n\n logger.info('Authenticating admin request', {shop});\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.isEmbeddedApp) {\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":["getShopFromRequest","renderAppBridge","request","createAdminApiContext","requireBillingFactory","checkBillingFactory","requestBillingFactory","cancelBillingFactory","createUsageRecordFactory","updateUsageCappedAmountFactory","ensureCORSHeadersFactory","redirectFactory","scopesApiFactory","respondToBotRequest","respondToOptionsRequest","getSessionTokenHeader","validateShopAndHostParams","ensureAppIsEmbeddedIfRequired","ensureSessionTokenSearchParamIfRequired","getSessionTokenFromUrlParam","validateSessionToken"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAkDM,SAAU,mBAAmB,CAAiC,EAClE,QAAQ,EACR,GAAG,MAAM,EACU,EAAA;IACnB,MAAM,EAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAC,GAAG,MAAM;IAEpC,eAAe,0BAA0B,CAAC,OAAgB,EAAA;QACxD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC;QAEhC,IAAI,GAAG,CAAC,QAAQ,KAAK,MAAM,CAAC,IAAI,CAAC,qBAAqB,EAAE;AACtD,YAAA,MAAM,CAAC,KAAK,CAAC,uBAAuB,EAAE;AACpC,gBAAA,IAAI,EAAEA,qCAAkB,CAAC,OAAO,CAAC;AAClC,aAAA,CAAC;AACF,YAAA,MAAMC,+BAAe,CAAC,EAAC,MAAmB,CAAC,EAAE,OAAO,CAAC;QACvD;IACF;IAEA,eAAe,0BAA0B,CAAC,OAAgB,EAAA;QACxD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC;QAEhC,IAAI,GAAG,CAAC,QAAQ,KAAK,MAAM,CAAC,IAAI,CAAC,cAAc,EAAE;YAC/C,MAAM,WAAW,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,YAAY,CAAE;AAEvD,YAAA,MAAM,CAAC,KAAK,CAAC,4BAA4B,EAAE;AACzC,gBAAA,IAAI,EAAED,qCAAkB,CAAC,OAAO,CAAC;gBACjC,WAAW;AACZ,aAAA,CAAC;AACF,YAAA,MAAMC,+BAAe,CAAC,EAAC,MAAmB,CAAC,EAAE,OAAO,EAAE,EAAC,GAAG,EAAE,WAAW,EAAC,CAAC;QAC3E;IACF;IAKA,SAAS,aAAa,CACpBC,SAAgB,EAChB,OAAgB,EAChB,YAAmC,EACnC,YAAyB,EAAA;AAEzB,QAAA,IAAI,OAAO,GAAqB;AAC9B,YAAA,KAAK,EAAEC,2CAAqB,CAC1B,OAAO,EACP,MAAM,EACN,YAAY,CAAC,iBAAiB,CAACD,SAAO,CAAC,CACxC;AACD,YAAA,OAAO,EAAE;gBACP,OAAO,EAAEE,+BAAqB,CAAC,MAAM,EAAEF,SAAO,EAAE,OAAO,CAAC;gBACxD,KAAK,EAAEG,yBAAmB,CAAC,MAAM,EAAEH,SAAO,EAAE,OAAO,CAAC;gBACpD,OAAO,EAAEI,6BAAqB,CAAC,MAAM,EAAEJ,SAAO,EAAE,OAAO,CAAC;gBACxD,MAAM,EAAEK,2BAAoB,CAAC,MAAM,EAAEL,SAAO,EAAE,OAAO,CAAC;gBACtD,iBAAiB,EAAEM,0CAAwB,CAAC,MAAM,EAAEN,SAAO,EAAE,OAAO,CAAC;gBACrE,uBAAuB,EAAEO,kEAA8B,CACrD,MAAM,EACNP,SAAO,EACP,OAAO,CACR;AACF,aAAA;YAED,OAAO;AACP,YAAA,IAAI,EAAEQ,0CAAwB,CAAC,MAAM,EAAER,SAAO,CAAC;SAChD;QAED,OAAO,GAAG,mBAAmB,CAAC,OAAO,EAAEA,SAAO,EAAE,OAAO,EAAE,YAAY,CAAC;AACtE,QAAA,OAAO,GAAG,iBAAiB,CAAC,OAAO,CAAC;AAEpC,QAAA,OAAO,OAAkC;IAC3C;IAEA,SAAS,mBAAmB,CAC1B,OAAyB,EACzB,OAAgB,EAChB,OAAgB,EAChB,YAAyB,EAAA;AAEzB,QAAA,IAAI,MAAM,CAAC,aAAa,EAAE;YACxB,OAAO;AACL,gBAAA,GAAG,OAAO;gBACV,YAAY;gBACZ,QAAQ,EAAES,wBAAe,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC;aACzD;QACH;AACA,QAAA,OAAO,OAAO;IAChB;IAEA,SAAS,iBAAiB,CAAC,OAAyB,EAAA;QAClD,OAAO;AACL,YAAA,GAAG,OAAO;AACV,YAAA,MAAM,EAAEC,wBAAgB,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC;SACjE;IACH;AAEA,IAAA,OAAO,eAAe,iBAAiB,CAAC,OAAgB,EAAA;AACtD,QAAA,IAAI;AACF,YAAAC,oCAAmB,CAAC,MAAM,EAAE,OAAO,CAAC;AACpC,YAAAC,+CAAuB,CAAC,MAAM,EAAE,OAAO,CAAC;AACxC,YAAA,MAAM,0BAA0B,CAAC,OAAO,CAAC;AACzC,YAAA,MAAM,0BAA0B,CAAC,OAAO,CAAC;AACzC,YAAA,MAAM,QAAQ,CAAC,sBAAsB,CAAC,OAAO,CAAC;;;AAI9C,YAAA,IAAI,CAACC,2CAAqB,CAAC,OAAO,CAAC,EAAE;AACnC,gBAAAC,mDAAyB,CAAC,MAAM,EAAE,OAAO,CAAC;AAC1C,gBAAA,MAAMC,2DAA6B,CAAC,MAAM,EAAE,OAAO,CAAC;AACpD,gBAAA,MAAMC,+EAAuC,CAAC,MAAM,EAAE,OAAO,CAAC;YAChE;AAEA,YAAA,MAAM,EAAC,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,YAAY,EAAC,GAC5C,MAAM,sBAAsB,CAAC,MAAM,EAAE,OAAO,CAAC;YAE/C,MAAM,CAAC,IAAI,CAAC,8BAA8B,EAAE,EAAC,IAAI,EAAC,CAAC;YAEnD,MAAM,CAAC,KAAK,CAAC,8BAA8B,EAAE,EAAC,IAAI,EAAE,SAAS,EAAC,CAAC;YAC/D,MAAM,eAAe,GAAG;kBACpB,MAAM,MAAM,CAAC,cAAe,CAAC,WAAW,CAAC,SAAS;kBAClD,SAAS;YAEb,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,YAAY,CAAC,OAAO,EAAE;AACnD,gBAAA,OAAO,EAAE,eAAe;gBACxB,YAAY;gBACZ,IAAI;AACL,aAAA,CAAC;YAEF,OAAO,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC;QAC3D;QAAE,OAAO,eAAe,EAAE;AACxB,YAAA,IAAI,eAAe,YAAY,QAAQ,EAAE;AACvC,gBAAA,MAAM,CAAC,KAAK,CAAC,kCAAkC,EAAE;AAC/C,oBAAA,IAAI,EAAElB,qCAAkB,CAAC,OAAO,CAAC;AAClC,iBAAA,CAAC;gBACFU,0CAAwB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,eAAe,CAAC;YAC5D;AAEA,YAAA,MAAM,eAAe;QACvB;AACF,IAAA,CAAC;AACH;AAEA,eAAe,sBAAsB,CACnC,MAAmB,EACnB,OAAgB,EAAA;IAEhB,MAAM,EAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAC,GAAG,MAAM;AAEpC,IAAA,MAAM,kBAAkB,GAAGK,2CAAqB,CAAC,OAAO,CAAC;AACzD,IAAA,MAAM,uBAAuB,GAAGI,iDAA2B,CAAC,OAAO,CAAC;AACpE,IAAA,MAAM,YAAY,IAAI,kBAAkB,IAAI,uBAAuB,CAAE;AAErE,IAAA,MAAM,CAAC,KAAK,CAAC,0CAA0C,EAAE;AACvD,QAAA,IAAI,EAAEnB,qCAAkB,CAAC,OAAO,CAAC;AACjC,QAAA,YAAY,EAAE,IAAI,CAAC,SAAS,CAAC;AAC3B,YAAA,MAAM,EAAE,kBAAkB;AAC1B,YAAA,MAAM,EAAE,uBAAuB;SAChC,CAAC;AACH,KAAA,CAAC;AAEF,IAAA,IAAI,MAAM,CAAC,aAAa,EAAE;QACxB,MAAM,OAAO,GAAG,MAAMoB,yCAAoB,CAAC,MAAM,EAAE,OAAO,EAAE,YAAY,CAAC;QACzE,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC;AAClC,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ;QAE1B,MAAM,CAAC,KAAK,CAAC,wCAAwC,EAAE,EAAC,IAAI,EAAE,OAAO,EAAC,CAAC;AACvE,QAAA,MAAM,SAAS,GAAG,MAAM,CAAC;AACvB,cAAE,GAAG,CAAC,OAAO,CAAC,eAAe,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG;cAC7C,GAAG,CAAC,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC;QAElC,OAAO,EAAC,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,YAAY,EAAC;IACjD;IAEA,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC;IAChC,MAAM,IAAI,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAE;IAE1C,MAAM,SAAS,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,YAAY,CAAC;QAC/C,QAAQ,EAAE,MAAM,CAAC,eAAe;AAChC,QAAA,UAAU,EAAE,OAAO;AACpB,KAAA,CAAC;IAEF,OAAO,EAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,YAAY,EAAC;AAC5D;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate-redirect-url.js","sources":["../../../../../../../src/server/authenticate/admin/helpers/validate-redirect-url.ts"],"sourcesContent":["import {ShopifyError} from '@shopify/shopify-api';\n\ninterface Options {\n requireSSL?: boolean;\n throwOnInvalid?: boolean;\n}\n\ntype SanitizedRedirectUrl<OptionsArg extends Options> =\n OptionsArg['throwOnInvalid'] extends false ? URL | undefined : URL;\n\nconst FILE_URI_MATCH = /\\/\\/\\//;\nconst INVALID_RELATIVE_URL = /[/\\\\][/\\\\]/;\nconst WHITESPACE_CHARACTER = /\\s/;\nconst VALID_PROTOCOLS = ['https:', 'http:'];\n\nfunction isSafe(\n domain: string,\n redirectUrl: unknown,\n requireSSL: boolean | undefined = true,\n): redirectUrl is string {\n if (typeof redirectUrl !== 'string') {\n return false;\n }\n\n if (\n FILE_URI_MATCH.test(redirectUrl) ||\n WHITESPACE_CHARACTER.test(redirectUrl)\n ) {\n return false;\n }\n\n let url: URL;\n\n try {\n url = new URL(redirectUrl, domain);\n } catch (
|
|
1
|
+
{"version":3,"file":"validate-redirect-url.js","sources":["../../../../../../../src/server/authenticate/admin/helpers/validate-redirect-url.ts"],"sourcesContent":["import {ShopifyError} from '@shopify/shopify-api';\n\ninterface Options {\n requireSSL?: boolean;\n throwOnInvalid?: boolean;\n}\n\ntype SanitizedRedirectUrl<OptionsArg extends Options> =\n OptionsArg['throwOnInvalid'] extends false ? URL | undefined : URL;\n\nconst FILE_URI_MATCH = /\\/\\/\\//;\nconst INVALID_RELATIVE_URL = /[/\\\\][/\\\\]/;\nconst WHITESPACE_CHARACTER = /\\s/;\nconst VALID_PROTOCOLS = ['https:', 'http:'];\n\nfunction isSafe(\n domain: string,\n redirectUrl: unknown,\n requireSSL: boolean | undefined = true,\n): redirectUrl is string {\n if (typeof redirectUrl !== 'string') {\n return false;\n }\n\n if (\n FILE_URI_MATCH.test(redirectUrl) ||\n WHITESPACE_CHARACTER.test(redirectUrl)\n ) {\n return false;\n }\n\n let url: URL;\n\n try {\n url = new URL(redirectUrl, domain);\n } catch (_error) {\n return false;\n }\n\n if (INVALID_RELATIVE_URL.test(url.pathname)) {\n return false;\n }\n\n if (!VALID_PROTOCOLS.includes(url.protocol)) {\n return false;\n }\n\n if (requireSSL && url.protocol !== 'https:') {\n return false;\n }\n\n return true;\n}\n\nexport function sanitizeRedirectUrl<OptionsArg extends Options>(\n domain: string,\n redirectUrl: unknown,\n options: OptionsArg = {} as OptionsArg,\n): SanitizedRedirectUrl<OptionsArg> {\n if (isSafe(domain, redirectUrl, options.requireSSL)) {\n return new URL(redirectUrl, domain) as SanitizedRedirectUrl<OptionsArg>;\n } else if (options.throwOnInvalid === false) {\n return undefined as SanitizedRedirectUrl<OptionsArg>;\n } else {\n throw new ShopifyError('Invalid URL. Refusing to redirect');\n }\n}\n"],"names":["ShopifyError"],"mappings":";;;;AAUA,MAAM,cAAc,GAAG,QAAQ;AAC/B,MAAM,oBAAoB,GAAG,YAAY;AACzC,MAAM,oBAAoB,GAAG,IAAI;AACjC,MAAM,eAAe,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC;AAE3C,SAAS,MAAM,CACb,MAAc,EACd,WAAoB,EACpB,aAAkC,IAAI,EAAA;AAEtC,IAAA,IAAI,OAAO,WAAW,KAAK,QAAQ,EAAE;AACnC,QAAA,OAAO,KAAK;IACd;AAEA,IAAA,IACE,cAAc,CAAC,IAAI,CAAC,WAAW,CAAC;AAChC,QAAA,oBAAoB,CAAC,IAAI,CAAC,WAAW,CAAC,EACtC;AACA,QAAA,OAAO,KAAK;IACd;AAEA,IAAA,IAAI,GAAQ;AAEZ,IAAA,IAAI;QACF,GAAG,GAAG,IAAI,GAAG,CAAC,WAAW,EAAE,MAAM,CAAC;IACpC;IAAE,OAAO,MAAM,EAAE;AACf,QAAA,OAAO,KAAK;IACd;IAEA,IAAI,oBAAoB,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE;AAC3C,QAAA,OAAO,KAAK;IACd;IAEA,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE;AAC3C,QAAA,OAAO,KAAK;IACd;IAEA,IAAI,UAAU,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,EAAE;AAC3C,QAAA,OAAO,KAAK;IACd;AAEA,IAAA,OAAO,IAAI;AACb;AAEM,SAAU,mBAAmB,CACjC,MAAc,EACd,WAAoB,EACpB,UAAsB,EAAgB,EAAA;IAEtC,IAAI,MAAM,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,CAAC,UAAU,CAAC,EAAE;AACnD,QAAA,OAAO,IAAI,GAAG,CAAC,WAAW,EAAE,MAAM,CAAqC;IACzE;AAAO,SAAA,IAAI,OAAO,CAAC,cAAc,KAAK,KAAK,EAAE;AAC3C,QAAA,OAAO,SAA6C;IACtD;SAAO;AACL,QAAA,MAAM,IAAIA,uBAAY,CAAC,mCAAmC,CAAC;IAC7D;AACF;;;;"}
|
|
@@ -68,7 +68,9 @@ async function validateAppProxyHmac(params, url) {
|
|
|
68
68
|
if (!searchParams.get('index')) {
|
|
69
69
|
searchParams.delete('index');
|
|
70
70
|
}
|
|
71
|
-
let isValid = await api.utils.validateHmac(
|
|
71
|
+
let isValid = await api.utils.validateHmac(searchParams, {
|
|
72
|
+
signator: 'appProxy',
|
|
73
|
+
});
|
|
72
74
|
if (!isValid) {
|
|
73
75
|
const cleanPath = url.pathname
|
|
74
76
|
.replace(/^\//, '')
|
|
@@ -76,10 +78,14 @@ async function validateAppProxyHmac(params, url) {
|
|
|
76
78
|
.replaceAll('/', '.');
|
|
77
79
|
const data = `routes%2F${cleanPath}`;
|
|
78
80
|
searchParams = new URLSearchParams(`?_data=${data}&${searchParams.toString().replace(/^\?/, '')}`);
|
|
79
|
-
isValid = await api.utils.validateHmac(
|
|
81
|
+
isValid = await api.utils.validateHmac(searchParams, {
|
|
82
|
+
signator: 'appProxy',
|
|
83
|
+
});
|
|
80
84
|
if (!isValid) {
|
|
81
85
|
const searchParams = new URLSearchParams(`?_data=${data}._index&${url.search.replace(/^\?/, '')}`);
|
|
82
|
-
isValid = await api.utils.validateHmac(
|
|
86
|
+
isValid = await api.utils.validateHmac(searchParams, {
|
|
87
|
+
signator: 'appProxy',
|
|
88
|
+
});
|
|
83
89
|
}
|
|
84
90
|
}
|
|
85
91
|
return isValid;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authenticate.js","sources":["../../../../../../../src/server/authenticate/public/appProxy/authenticate.ts"],"sourcesContent":["import {adminClientFactory, storefrontClientFactory} from '../../../clients';\nimport {ensureValidOfflineSession} from '../../../helpers';\nimport {BasicParams} from '../../../types';\n\nimport {\n AppProxyContext,\n AppProxyContextWithSession,\n AuthenticateAppProxy,\n LiquidResponseFunction,\n} from './types';\n\nexport function authenticateAppProxyFactory(\n params: BasicParams,\n): AuthenticateAppProxy {\n const {logger} = params;\n\n return async function authenticate(\n request: Request,\n ): Promise<AppProxyContext | AppProxyContextWithSession> {\n const url = new URL(request.url);\n const shop = url.searchParams.get('shop')!;\n logger.info('Authenticating app proxy request', {shop});\n\n if (!(await validateAppProxyHmac(params, url))) {\n logger.info('App proxy request has invalid signature', {shop});\n throw new Response(undefined, {\n status: 400,\n statusText: 'Bad Request',\n });\n }\n\n const session = await ensureValidOfflineSession(params, shop);\n\n if (!session) {\n logger.debug('Could not find offline session, returning empty context', {\n shop,\n ...Object.fromEntries(url.searchParams.entries()),\n });\n\n const context: AppProxyContext = {\n liquid,\n session: undefined,\n admin: undefined,\n storefront: undefined,\n };\n\n return context;\n }\n\n const context: AppProxyContextWithSession = {\n liquid,\n session,\n admin: adminClientFactory({params, session}),\n storefront: storefrontClientFactory({params, session}),\n };\n\n return context;\n };\n}\n\nconst liquid: LiquidResponseFunction = (body, initAndOptions) => {\n const processedBody = processLiquidBody(body);\n\n if (typeof initAndOptions !== 'object') {\n return new Response(processedBody, {\n status: initAndOptions || 200,\n headers: {\n 'Content-Type': 'application/liquid',\n },\n });\n }\n\n const {layout, ...responseInit} = initAndOptions || {};\n const responseBody =\n layout === false ? `{% layout none %} ${processedBody}` : processedBody;\n\n const headers = new Headers(responseInit.headers);\n headers.set('Content-Type', 'application/liquid');\n\n return new Response(responseBody, {\n ...responseInit,\n headers,\n });\n};\n\nasync function validateAppProxyHmac(\n params: BasicParams,\n url: URL,\n): Promise<boolean> {\n const {api, logger} = params;\n\n try {\n let searchParams = new URLSearchParams(url.search);\n if (!searchParams.get('index')) {\n searchParams.delete('index');\n }\n\n let isValid = await api.utils.validateHmac(\n
|
|
1
|
+
{"version":3,"file":"authenticate.js","sources":["../../../../../../../src/server/authenticate/public/appProxy/authenticate.ts"],"sourcesContent":["import {adminClientFactory, storefrontClientFactory} from '../../../clients';\nimport {ensureValidOfflineSession} from '../../../helpers';\nimport {BasicParams} from '../../../types';\n\nimport {\n AppProxyContext,\n AppProxyContextWithSession,\n AuthenticateAppProxy,\n LiquidResponseFunction,\n} from './types';\n\nexport function authenticateAppProxyFactory(\n params: BasicParams,\n): AuthenticateAppProxy {\n const {logger} = params;\n\n return async function authenticate(\n request: Request,\n ): Promise<AppProxyContext | AppProxyContextWithSession> {\n const url = new URL(request.url);\n const shop = url.searchParams.get('shop')!;\n logger.info('Authenticating app proxy request', {shop});\n\n if (!(await validateAppProxyHmac(params, url))) {\n logger.info('App proxy request has invalid signature', {shop});\n throw new Response(undefined, {\n status: 400,\n statusText: 'Bad Request',\n });\n }\n\n const session = await ensureValidOfflineSession(params, shop);\n\n if (!session) {\n logger.debug('Could not find offline session, returning empty context', {\n shop,\n ...Object.fromEntries(url.searchParams.entries()),\n });\n\n const context: AppProxyContext = {\n liquid,\n session: undefined,\n admin: undefined,\n storefront: undefined,\n };\n\n return context;\n }\n\n const context: AppProxyContextWithSession = {\n liquid,\n session,\n admin: adminClientFactory({params, session}),\n storefront: storefrontClientFactory({params, session}),\n };\n\n return context;\n };\n}\n\nconst liquid: LiquidResponseFunction = (body, initAndOptions) => {\n const processedBody = processLiquidBody(body);\n\n if (typeof initAndOptions !== 'object') {\n return new Response(processedBody, {\n status: initAndOptions || 200,\n headers: {\n 'Content-Type': 'application/liquid',\n },\n });\n }\n\n const {layout, ...responseInit} = initAndOptions || {};\n const responseBody =\n layout === false ? `{% layout none %} ${processedBody}` : processedBody;\n\n const headers = new Headers(responseInit.headers);\n headers.set('Content-Type', 'application/liquid');\n\n return new Response(responseBody, {\n ...responseInit,\n headers,\n });\n};\n\nasync function validateAppProxyHmac(\n params: BasicParams,\n url: URL,\n): Promise<boolean> {\n const {api, logger} = params;\n\n try {\n let searchParams = new URLSearchParams(url.search);\n if (!searchParams.get('index')) {\n searchParams.delete('index');\n }\n\n let isValid = await api.utils.validateHmac(searchParams, {\n signator: 'appProxy',\n });\n\n if (!isValid) {\n const cleanPath = url.pathname\n .replace(/^\\//, '')\n .replace(/\\/$/, '')\n .replaceAll('/', '.');\n const data = `routes%2F${cleanPath}`;\n\n searchParams = new URLSearchParams(\n `?_data=${data}&${searchParams.toString().replace(/^\\?/, '')}`,\n );\n\n isValid = await api.utils.validateHmac(searchParams, {\n signator: 'appProxy',\n });\n\n if (!isValid) {\n const searchParams = new URLSearchParams(\n `?_data=${data}._index&${url.search.replace(/^\\?/, '')}`,\n );\n\n isValid = await api.utils.validateHmac(searchParams, {\n signator: 'appProxy',\n });\n }\n }\n\n return isValid;\n } catch (error) {\n const shop = url.searchParams.get('shop')!;\n logger.info(error.message, {shop});\n throw new Response(undefined, {status: 400, statusText: 'Bad Request'});\n }\n}\n\nfunction processLiquidBody(body: string) {\n return (\n body\n // Add trailing slashes to relative form action URLs\n .replaceAll(\n /<(form[^>]+)action=\"(\\/[^\"?]+)(\\?[^\"]+)?\">/g,\n '<$1action=\"$2/$3\">',\n )\n // Add trailing slashes to relative link href URLs\n .replaceAll(/<(a[^>]+)href=\"(\\/[^\"?]+)(\\?[^\"]+)?\">/g, '<$1href=\"$2/$3\">')\n );\n}\n"],"names":["ensureValidOfflineSession","adminClientFactory","storefrontClientFactory"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authenticate.js","sources":["../../../../../../src/server/authenticate/webhooks/authenticate.ts"],"sourcesContent":["import {WebhookValidationErrorReason, WebhookType} from '@shopify/shopify-api';\n\nimport type {BasicParams} from '../../types';\nimport {adminClientFactory} from '../../clients';\nimport {handleClientErrorFactory} from '../admin/helpers';\nimport {ensureValidOfflineSession} from '../../helpers';\n\nimport type {\n AuthenticateWebhook,\n WebhookContext,\n WebhookContextWithoutSession,\n} from './types';\n\nexport function authenticateWebhookFactory<Topics extends string>(\n params: BasicParams,\n): AuthenticateWebhook<Topics> {\n const {api, logger} = params;\n\n return async function authenticate(\n request: Request,\n ): Promise<WebhookContext<Topics>> {\n if (request.method !== 'POST') {\n logger.debug(\n 'Received a non-POST request for a webhook. Only POST requests are allowed.',\n {url: request.url, method: request.method},\n );\n throw new Response(undefined, {\n status: 405,\n statusText: 'Method not allowed',\n });\n }\n\n const rawBody = await request.text();\n\n const check = await api.webhooks.validate({\n rawBody,\n rawRequest: request,\n });\n\n if (!check.valid) {\n if (check.reason === WebhookValidationErrorReason.InvalidHmac) {\n logger.debug('Webhook HMAC validation failed', check);\n throw new Response(undefined, {\n status: 401,\n statusText: 'Unauthorized',\n });\n } else {\n logger.debug('Webhook validation failed', check);\n throw new Response(undefined, {status: 400, statusText: 'Bad Request'});\n }\n }\n const session = await ensureValidOfflineSession(params, check.domain);\n\n let webhookContext: WebhookContextWithoutSession<Topics>;\n\n if (check.webhookType === WebhookType.Webhooks) {\n webhookContext = {\n apiVersion: check.apiVersion,\n shop: check.domain,\n topic: check.topic as Topics,\n webhookId: check.webhookId,\n payload: JSON.parse(rawBody),\n
|
|
1
|
+
{"version":3,"file":"authenticate.js","sources":["../../../../../../src/server/authenticate/webhooks/authenticate.ts"],"sourcesContent":["import {WebhookValidationErrorReason, WebhookType} from '@shopify/shopify-api';\n\nimport type {BasicParams} from '../../types';\nimport {adminClientFactory} from '../../clients';\nimport {handleClientErrorFactory} from '../admin/helpers';\nimport {ensureValidOfflineSession} from '../../helpers';\n\nimport type {\n AuthenticateWebhook,\n WebhookContext,\n WebhookContextWithoutSession,\n} from './types';\n\nexport function authenticateWebhookFactory<Topics extends string>(\n params: BasicParams,\n): AuthenticateWebhook<Topics> {\n const {api, logger} = params;\n\n return async function authenticate(\n request: Request,\n ): Promise<WebhookContext<Topics>> {\n if (request.method !== 'POST') {\n logger.debug(\n 'Received a non-POST request for a webhook. Only POST requests are allowed.',\n {url: request.url, method: request.method},\n );\n throw new Response(undefined, {\n status: 405,\n statusText: 'Method not allowed',\n });\n }\n\n const rawBody = await request.text();\n\n const check = await api.webhooks.validate({\n rawBody,\n rawRequest: request,\n });\n\n if (!check.valid) {\n if (check.reason === WebhookValidationErrorReason.InvalidHmac) {\n logger.debug('Webhook HMAC validation failed', check);\n throw new Response(undefined, {\n status: 401,\n statusText: 'Unauthorized',\n });\n } else {\n logger.debug('Webhook validation failed', check);\n throw new Response(undefined, {status: 400, statusText: 'Bad Request'});\n }\n }\n const session = await ensureValidOfflineSession(params, check.domain);\n\n let webhookContext: WebhookContextWithoutSession<Topics>;\n\n if (check.webhookType === WebhookType.Webhooks) {\n webhookContext = {\n apiVersion: check.apiVersion,\n shop: check.domain,\n topic: check.topic as Topics,\n webhookId: check.webhookId,\n payload: JSON.parse(rawBody),\n session: undefined,\n admin: undefined,\n webhookType: check.webhookType,\n name: check.name,\n triggeredAt: check.triggeredAt,\n eventId: check.eventId,\n };\n } else {\n webhookContext = {\n apiVersion: check.apiVersion,\n shop: check.domain,\n topic: check.topic as Topics,\n webhookId: check.webhookId,\n payload: JSON.parse(rawBody),\n session: undefined,\n admin: undefined,\n webhookType: check.webhookType,\n handle: check.handle,\n action: check.action,\n resourceId: check.resourceId,\n triggeredAt: check.triggeredAt,\n eventId: check.eventId,\n };\n }\n\n if (!session) {\n return webhookContext;\n }\n\n const admin = adminClientFactory({\n params,\n session,\n handleClientError: handleClientErrorFactory({request}),\n });\n\n return {\n ...webhookContext,\n session,\n admin,\n };\n };\n}\n"],"names":["WebhookValidationErrorReason","ensureValidOfflineSession","WebhookType","adminClientFactory","handleClientErrorFactory"],"mappings":"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"}
|
|
@@ -104,7 +104,7 @@ function deriveApi(appConfig) {
|
|
|
104
104
|
try {
|
|
105
105
|
appUrl = new URL(appConfig.appUrl);
|
|
106
106
|
}
|
|
107
|
-
catch (
|
|
107
|
+
catch (_error) {
|
|
108
108
|
const message = appConfig.appUrl === ''
|
|
109
109
|
? `Detected an empty appUrl configuration, please make sure to set the necessary environment variables.\n` +
|
|
110
110
|
`If you're deploying your app, you can find more information at https://shopify.dev/docs/apps/launch/deployment/deploy-web-app/deploy-to-hosting-service#step-4-set-up-environment-variables`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shopify-app.js","sources":["../../../../src/server/shopify-app.ts"],"sourcesContent":["import '@shopify/shopify-api/adapters/web-api';\nimport {\n ConfigInterface as ApiConfig,\n ShopifyError,\n shopifyApi,\n} from '@shopify/shopify-api';\nimport {SessionStorage} from '@shopify/shopify-app-session-storage';\n\nimport {type AppConfig, type AppConfigArg} from './config-types';\nimport {\n AppDistribution,\n type BasicParams,\n type ShopifyApp,\n type ShopifyAppBase,\n type AdminApp,\n type SingleMerchantApp,\n type AppStoreApp,\n} from './types';\nimport {SHOPIFY_REMIX_LIBRARY_VERSION} from './version';\nimport {registerWebhooksFactory} from './authenticate/webhooks';\nimport {authStrategyFactory} from './authenticate/admin/authenticate';\nimport {authenticateWebhookFactory} from './authenticate/webhooks/authenticate';\nimport {overrideLogger} from './override-logger';\nimport {addDocumentResponseHeadersFactory} from './authenticate/helpers';\nimport {loginFactory} from './authenticate/login/login';\nimport {unauthenticatedAdminContextFactory} from './unauthenticated/admin';\nimport {authenticatePublicFactory} from './authenticate/public';\nimport {unauthenticatedStorefrontContextFactory} from './unauthenticated/storefront';\nimport {AuthCodeFlowStrategy} from './authenticate/admin/strategies/auth-code-flow';\nimport {TokenExchangeStrategy} from './authenticate/admin/strategies/token-exchange';\nimport {MerchantCustomAuth} from './authenticate/admin/strategies/merchant-custom-app';\nimport {IdempotentPromiseHandler} from './authenticate/helpers/idempotent-promise-handler';\nimport {authenticateFlowFactory} from './authenticate/flow/authenticate';\nimport {authenticateFulfillmentServiceFactory} from './authenticate/fulfillment-service/authenticate';\nimport {FutureFlagOptions, logDisabledFutureFlags} from './future/flags';\n\n/**\n * Creates an object your app will use to interact with Shopify.\n *\n * @param appConfig Configuration options for your Shopify app, such as the scopes your app needs.\n * @returns `ShopifyApp` An object constructed using your appConfig. It has methods for interacting with Shopify.\n * @publicDocs\n *\n * @example\n * <caption>The minimum viable configuration</caption>\n * ```ts\n * // /shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * apiKey: process.env.SHOPIFY_API_KEY!,\n * apiSecretKey: process.env.SHOPIFY_API_SECRET!,\n * scopes: process.env.SCOPES?.split(\",\")!,\n * appUrl: process.env.SHOPIFY_APP_URL!,\n * });\n * export default shopify;\n * ```\n */\nexport function shopifyApp<\n Config extends AppConfigArg<Storage, Future>,\n Storage extends SessionStorage,\n Future extends FutureFlagOptions = Config['future'],\n>(appConfig: Readonly<Config>): ShopifyApp<Config> {\n const api = deriveApi(appConfig);\n const config = deriveConfig<Storage>(appConfig, api.config);\n const logger = overrideLogger(api.logger);\n\n if (appConfig.webhooks) {\n api.webhooks.addHandlers(appConfig.webhooks);\n }\n\n const params: BasicParams = {api, config, logger};\n\n let strategy;\n if (config.distribution === AppDistribution.ShopifyAdmin) {\n strategy = new MerchantCustomAuth(params);\n } else if (\n config.future.unstable_newEmbeddedAuthStrategy &&\n config.isEmbeddedApp\n ) {\n strategy = new TokenExchangeStrategy(params);\n } else {\n strategy = new AuthCodeFlowStrategy(params);\n }\n\n const authStrategy = authStrategyFactory<Config>({\n ...params,\n strategy,\n });\n\n const shopify:\n | AdminApp<Config>\n | AppStoreApp<Config>\n | SingleMerchantApp<Config> = {\n sessionStorage: config.sessionStorage,\n addDocumentResponseHeaders: addDocumentResponseHeadersFactory(params),\n registerWebhooks: registerWebhooksFactory(params),\n authenticate: {\n admin: authStrategy,\n flow: authenticateFlowFactory(params),\n public: authenticatePublicFactory(params),\n fulfillmentService: authenticateFulfillmentServiceFactory(params),\n webhook: authenticateWebhookFactory<string>(params),\n },\n unauthenticated: {\n admin: unauthenticatedAdminContextFactory(params),\n storefront: unauthenticatedStorefrontContextFactory(params),\n },\n };\n\n if (\n isAppStoreApp(shopify, appConfig) ||\n isSingleMerchantApp(shopify, appConfig)\n ) {\n shopify.login = loginFactory(params);\n }\n\n logDisabledFutureFlags(config, logger);\n\n return shopify as ShopifyApp<Config>;\n}\n\nfunction isAppStoreApp<Config extends AppConfigArg>(\n _shopify: ShopifyAppBase<Config>,\n config: Config,\n): _shopify is AppStoreApp<Config> {\n return config.distribution === AppDistribution.AppStore;\n}\n\nfunction isSingleMerchantApp<Config extends AppConfigArg>(\n _shopify: ShopifyAppBase<Config>,\n config: Config,\n): _shopify is SingleMerchantApp<Config> {\n return config.distribution === AppDistribution.SingleMerchant;\n}\n\n// This function is only exported so we can unit test it without having to mock the underlying module.\n// It's not available to consumers of the library because it is not exported in the index module, and never should be.\nexport function deriveApi(appConfig: AppConfigArg): BasicParams['api'] {\n let appUrl: URL;\n try {\n appUrl = new URL(appConfig.appUrl);\n } catch (error) {\n const message =\n appConfig.appUrl === ''\n ? `Detected an empty appUrl configuration, please make sure to set the necessary environment variables.\\n` +\n `If you're deploying your app, you can find more information at https://shopify.dev/docs/apps/launch/deployment/deploy-web-app/deploy-to-hosting-service#step-4-set-up-environment-variables`\n : `Invalid appUrl configuration '${appConfig.appUrl}', please provide a valid URL.`;\n throw new ShopifyError(message);\n }\n\n /* eslint-disable no-process-env */\n if (appUrl.hostname === 'localhost' && !appUrl.port && process.env.PORT) {\n appUrl.port = process.env.PORT;\n }\n /* eslint-enable no-process-env */\n appConfig.appUrl = appUrl.origin;\n\n let userAgentPrefix = `Shopify Remix Library v${SHOPIFY_REMIX_LIBRARY_VERSION}`;\n if (appConfig.userAgentPrefix) {\n userAgentPrefix = `${appConfig.userAgentPrefix} | ${userAgentPrefix}`;\n }\n\n return shopifyApi({\n ...appConfig,\n hostName: appUrl.host,\n hostScheme: appUrl.protocol.replace(':', '') as 'http' | 'https',\n userAgentPrefix,\n isEmbeddedApp: appConfig.isEmbeddedApp ?? true,\n apiVersion: appConfig.apiVersion,\n isCustomStoreApp: appConfig.distribution === AppDistribution.ShopifyAdmin,\n billing: appConfig.billing as ApiConfig['billing'],\n future: {\n unstable_managedPricingSupport: true,\n },\n _logDisabledFutureFlags: false,\n });\n}\n\nfunction deriveConfig<Storage extends SessionStorage>(\n appConfig: AppConfigArg,\n apiConfig: ApiConfig,\n): AppConfig<Storage> {\n if (\n !appConfig.sessionStorage &&\n appConfig.distribution !== AppDistribution.ShopifyAdmin\n ) {\n throw new ShopifyError(\n 'Please provide a valid session storage. Refer to https://github.com/Shopify/shopify-app-js/blob/main/README.md#session-storage-options for options.',\n );\n }\n\n const authPathPrefix = appConfig.authPathPrefix || '/auth';\n appConfig.distribution = appConfig.distribution ?? AppDistribution.AppStore;\n\n return {\n ...appConfig,\n ...apiConfig,\n billing: appConfig.billing as ApiConfig['billing'],\n scopes: apiConfig.scopes,\n idempotentPromiseHandler: new IdempotentPromiseHandler(),\n canUseLoginForm: appConfig.distribution !== AppDistribution.ShopifyAdmin,\n useOnlineTokens: appConfig.useOnlineTokens ?? false,\n hooks: appConfig.hooks ?? {},\n sessionStorage: appConfig.sessionStorage as Storage,\n future: appConfig.future ?? {},\n auth: {\n path: authPathPrefix,\n callbackPath: `${authPathPrefix}/callback`,\n patchSessionTokenPath: `${authPathPrefix}/session-token`,\n exitIframePath: `${authPathPrefix}/exit-iframe`,\n loginPath: `${authPathPrefix}/login`,\n },\n distribution: appConfig.distribution,\n };\n}\n"],"names":["overrideLogger","AppDistribution","MerchantCustomAuth","TokenExchangeStrategy","AuthCodeFlowStrategy","authStrategyFactory","addDocumentResponseHeadersFactory","registerWebhooksFactory","authenticateFlowFactory","authenticatePublicFactory","authenticateFulfillmentServiceFactory","authenticateWebhookFactory","unauthenticatedAdminContextFactory","unauthenticatedStorefrontContextFactory","loginFactory","logDisabledFutureFlags","ShopifyError","SHOPIFY_REMIX_LIBRARY_VERSION","shopifyApi","IdempotentPromiseHandler"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"shopify-app.js","sources":["../../../../src/server/shopify-app.ts"],"sourcesContent":["import '@shopify/shopify-api/adapters/web-api';\nimport {\n ConfigInterface as ApiConfig,\n ShopifyError,\n shopifyApi,\n} from '@shopify/shopify-api';\nimport {SessionStorage} from '@shopify/shopify-app-session-storage';\n\nimport {type AppConfig, type AppConfigArg} from './config-types';\nimport {\n AppDistribution,\n type BasicParams,\n type ShopifyApp,\n type ShopifyAppBase,\n type AdminApp,\n type SingleMerchantApp,\n type AppStoreApp,\n} from './types';\nimport {SHOPIFY_REMIX_LIBRARY_VERSION} from './version';\nimport {registerWebhooksFactory} from './authenticate/webhooks';\nimport {authStrategyFactory} from './authenticate/admin/authenticate';\nimport {authenticateWebhookFactory} from './authenticate/webhooks/authenticate';\nimport {overrideLogger} from './override-logger';\nimport {addDocumentResponseHeadersFactory} from './authenticate/helpers';\nimport {loginFactory} from './authenticate/login/login';\nimport {unauthenticatedAdminContextFactory} from './unauthenticated/admin';\nimport {authenticatePublicFactory} from './authenticate/public';\nimport {unauthenticatedStorefrontContextFactory} from './unauthenticated/storefront';\nimport {AuthCodeFlowStrategy} from './authenticate/admin/strategies/auth-code-flow';\nimport {TokenExchangeStrategy} from './authenticate/admin/strategies/token-exchange';\nimport {MerchantCustomAuth} from './authenticate/admin/strategies/merchant-custom-app';\nimport {IdempotentPromiseHandler} from './authenticate/helpers/idempotent-promise-handler';\nimport {authenticateFlowFactory} from './authenticate/flow/authenticate';\nimport {authenticateFulfillmentServiceFactory} from './authenticate/fulfillment-service/authenticate';\nimport {FutureFlagOptions, logDisabledFutureFlags} from './future/flags';\n\n/**\n * Creates an object your app will use to interact with Shopify.\n *\n * @param appConfig Configuration options for your Shopify app, such as the scopes your app needs.\n * @returns `ShopifyApp` An object constructed using your appConfig. It has methods for interacting with Shopify.\n * @publicDocs\n *\n * @example\n * <caption>The minimum viable configuration</caption>\n * ```ts\n * // /shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * apiKey: process.env.SHOPIFY_API_KEY!,\n * apiSecretKey: process.env.SHOPIFY_API_SECRET!,\n * scopes: process.env.SCOPES?.split(\",\")!,\n * appUrl: process.env.SHOPIFY_APP_URL!,\n * });\n * export default shopify;\n * ```\n */\nexport function shopifyApp<\n Config extends AppConfigArg<Storage, Future>,\n Storage extends SessionStorage,\n Future extends FutureFlagOptions = Config['future'],\n>(appConfig: Readonly<Config>): ShopifyApp<Config> {\n const api = deriveApi(appConfig);\n const config = deriveConfig<Storage>(appConfig, api.config);\n const logger = overrideLogger(api.logger);\n\n if (appConfig.webhooks) {\n api.webhooks.addHandlers(appConfig.webhooks);\n }\n\n const params: BasicParams = {api, config, logger};\n\n let strategy;\n if (config.distribution === AppDistribution.ShopifyAdmin) {\n strategy = new MerchantCustomAuth(params);\n } else if (\n config.future.unstable_newEmbeddedAuthStrategy &&\n config.isEmbeddedApp\n ) {\n strategy = new TokenExchangeStrategy(params);\n } else {\n strategy = new AuthCodeFlowStrategy(params);\n }\n\n const authStrategy = authStrategyFactory<Config>({\n ...params,\n strategy,\n });\n\n const shopify:\n AdminApp<Config> | AppStoreApp<Config> | SingleMerchantApp<Config> = {\n sessionStorage: config.sessionStorage,\n addDocumentResponseHeaders: addDocumentResponseHeadersFactory(params),\n registerWebhooks: registerWebhooksFactory(params),\n authenticate: {\n admin: authStrategy,\n flow: authenticateFlowFactory(params),\n public: authenticatePublicFactory(params),\n fulfillmentService: authenticateFulfillmentServiceFactory(params),\n webhook: authenticateWebhookFactory<string>(params),\n },\n unauthenticated: {\n admin: unauthenticatedAdminContextFactory(params),\n storefront: unauthenticatedStorefrontContextFactory(params),\n },\n };\n\n if (\n isAppStoreApp(shopify, appConfig) ||\n isSingleMerchantApp(shopify, appConfig)\n ) {\n shopify.login = loginFactory(params);\n }\n\n logDisabledFutureFlags(config, logger);\n\n return shopify as ShopifyApp<Config>;\n}\n\nfunction isAppStoreApp<Config extends AppConfigArg>(\n _shopify: ShopifyAppBase<Config>,\n config: Config,\n): _shopify is AppStoreApp<Config> {\n return config.distribution === AppDistribution.AppStore;\n}\n\nfunction isSingleMerchantApp<Config extends AppConfigArg>(\n _shopify: ShopifyAppBase<Config>,\n config: Config,\n): _shopify is SingleMerchantApp<Config> {\n return config.distribution === AppDistribution.SingleMerchant;\n}\n\n// This function is only exported so we can unit test it without having to mock the underlying module.\n// It's not available to consumers of the library because it is not exported in the index module, and never should be.\nexport function deriveApi(appConfig: AppConfigArg): BasicParams['api'] {\n let appUrl: URL;\n try {\n appUrl = new URL(appConfig.appUrl);\n } catch (_error) {\n const message =\n appConfig.appUrl === ''\n ? `Detected an empty appUrl configuration, please make sure to set the necessary environment variables.\\n` +\n `If you're deploying your app, you can find more information at https://shopify.dev/docs/apps/launch/deployment/deploy-web-app/deploy-to-hosting-service#step-4-set-up-environment-variables`\n : `Invalid appUrl configuration '${appConfig.appUrl}', please provide a valid URL.`;\n throw new ShopifyError(message);\n }\n\n /* eslint-disable no-process-env */\n if (appUrl.hostname === 'localhost' && !appUrl.port && process.env.PORT) {\n appUrl.port = process.env.PORT;\n }\n /* eslint-enable no-process-env */\n appConfig.appUrl = appUrl.origin;\n\n let userAgentPrefix = `Shopify Remix Library v${SHOPIFY_REMIX_LIBRARY_VERSION}`;\n if (appConfig.userAgentPrefix) {\n userAgentPrefix = `${appConfig.userAgentPrefix} | ${userAgentPrefix}`;\n }\n\n return shopifyApi({\n ...appConfig,\n hostName: appUrl.host,\n hostScheme: appUrl.protocol.replace(':', '') as 'http' | 'https',\n userAgentPrefix,\n isEmbeddedApp: appConfig.isEmbeddedApp ?? true,\n apiVersion: appConfig.apiVersion,\n isCustomStoreApp: appConfig.distribution === AppDistribution.ShopifyAdmin,\n billing: appConfig.billing as ApiConfig['billing'],\n future: {\n unstable_managedPricingSupport: true,\n },\n _logDisabledFutureFlags: false,\n });\n}\n\nfunction deriveConfig<Storage extends SessionStorage>(\n appConfig: AppConfigArg,\n apiConfig: ApiConfig,\n): AppConfig<Storage> {\n if (\n !appConfig.sessionStorage &&\n appConfig.distribution !== AppDistribution.ShopifyAdmin\n ) {\n throw new ShopifyError(\n 'Please provide a valid session storage. Refer to https://github.com/Shopify/shopify-app-js/blob/main/README.md#session-storage-options for options.',\n );\n }\n\n const authPathPrefix = appConfig.authPathPrefix || '/auth';\n appConfig.distribution = appConfig.distribution ?? AppDistribution.AppStore;\n\n return {\n ...appConfig,\n ...apiConfig,\n billing: appConfig.billing as ApiConfig['billing'],\n scopes: apiConfig.scopes,\n idempotentPromiseHandler: new IdempotentPromiseHandler(),\n canUseLoginForm: appConfig.distribution !== AppDistribution.ShopifyAdmin,\n useOnlineTokens: appConfig.useOnlineTokens ?? false,\n hooks: appConfig.hooks ?? {},\n sessionStorage: appConfig.sessionStorage as Storage,\n future: appConfig.future ?? {},\n auth: {\n path: authPathPrefix,\n callbackPath: `${authPathPrefix}/callback`,\n patchSessionTokenPath: `${authPathPrefix}/session-token`,\n exitIframePath: `${authPathPrefix}/exit-iframe`,\n loginPath: `${authPathPrefix}/login`,\n },\n distribution: appConfig.distribution,\n };\n}\n"],"names":["overrideLogger","AppDistribution","MerchantCustomAuth","TokenExchangeStrategy","AuthCodeFlowStrategy","authStrategyFactory","addDocumentResponseHeadersFactory","registerWebhooksFactory","authenticateFlowFactory","authenticatePublicFactory","authenticateFulfillmentServiceFactory","authenticateWebhookFactory","unauthenticatedAdminContextFactory","unauthenticatedStorefrontContextFactory","loginFactory","logDisabledFutureFlags","ShopifyError","SHOPIFY_REMIX_LIBRARY_VERSION","shopifyApi","IdempotentPromiseHandler"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-config.js","sources":["../../../../../src/server/test-helpers/test-config.ts"],"sourcesContent":["import {LogSeverity} from '@shopify/shopify-api';\nimport {SessionStorage} from '@shopify/shopify-app-session-storage';\nimport {MemorySessionStorage} from '@shopify/shopify-app-session-storage-memory';\nimport {\n API_KEY,\n API_SECRET_KEY,\n APP_URL,\n} from '@shopify/shopify-api/test-helpers';\n\nimport type {AppConfigArg} from '../config-types';\nimport type {FutureFlagOptions} from '../future/flags';\n\nconst TEST_CONFIG = {\n apiKey: API_KEY,\n apiSecretKey: API_SECRET_KEY,\n scopes: ['testScope'] as any,\n appUrl: APP_URL,\n adminApiAccessToken: API_KEY,\n privateAppStorefrontAccessToken: API_KEY,\n logger: {\n level: LogSeverity.Debug,\n },\n sessionStorage: new MemorySessionStorage(),\n isTesting: true,\n} as const;\n\nexport function testConfig<\n Overrides extends TestOverridesArg,\n Future extends FutureFlagOptions,\n>(\n {future, ...overrides}: Overrides & {future?: Future} = {} as Overrides & {\n future?: Future;\n },\n): TestConfig<Overrides> {\n return {\n ...TEST_CONFIG,\n ...overrides,\n logger: {\n ...TEST_CONFIG.logger,\n ...(overrides as NonNullable<Overrides>).logger,\n },\n future,\n } as any;\n}\n\n/*\n * This type combines both passed in types, by ignoring any keys from Type1 that are present (and not undefined)\n * in Type2, and then adding any keys from Type1 that are not present in Type2.\n *\n * This effectively enables us to create a type that is the const TEST_CONFIG below, plus any overrides passed in with\n * the output being a const object.\n */\ntype Modify<Type1, Type2> = {\n [key in keyof Type2 as Type2[key] extends undefined
|
|
1
|
+
{"version":3,"file":"test-config.js","sources":["../../../../../src/server/test-helpers/test-config.ts"],"sourcesContent":["import {LogSeverity} from '@shopify/shopify-api';\nimport {SessionStorage} from '@shopify/shopify-app-session-storage';\nimport {MemorySessionStorage} from '@shopify/shopify-app-session-storage-memory';\nimport {\n API_KEY,\n API_SECRET_KEY,\n APP_URL,\n} from '@shopify/shopify-api/test-helpers';\n\nimport type {AppConfigArg} from '../config-types';\nimport type {FutureFlagOptions} from '../future/flags';\n\nconst TEST_CONFIG = {\n apiKey: API_KEY,\n apiSecretKey: API_SECRET_KEY,\n scopes: ['testScope'] as any,\n appUrl: APP_URL,\n adminApiAccessToken: API_KEY,\n privateAppStorefrontAccessToken: API_KEY,\n logger: {\n level: LogSeverity.Debug,\n },\n sessionStorage: new MemorySessionStorage(),\n isTesting: true,\n} as const;\n\nexport function testConfig<\n Overrides extends TestOverridesArg,\n Future extends FutureFlagOptions,\n>(\n {future, ...overrides}: Overrides & {future?: Future} = {} as Overrides & {\n future?: Future;\n },\n): TestConfig<Overrides> {\n return {\n ...TEST_CONFIG,\n ...overrides,\n logger: {\n ...TEST_CONFIG.logger,\n ...(overrides as NonNullable<Overrides>).logger,\n },\n future,\n } as any;\n}\n\n/*\n * This type combines both passed in types, by ignoring any keys from Type1 that are present (and not undefined)\n * in Type2, and then adding any keys from Type1 that are not present in Type2.\n *\n * This effectively enables us to create a type that is the const TEST_CONFIG below, plus any overrides passed in with\n * the output being a const object.\n */\ntype Modify<Type1, Type2> = {\n [\n key in keyof Type2 as Type2[key] extends undefined ? never : key\n ]: Type2[key];\n} & {\n [key in keyof Type1 as key extends keyof Type2 ? never : key]: Type1[key];\n};\n\n/*\n * We omit the future prop and then redefine it with a partial of that object so we can pass the fully typed object in\n * to AppConfigArg.\n */\ntype TestOverrides = Partial<\n Omit<AppConfigArg<SessionStorage, FutureFlagOptions>, 'future'> & {\n future: Partial<FutureFlagOptions>;\n }\n>;\n\nexport type TestOverridesArg = undefined | TestOverrides;\n\nexport type TestConfig<Overrides extends TestOverridesArg> = Modify<\n typeof TEST_CONFIG,\n Overrides\n> & {\n future: FutureFlagOptions;\n logger: AppConfigArg['logger'];\n};\n"],"names":["API_KEY","API_SECRET_KEY","APP_URL","LogSeverity","MemorySessionStorage"],"mappings":";;;;;;AAYA,MAAM,WAAW,GAAG;AAClB,IAAA,MAAM,EAAEA,mBAAO;AACf,IAAA,YAAY,EAAEC,0BAAc;IAC5B,MAAM,EAAE,CAAC,WAAW,CAAQ;AAC5B,IAAA,MAAM,EAAEC,mBAAO;AACf,IAAA,mBAAmB,EAAEF,mBAAO;AAC5B,IAAA,+BAA+B,EAAEA,mBAAO;AACxC,IAAA,MAAM,EAAE;QACN,KAAK,EAAEG,sBAAW,CAAC,KAAK;AACzB,KAAA;IACD,cAAc,EAAE,IAAIC,2BAAoB,EAAE;AAC1C,IAAA,SAAS,EAAE,IAAI;CACP;AAEJ,SAAU,UAAU,CAIxB,EAAC,MAAM,EAAE,GAAG,SAAS,EAAA,GAAmC,EAEvD,EAAA;IAED,OAAO;AACL,QAAA,GAAG,WAAW;AACd,QAAA,GAAG,SAAS;AACZ,QAAA,MAAM,EAAE;YACN,GAAG,WAAW,CAAC,MAAM;YACrB,GAAI,SAAoC,CAAC,MAAM;AAChD,SAAA;QACD,MAAM;KACA;AACV;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.js","sources":["../../../../src/server/version.ts"],"sourcesContent":["export const SHOPIFY_REMIX_LIBRARY_VERSION = '
|
|
1
|
+
{"version":3,"file":"version.js","sources":["../../../../src/server/version.ts"],"sourcesContent":["export const SHOPIFY_REMIX_LIBRARY_VERSION = '5.0.0';\n"],"names":[],"mappings":";;AAAO,MAAM,6BAA6B,GAAG;;;;"}
|
|
@@ -90,10 +90,8 @@ function authStrategyFactory({ strategy, ...params }) {
|
|
|
90
90
|
await ensureAppIsEmbeddedIfRequired(params, request);
|
|
91
91
|
await ensureSessionTokenSearchParamIfRequired(params, request);
|
|
92
92
|
}
|
|
93
|
-
logger.info('Authenticating admin request', {
|
|
94
|
-
shop: getShopFromRequest(request),
|
|
95
|
-
});
|
|
96
93
|
const { payload, shop, sessionId, sessionToken } = await getSessionTokenContext(params, request);
|
|
94
|
+
logger.info('Authenticating admin request', { shop });
|
|
97
95
|
logger.debug('Loading session from storage', { shop, sessionId });
|
|
98
96
|
const existingSession = sessionId
|
|
99
97
|
? await config.sessionStorage.loadSession(sessionId)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"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 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.isEmbeddedApp) {\n return {\n ...context,\n sessionToken,\n redirect: redirectFactory(params, request, session.shop),\n };\n }\n return context;\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 await strategy.respondToOAuthRequests(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.isEmbeddedApp) {\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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"}
|
|
1
|
+
{"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 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> | 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.isEmbeddedApp) {\n return {\n ...context,\n sessionToken,\n redirect: redirectFactory(params, request, session.shop),\n };\n }\n return context;\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 await strategy.respondToOAuthRequests(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 const {payload, shop, sessionId, sessionToken} =\n await getSessionTokenContext(params, request);\n\n logger.info('Authenticating admin request', {shop});\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.isEmbeddedApp) {\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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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate-redirect-url.mjs","sources":["../../../../../../../src/server/authenticate/admin/helpers/validate-redirect-url.ts"],"sourcesContent":["import {ShopifyError} from '@shopify/shopify-api';\n\ninterface Options {\n requireSSL?: boolean;\n throwOnInvalid?: boolean;\n}\n\ntype SanitizedRedirectUrl<OptionsArg extends Options> =\n OptionsArg['throwOnInvalid'] extends false ? URL | undefined : URL;\n\nconst FILE_URI_MATCH = /\\/\\/\\//;\nconst INVALID_RELATIVE_URL = /[/\\\\][/\\\\]/;\nconst WHITESPACE_CHARACTER = /\\s/;\nconst VALID_PROTOCOLS = ['https:', 'http:'];\n\nfunction isSafe(\n domain: string,\n redirectUrl: unknown,\n requireSSL: boolean | undefined = true,\n): redirectUrl is string {\n if (typeof redirectUrl !== 'string') {\n return false;\n }\n\n if (\n FILE_URI_MATCH.test(redirectUrl) ||\n WHITESPACE_CHARACTER.test(redirectUrl)\n ) {\n return false;\n }\n\n let url: URL;\n\n try {\n url = new URL(redirectUrl, domain);\n } catch (
|
|
1
|
+
{"version":3,"file":"validate-redirect-url.mjs","sources":["../../../../../../../src/server/authenticate/admin/helpers/validate-redirect-url.ts"],"sourcesContent":["import {ShopifyError} from '@shopify/shopify-api';\n\ninterface Options {\n requireSSL?: boolean;\n throwOnInvalid?: boolean;\n}\n\ntype SanitizedRedirectUrl<OptionsArg extends Options> =\n OptionsArg['throwOnInvalid'] extends false ? URL | undefined : URL;\n\nconst FILE_URI_MATCH = /\\/\\/\\//;\nconst INVALID_RELATIVE_URL = /[/\\\\][/\\\\]/;\nconst WHITESPACE_CHARACTER = /\\s/;\nconst VALID_PROTOCOLS = ['https:', 'http:'];\n\nfunction isSafe(\n domain: string,\n redirectUrl: unknown,\n requireSSL: boolean | undefined = true,\n): redirectUrl is string {\n if (typeof redirectUrl !== 'string') {\n return false;\n }\n\n if (\n FILE_URI_MATCH.test(redirectUrl) ||\n WHITESPACE_CHARACTER.test(redirectUrl)\n ) {\n return false;\n }\n\n let url: URL;\n\n try {\n url = new URL(redirectUrl, domain);\n } catch (_error) {\n return false;\n }\n\n if (INVALID_RELATIVE_URL.test(url.pathname)) {\n return false;\n }\n\n if (!VALID_PROTOCOLS.includes(url.protocol)) {\n return false;\n }\n\n if (requireSSL && url.protocol !== 'https:') {\n return false;\n }\n\n return true;\n}\n\nexport function sanitizeRedirectUrl<OptionsArg extends Options>(\n domain: string,\n redirectUrl: unknown,\n options: OptionsArg = {} as OptionsArg,\n): SanitizedRedirectUrl<OptionsArg> {\n if (isSafe(domain, redirectUrl, options.requireSSL)) {\n return new URL(redirectUrl, domain) as SanitizedRedirectUrl<OptionsArg>;\n } else if (options.throwOnInvalid === false) {\n return undefined as SanitizedRedirectUrl<OptionsArg>;\n } else {\n throw new ShopifyError('Invalid URL. Refusing to redirect');\n }\n}\n"],"names":[],"mappings":";;AAUA,MAAM,cAAc,GAAG,QAAQ;AAC/B,MAAM,oBAAoB,GAAG,YAAY;AACzC,MAAM,oBAAoB,GAAG,IAAI;AACjC,MAAM,eAAe,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC;AAE3C,SAAS,MAAM,CACb,MAAc,EACd,WAAoB,EACpB,aAAkC,IAAI,EAAA;AAEtC,IAAA,IAAI,OAAO,WAAW,KAAK,QAAQ,EAAE;AACnC,QAAA,OAAO,KAAK;IACd;AAEA,IAAA,IACE,cAAc,CAAC,IAAI,CAAC,WAAW,CAAC;AAChC,QAAA,oBAAoB,CAAC,IAAI,CAAC,WAAW,CAAC,EACtC;AACA,QAAA,OAAO,KAAK;IACd;AAEA,IAAA,IAAI,GAAQ;AAEZ,IAAA,IAAI;QACF,GAAG,GAAG,IAAI,GAAG,CAAC,WAAW,EAAE,MAAM,CAAC;IACpC;IAAE,OAAO,MAAM,EAAE;AACf,QAAA,OAAO,KAAK;IACd;IAEA,IAAI,oBAAoB,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE;AAC3C,QAAA,OAAO,KAAK;IACd;IAEA,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE;AAC3C,QAAA,OAAO,KAAK;IACd;IAEA,IAAI,UAAU,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,EAAE;AAC3C,QAAA,OAAO,KAAK;IACd;AAEA,IAAA,OAAO,IAAI;AACb;AAEM,SAAU,mBAAmB,CACjC,MAAc,EACd,WAAoB,EACpB,UAAsB,EAAgB,EAAA;IAEtC,IAAI,MAAM,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,CAAC,UAAU,CAAC,EAAE;AACnD,QAAA,OAAO,IAAI,GAAG,CAAC,WAAW,EAAE,MAAM,CAAqC;IACzE;AAAO,SAAA,IAAI,OAAO,CAAC,cAAc,KAAK,KAAK,EAAE;AAC3C,QAAA,OAAO,SAA6C;IACtD;SAAO;AACL,QAAA,MAAM,IAAI,YAAY,CAAC,mCAAmC,CAAC;IAC7D;AACF;;;;"}
|
|
@@ -66,7 +66,9 @@ async function validateAppProxyHmac(params, url) {
|
|
|
66
66
|
if (!searchParams.get('index')) {
|
|
67
67
|
searchParams.delete('index');
|
|
68
68
|
}
|
|
69
|
-
let isValid = await api.utils.validateHmac(
|
|
69
|
+
let isValid = await api.utils.validateHmac(searchParams, {
|
|
70
|
+
signator: 'appProxy',
|
|
71
|
+
});
|
|
70
72
|
if (!isValid) {
|
|
71
73
|
const cleanPath = url.pathname
|
|
72
74
|
.replace(/^\//, '')
|
|
@@ -74,10 +76,14 @@ async function validateAppProxyHmac(params, url) {
|
|
|
74
76
|
.replaceAll('/', '.');
|
|
75
77
|
const data = `routes%2F${cleanPath}`;
|
|
76
78
|
searchParams = new URLSearchParams(`?_data=${data}&${searchParams.toString().replace(/^\?/, '')}`);
|
|
77
|
-
isValid = await api.utils.validateHmac(
|
|
79
|
+
isValid = await api.utils.validateHmac(searchParams, {
|
|
80
|
+
signator: 'appProxy',
|
|
81
|
+
});
|
|
78
82
|
if (!isValid) {
|
|
79
83
|
const searchParams = new URLSearchParams(`?_data=${data}._index&${url.search.replace(/^\?/, '')}`);
|
|
80
|
-
isValid = await api.utils.validateHmac(
|
|
84
|
+
isValid = await api.utils.validateHmac(searchParams, {
|
|
85
|
+
signator: 'appProxy',
|
|
86
|
+
});
|
|
81
87
|
}
|
|
82
88
|
}
|
|
83
89
|
return isValid;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authenticate.mjs","sources":["../../../../../../../src/server/authenticate/public/appProxy/authenticate.ts"],"sourcesContent":["import {adminClientFactory, storefrontClientFactory} from '../../../clients';\nimport {ensureValidOfflineSession} from '../../../helpers';\nimport {BasicParams} from '../../../types';\n\nimport {\n AppProxyContext,\n AppProxyContextWithSession,\n AuthenticateAppProxy,\n LiquidResponseFunction,\n} from './types';\n\nexport function authenticateAppProxyFactory(\n params: BasicParams,\n): AuthenticateAppProxy {\n const {logger} = params;\n\n return async function authenticate(\n request: Request,\n ): Promise<AppProxyContext | AppProxyContextWithSession> {\n const url = new URL(request.url);\n const shop = url.searchParams.get('shop')!;\n logger.info('Authenticating app proxy request', {shop});\n\n if (!(await validateAppProxyHmac(params, url))) {\n logger.info('App proxy request has invalid signature', {shop});\n throw new Response(undefined, {\n status: 400,\n statusText: 'Bad Request',\n });\n }\n\n const session = await ensureValidOfflineSession(params, shop);\n\n if (!session) {\n logger.debug('Could not find offline session, returning empty context', {\n shop,\n ...Object.fromEntries(url.searchParams.entries()),\n });\n\n const context: AppProxyContext = {\n liquid,\n session: undefined,\n admin: undefined,\n storefront: undefined,\n };\n\n return context;\n }\n\n const context: AppProxyContextWithSession = {\n liquid,\n session,\n admin: adminClientFactory({params, session}),\n storefront: storefrontClientFactory({params, session}),\n };\n\n return context;\n };\n}\n\nconst liquid: LiquidResponseFunction = (body, initAndOptions) => {\n const processedBody = processLiquidBody(body);\n\n if (typeof initAndOptions !== 'object') {\n return new Response(processedBody, {\n status: initAndOptions || 200,\n headers: {\n 'Content-Type': 'application/liquid',\n },\n });\n }\n\n const {layout, ...responseInit} = initAndOptions || {};\n const responseBody =\n layout === false ? `{% layout none %} ${processedBody}` : processedBody;\n\n const headers = new Headers(responseInit.headers);\n headers.set('Content-Type', 'application/liquid');\n\n return new Response(responseBody, {\n ...responseInit,\n headers,\n });\n};\n\nasync function validateAppProxyHmac(\n params: BasicParams,\n url: URL,\n): Promise<boolean> {\n const {api, logger} = params;\n\n try {\n let searchParams = new URLSearchParams(url.search);\n if (!searchParams.get('index')) {\n searchParams.delete('index');\n }\n\n let isValid = await api.utils.validateHmac(\n
|
|
1
|
+
{"version":3,"file":"authenticate.mjs","sources":["../../../../../../../src/server/authenticate/public/appProxy/authenticate.ts"],"sourcesContent":["import {adminClientFactory, storefrontClientFactory} from '../../../clients';\nimport {ensureValidOfflineSession} from '../../../helpers';\nimport {BasicParams} from '../../../types';\n\nimport {\n AppProxyContext,\n AppProxyContextWithSession,\n AuthenticateAppProxy,\n LiquidResponseFunction,\n} from './types';\n\nexport function authenticateAppProxyFactory(\n params: BasicParams,\n): AuthenticateAppProxy {\n const {logger} = params;\n\n return async function authenticate(\n request: Request,\n ): Promise<AppProxyContext | AppProxyContextWithSession> {\n const url = new URL(request.url);\n const shop = url.searchParams.get('shop')!;\n logger.info('Authenticating app proxy request', {shop});\n\n if (!(await validateAppProxyHmac(params, url))) {\n logger.info('App proxy request has invalid signature', {shop});\n throw new Response(undefined, {\n status: 400,\n statusText: 'Bad Request',\n });\n }\n\n const session = await ensureValidOfflineSession(params, shop);\n\n if (!session) {\n logger.debug('Could not find offline session, returning empty context', {\n shop,\n ...Object.fromEntries(url.searchParams.entries()),\n });\n\n const context: AppProxyContext = {\n liquid,\n session: undefined,\n admin: undefined,\n storefront: undefined,\n };\n\n return context;\n }\n\n const context: AppProxyContextWithSession = {\n liquid,\n session,\n admin: adminClientFactory({params, session}),\n storefront: storefrontClientFactory({params, session}),\n };\n\n return context;\n };\n}\n\nconst liquid: LiquidResponseFunction = (body, initAndOptions) => {\n const processedBody = processLiquidBody(body);\n\n if (typeof initAndOptions !== 'object') {\n return new Response(processedBody, {\n status: initAndOptions || 200,\n headers: {\n 'Content-Type': 'application/liquid',\n },\n });\n }\n\n const {layout, ...responseInit} = initAndOptions || {};\n const responseBody =\n layout === false ? `{% layout none %} ${processedBody}` : processedBody;\n\n const headers = new Headers(responseInit.headers);\n headers.set('Content-Type', 'application/liquid');\n\n return new Response(responseBody, {\n ...responseInit,\n headers,\n });\n};\n\nasync function validateAppProxyHmac(\n params: BasicParams,\n url: URL,\n): Promise<boolean> {\n const {api, logger} = params;\n\n try {\n let searchParams = new URLSearchParams(url.search);\n if (!searchParams.get('index')) {\n searchParams.delete('index');\n }\n\n let isValid = await api.utils.validateHmac(searchParams, {\n signator: 'appProxy',\n });\n\n if (!isValid) {\n const cleanPath = url.pathname\n .replace(/^\\//, '')\n .replace(/\\/$/, '')\n .replaceAll('/', '.');\n const data = `routes%2F${cleanPath}`;\n\n searchParams = new URLSearchParams(\n `?_data=${data}&${searchParams.toString().replace(/^\\?/, '')}`,\n );\n\n isValid = await api.utils.validateHmac(searchParams, {\n signator: 'appProxy',\n });\n\n if (!isValid) {\n const searchParams = new URLSearchParams(\n `?_data=${data}._index&${url.search.replace(/^\\?/, '')}`,\n );\n\n isValid = await api.utils.validateHmac(searchParams, {\n signator: 'appProxy',\n });\n }\n }\n\n return isValid;\n } catch (error) {\n const shop = url.searchParams.get('shop')!;\n logger.info(error.message, {shop});\n throw new Response(undefined, {status: 400, statusText: 'Bad Request'});\n }\n}\n\nfunction processLiquidBody(body: string) {\n return (\n body\n // Add trailing slashes to relative form action URLs\n .replaceAll(\n /<(form[^>]+)action=\"(\\/[^\"?]+)(\\?[^\"]+)?\">/g,\n '<$1action=\"$2/$3\">',\n )\n // Add trailing slashes to relative link href URLs\n .replaceAll(/<(a[^>]+)href=\"(\\/[^\"?]+)(\\?[^\"]+)?\">/g, '<$1href=\"$2/$3\">')\n );\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authenticate.mjs","sources":["../../../../../../src/server/authenticate/webhooks/authenticate.ts"],"sourcesContent":["import {WebhookValidationErrorReason, WebhookType} from '@shopify/shopify-api';\n\nimport type {BasicParams} from '../../types';\nimport {adminClientFactory} from '../../clients';\nimport {handleClientErrorFactory} from '../admin/helpers';\nimport {ensureValidOfflineSession} from '../../helpers';\n\nimport type {\n AuthenticateWebhook,\n WebhookContext,\n WebhookContextWithoutSession,\n} from './types';\n\nexport function authenticateWebhookFactory<Topics extends string>(\n params: BasicParams,\n): AuthenticateWebhook<Topics> {\n const {api, logger} = params;\n\n return async function authenticate(\n request: Request,\n ): Promise<WebhookContext<Topics>> {\n if (request.method !== 'POST') {\n logger.debug(\n 'Received a non-POST request for a webhook. Only POST requests are allowed.',\n {url: request.url, method: request.method},\n );\n throw new Response(undefined, {\n status: 405,\n statusText: 'Method not allowed',\n });\n }\n\n const rawBody = await request.text();\n\n const check = await api.webhooks.validate({\n rawBody,\n rawRequest: request,\n });\n\n if (!check.valid) {\n if (check.reason === WebhookValidationErrorReason.InvalidHmac) {\n logger.debug('Webhook HMAC validation failed', check);\n throw new Response(undefined, {\n status: 401,\n statusText: 'Unauthorized',\n });\n } else {\n logger.debug('Webhook validation failed', check);\n throw new Response(undefined, {status: 400, statusText: 'Bad Request'});\n }\n }\n const session = await ensureValidOfflineSession(params, check.domain);\n\n let webhookContext: WebhookContextWithoutSession<Topics>;\n\n if (check.webhookType === WebhookType.Webhooks) {\n webhookContext = {\n apiVersion: check.apiVersion,\n shop: check.domain,\n topic: check.topic as Topics,\n webhookId: check.webhookId,\n payload: JSON.parse(rawBody),\n
|
|
1
|
+
{"version":3,"file":"authenticate.mjs","sources":["../../../../../../src/server/authenticate/webhooks/authenticate.ts"],"sourcesContent":["import {WebhookValidationErrorReason, WebhookType} from '@shopify/shopify-api';\n\nimport type {BasicParams} from '../../types';\nimport {adminClientFactory} from '../../clients';\nimport {handleClientErrorFactory} from '../admin/helpers';\nimport {ensureValidOfflineSession} from '../../helpers';\n\nimport type {\n AuthenticateWebhook,\n WebhookContext,\n WebhookContextWithoutSession,\n} from './types';\n\nexport function authenticateWebhookFactory<Topics extends string>(\n params: BasicParams,\n): AuthenticateWebhook<Topics> {\n const {api, logger} = params;\n\n return async function authenticate(\n request: Request,\n ): Promise<WebhookContext<Topics>> {\n if (request.method !== 'POST') {\n logger.debug(\n 'Received a non-POST request for a webhook. Only POST requests are allowed.',\n {url: request.url, method: request.method},\n );\n throw new Response(undefined, {\n status: 405,\n statusText: 'Method not allowed',\n });\n }\n\n const rawBody = await request.text();\n\n const check = await api.webhooks.validate({\n rawBody,\n rawRequest: request,\n });\n\n if (!check.valid) {\n if (check.reason === WebhookValidationErrorReason.InvalidHmac) {\n logger.debug('Webhook HMAC validation failed', check);\n throw new Response(undefined, {\n status: 401,\n statusText: 'Unauthorized',\n });\n } else {\n logger.debug('Webhook validation failed', check);\n throw new Response(undefined, {status: 400, statusText: 'Bad Request'});\n }\n }\n const session = await ensureValidOfflineSession(params, check.domain);\n\n let webhookContext: WebhookContextWithoutSession<Topics>;\n\n if (check.webhookType === WebhookType.Webhooks) {\n webhookContext = {\n apiVersion: check.apiVersion,\n shop: check.domain,\n topic: check.topic as Topics,\n webhookId: check.webhookId,\n payload: JSON.parse(rawBody),\n session: undefined,\n admin: undefined,\n webhookType: check.webhookType,\n name: check.name,\n triggeredAt: check.triggeredAt,\n eventId: check.eventId,\n };\n } else {\n webhookContext = {\n apiVersion: check.apiVersion,\n shop: check.domain,\n topic: check.topic as Topics,\n webhookId: check.webhookId,\n payload: JSON.parse(rawBody),\n session: undefined,\n admin: undefined,\n webhookType: check.webhookType,\n handle: check.handle,\n action: check.action,\n resourceId: check.resourceId,\n triggeredAt: check.triggeredAt,\n eventId: check.eventId,\n };\n }\n\n if (!session) {\n return webhookContext;\n }\n\n const admin = adminClientFactory({\n params,\n session,\n handleClientError: handleClientErrorFactory({request}),\n });\n\n return {\n ...webhookContext,\n session,\n admin,\n };\n };\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -102,7 +102,7 @@ function deriveApi(appConfig) {
|
|
|
102
102
|
try {
|
|
103
103
|
appUrl = new URL(appConfig.appUrl);
|
|
104
104
|
}
|
|
105
|
-
catch (
|
|
105
|
+
catch (_error) {
|
|
106
106
|
const message = appConfig.appUrl === ''
|
|
107
107
|
? `Detected an empty appUrl configuration, please make sure to set the necessary environment variables.\n` +
|
|
108
108
|
`If you're deploying your app, you can find more information at https://shopify.dev/docs/apps/launch/deployment/deploy-web-app/deploy-to-hosting-service#step-4-set-up-environment-variables`
|