@shopify/cli-kit 4.9.4 → 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.
Files changed (124) hide show
  1. package/dist/cli/api/graphql/admin/generated/metafield_definitions_by_owner_type.d.ts +5 -0
  2. package/dist/cli/api/graphql/admin/generated/metafield_definitions_by_owner_type.js +22 -0
  3. package/dist/cli/api/graphql/admin/generated/metafield_definitions_by_owner_type.js.map +1 -1
  4. package/dist/private/node/api/graphql.d.ts +2 -1
  5. package/dist/private/node/api/graphql.js +12 -1
  6. package/dist/private/node/api/graphql.js.map +1 -1
  7. package/dist/private/node/api.d.ts +2 -0
  8. package/dist/private/node/api.js +49 -22
  9. package/dist/private/node/api.js.map +1 -1
  10. package/dist/private/node/command-json-schema.js +1 -0
  11. package/dist/private/node/command-json-schema.js.map +1 -1
  12. package/dist/private/node/constants.d.ts +1 -0
  13. package/dist/private/node/constants.js +1 -0
  14. package/dist/private/node/constants.js.map +1 -1
  15. package/dist/private/node/dev-instance-output.d.ts +47 -0
  16. package/dist/private/node/dev-instance-output.js +143 -0
  17. package/dist/private/node/dev-instance-output.js.map +1 -0
  18. package/dist/private/node/json-error.d.ts +1 -15
  19. package/dist/private/node/json-error.js +4 -98
  20. package/dist/private/node/json-error.js.map +1 -1
  21. package/dist/private/node/output-context.d.ts +8 -0
  22. package/dist/private/node/output-context.js +10 -0
  23. package/dist/private/node/output-context.js.map +1 -0
  24. package/dist/private/node/output.test.js +4 -4
  25. package/dist/private/node/output.test.js.map +1 -1
  26. package/dist/private/node/session/automation-token.d.ts +50 -0
  27. package/dist/private/node/session/automation-token.js +77 -0
  28. package/dist/private/node/session/automation-token.js.map +1 -0
  29. package/dist/private/node/session/exchange.d.ts +2 -1
  30. package/dist/private/node/session/exchange.js +5 -3
  31. package/dist/private/node/session/exchange.js.map +1 -1
  32. package/dist/private/node/session/permanent-store-domain.d.ts +21 -0
  33. package/dist/private/node/session/permanent-store-domain.js +72 -0
  34. package/dist/private/node/session/permanent-store-domain.js.map +1 -0
  35. package/dist/private/node/session.d.ts +3 -2
  36. package/dist/private/node/session.js +12 -3
  37. package/dist/private/node/session.js.map +1 -1
  38. package/dist/private/node/themes/trust-challenge.d.ts +31 -0
  39. package/dist/private/node/themes/trust-challenge.js +143 -0
  40. package/dist/private/node/themes/trust-challenge.js.map +1 -0
  41. package/dist/private/node/ui/alert.js +1 -1
  42. package/dist/private/node/ui/alert.js.map +1 -1
  43. package/dist/private/node/ui/components/ConcurrentOutput.d.ts +2 -7
  44. package/dist/private/node/ui/components/ConcurrentOutput.js +4 -8
  45. package/dist/private/node/ui/components/ConcurrentOutput.js.map +1 -1
  46. package/dist/private/node/ui/components/LoadingBar.js +9 -15
  47. package/dist/private/node/ui/components/LoadingBar.js.map +1 -1
  48. package/dist/private/node/ui/components/LoadingBar.test.js +45 -107
  49. package/dist/private/node/ui/components/LoadingBar.test.js.map +1 -1
  50. package/dist/private/node/ui/components/LoadingIndicator.d.ts +7 -0
  51. package/dist/private/node/ui/components/LoadingIndicator.js +22 -0
  52. package/dist/private/node/ui/components/LoadingIndicator.js.map +1 -0
  53. package/dist/public/common/version.d.ts +1 -1
  54. package/dist/public/common/version.js +1 -1
  55. package/dist/public/common/version.js.map +1 -1
  56. package/dist/public/node/api/admin.js +13 -0
  57. package/dist/public/node/api/admin.js.map +1 -1
  58. package/dist/public/node/api/graphql.js +38 -7
  59. package/dist/public/node/api/graphql.js.map +1 -1
  60. package/dist/public/node/archiver.js +13 -6
  61. package/dist/public/node/archiver.js.map +1 -1
  62. package/dist/public/node/base-command.d.ts +6 -0
  63. package/dist/public/node/base-command.js +97 -16
  64. package/dist/public/node/base-command.js.map +1 -1
  65. package/dist/public/node/dev-instance.d.ts +138 -0
  66. package/dist/public/node/dev-instance.js +365 -0
  67. package/dist/public/node/dev-instance.js.map +1 -0
  68. package/dist/public/node/environment.d.ts +6 -3
  69. package/dist/public/node/environment.js +11 -4
  70. package/dist/public/node/environment.js.map +1 -1
  71. package/dist/public/node/error/index.js +2 -1
  72. package/dist/public/node/error/index.js.map +1 -1
  73. package/dist/public/node/error/schema.d.ts +24 -24
  74. package/dist/public/node/error/serialization.d.ts +8 -0
  75. package/dist/public/node/error/serialization.js +103 -0
  76. package/dist/public/node/error/serialization.js.map +1 -0
  77. package/dist/public/node/graphiql/server.js +18 -2
  78. package/dist/public/node/graphiql/server.js.map +1 -1
  79. package/dist/public/node/is-global.js +2 -1
  80. package/dist/public/node/is-global.js.map +1 -1
  81. package/dist/public/node/notifications-system.d.ts +6 -6
  82. package/dist/public/node/path.d.ts +19 -3
  83. package/dist/public/node/path.js +21 -3
  84. package/dist/public/node/path.js.map +1 -1
  85. package/dist/public/node/session.d.ts +36 -2
  86. package/dist/public/node/session.js +48 -4
  87. package/dist/public/node/session.js.map +1 -1
  88. package/dist/public/node/system.d.ts +4 -0
  89. package/dist/public/node/system.js +39 -9
  90. package/dist/public/node/system.js.map +1 -1
  91. package/dist/public/node/themes/api.d.ts +9 -0
  92. package/dist/public/node/themes/api.js +50 -22
  93. package/dist/public/node/themes/api.js.map +1 -1
  94. package/dist/public/node/themes/theme-manager.d.ts +8 -1
  95. package/dist/public/node/themes/theme-manager.js +1 -1
  96. package/dist/public/node/themes/theme-manager.js.map +1 -1
  97. package/dist/public/node/tree-kill.js +12 -2
  98. package/dist/public/node/tree-kill.js.map +1 -1
  99. package/dist/public/node/ui/components.d.ts +1 -0
  100. package/dist/public/node/ui/components.js +1 -0
  101. package/dist/public/node/ui/components.js.map +1 -1
  102. package/dist/public/node/ui.d.ts +24 -4
  103. package/dist/public/node/ui.js +41 -18
  104. package/dist/public/node/ui.js.map +1 -1
  105. package/dist/public/node/upgrade/output.d.ts +17 -0
  106. package/dist/public/node/upgrade/output.js +51 -0
  107. package/dist/public/node/upgrade/output.js.map +1 -0
  108. package/dist/public/node/upgrade/result.d.ts +8 -0
  109. package/dist/public/node/upgrade/result.js +21 -0
  110. package/dist/public/node/upgrade/result.js.map +1 -0
  111. package/dist/public/node/upgrade/types.d.ts +61 -0
  112. package/dist/public/node/upgrade/types.js +46 -0
  113. package/dist/public/node/upgrade/types.js.map +1 -0
  114. package/dist/public/node/upgrade.d.ts +8 -0
  115. package/dist/public/node/upgrade.js +57 -22
  116. package/dist/public/node/upgrade.js.map +1 -1
  117. package/dist/tsconfig.tsbuildinfo +1 -1
  118. package/package.json +3 -3
  119. package/dist/private/node/ui/components/TextAnimation.d.ts +0 -10
  120. package/dist/private/node/ui/components/TextAnimation.js +0 -56
  121. package/dist/private/node/ui/components/TextAnimation.js.map +0 -1
  122. package/dist/private/node/ui/components/TextAnimation.test.d.ts +0 -1
  123. package/dist/private/node/ui/components/TextAnimation.test.js +0 -73
  124. package/dist/private/node/ui/components/TextAnimation.test.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"exchange.js","sourceRoot":"","sources":["../../../../src/private/node/session/exchange.ts"],"names":[],"mappings":"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","sourcesContent":["import {ApplicationToken, IdentityToken} from './schema.js'\nimport {applicationId, clientId as getIdentityClientId} from './identity.js'\nimport {tokenExchangeScopes} from './scopes.js'\nimport {API} from '../api.js'\nimport {identityFqdn} from '../../../public/node/context/fqdn.js'\nimport {shopifyFetch} from '../../../public/node/http.js'\nimport {err, ok, Result} from '../../../public/node/result.js'\nimport {AbortError, BugError} from '../../../public/node/error.js'\nimport {setLastSeenAuthMethod, setLastSeenUserIdAfterAuth} from '../session.js'\nimport {nonRandomUUID} from '../../../public/node/crypto.js'\nimport {outputDebug} from '../../../public/node/output.js'\n\nimport * as jose from 'jose'\n\nexport class InvalidGrantError extends Error {}\nexport class InvalidRequestError extends Error {}\nclass InvalidTargetError extends AbortError {}\n\nexport interface ExchangeScopes {\n admin: string[]\n partners: string[]\n storefront: string[]\n businessPlatform: string[]\n appManagement: string[]\n}\n\n/**\n * Given an identity token, request an application token.\n * @param identityToken - access token obtained in a previous step\n * @param store - the store to use, only needed for admin API\n * @returns An array with the application access tokens.\n */\nexport async function exchangeAccessForApplicationTokens(\n identityToken: IdentityToken,\n scopes: ExchangeScopes,\n store?: string,\n): Promise<Record<string, ApplicationToken>> {\n const token = identityToken.accessToken\n\n const [partners, storefront, businessPlatform, admin, appManagement] = await Promise.all([\n requestAppToken('partners', token, scopes.partners),\n requestAppToken('storefront-renderer', token, scopes.storefront),\n requestAppToken('business-platform', token, scopes.businessPlatform),\n store ? requestAppToken('admin', token, scopes.admin, store) : {},\n requestAppToken('app-management', token, scopes.appManagement),\n ])\n\n return {\n ...partners,\n ...storefront,\n ...businessPlatform,\n ...admin,\n ...appManagement,\n }\n}\n\n/**\n * Given an expired access token, refresh it to get a new one.\n */\nexport async function refreshAccessToken(currentToken: IdentityToken): Promise<IdentityToken> {\n const clientId = getIdentityClientId()\n const params = {\n grant_type: 'refresh_token',\n access_token: currentToken.accessToken,\n refresh_token: currentToken.refreshToken,\n client_id: clientId,\n }\n const tokenResult = await tokenRequest(params)\n const value = tokenResult.mapError(tokenRequestErrorHandler).valueOrBug()\n return buildIdentityToken(value, currentToken.userId, currentToken.alias)\n}\n\n/**\n * Given a custom app automation token passed as ENV variable, request a valid API access token.\n * @param apiName - The API to exchange for the access token\n * @param token - The app automation token passed as ENV variable `SHOPIFY_APP_AUTOMATION_TOKEN`\n * @param scopes - The scopes to request with the access token\n * @returns An instance with the application access tokens.\n */\nasync function exchangeAppAutomationTokenForAccessToken(\n apiName: API,\n token: string,\n scopes: string[],\n): Promise<{accessToken: string; userId: string}> {\n const appId = applicationId(apiName)\n try {\n const newToken = await requestAppToken(apiName, token, scopes)\n\n const accessToken = newToken[appId]!.accessToken\n const userId = nonRandomUUID(token)\n setLastSeenUserIdAfterAuth(userId)\n setLastSeenAuthMethod('partners_token')\n return {accessToken, userId}\n } catch (error) {\n const prettyName = apiName.replace(/-/g, ' ').replace(/\\b\\w/g, (char) => char.toUpperCase())\n const reason = error instanceof Error ? error.message.slice(0, 200) : 'unknown error'\n outputDebug(`Token exchange for the ${prettyName} API failed: ${reason}`)\n throw new AbortError(\n `The custom token provided can't be used for the ${prettyName} API.`,\n 'Ensure the token is correct and not expired.',\n )\n }\n}\n\n/**\n * Given a custom app automation token passed as ENV variable, request a valid Partners API token.\n * This token does not accept extra scopes, just the cli one.\n * @param token - The app automation token passed as ENV variable `SHOPIFY_APP_AUTOMATION_TOKEN`\n * @returns An instance with the application access tokens.\n */\nexport async function exchangeCustomPartnerToken(token: string): Promise<{accessToken: string; userId: string}> {\n return exchangeAppAutomationTokenForAccessToken('partners', token, tokenExchangeScopes('partners'))\n}\n\n/**\n * Given a custom app automation token passed as ENV variable, request a valid App Management API token.\n * @param token - The app automation token passed as ENV variable `SHOPIFY_APP_AUTOMATION_TOKEN`\n * @returns An instance with the application access tokens.\n */\nexport async function exchangeAppAutomationTokenForAppManagementAccessToken(\n token: string,\n): Promise<{accessToken: string; userId: string}> {\n return exchangeAppAutomationTokenForAccessToken('app-management', token, tokenExchangeScopes('app-management'))\n}\n\n/**\n * Given a custom app automation token passed as ENV variable, request a valid Business Platform API token.\n * @param token - The app automation token passed as ENV variable `SHOPIFY_APP_AUTOMATION_TOKEN`\n * @returns An instance with the application access tokens.\n */\nexport async function exchangeAppAutomationTokenForBusinessPlatformAccessToken(\n token: string,\n): Promise<{accessToken: string; userId: string}> {\n return exchangeAppAutomationTokenForAccessToken('business-platform', token, tokenExchangeScopes('business-platform'))\n}\n\nconst identityDeviceErrors = [\n 'authorization_pending',\n 'access_denied',\n 'expired_token',\n 'slow_down',\n 'unknown_failure',\n] as const\n\ntype IdentityDeviceError = (typeof identityDeviceErrors)[number]\n\n/**\n * Given a deviceCode obtained after starting a device identity flow, request an identity token.\n * @param deviceCode - The device code obtained after starting a device identity flow\n * @param scopes - The scopes to request\n * @returns An instance with the identity access tokens.\n */\nexport async function exchangeDeviceCodeForAccessToken(\n deviceCode: string,\n): Promise<Result<IdentityToken, IdentityDeviceError>> {\n const clientId = await getIdentityClientId()\n\n const params = {\n grant_type: 'urn:ietf:params:oauth:grant-type:device_code',\n device_code: deviceCode,\n client_id: clientId,\n }\n\n const tokenResult = await tokenRequest(params)\n if (tokenResult.isErr()) {\n const deviceError = identityDeviceErrors.find((code) => code === tokenResult.error.error) ?? 'unknown_failure'\n return err(deviceError)\n }\n const identityToken = buildIdentityToken(tokenResult.value)\n return ok(identityToken)\n}\n\nexport async function requestAppToken(\n api: API,\n token: string,\n scopes: string[] = [],\n store?: string,\n): Promise<Record<string, ApplicationToken>> {\n const appId = applicationId(api)\n const clientId = await getIdentityClientId()\n\n const params = {\n grant_type: 'urn:ietf:params:oauth:grant-type:token-exchange',\n requested_token_type: 'urn:ietf:params:oauth:token-type:access_token',\n subject_token_type: 'urn:ietf:params:oauth:token-type:access_token',\n client_id: clientId,\n audience: appId,\n scope: scopes.join(' '),\n subject_token: token,\n ...(api === 'admin' && {destination: `https://${store}/admin`, store}),\n }\n\n let identifier = appId\n if (api === 'admin' && store) {\n identifier = `${store}-${appId}`\n }\n const tokenResult = await tokenRequest(params)\n const value = tokenResult.mapError(tokenRequestErrorHandler).valueOrBug()\n const appToken = buildApplicationToken(value)\n return {[identifier]: appToken}\n}\n\ninterface TokenRequestResult {\n access_token: string\n expires_in: number\n refresh_token: string\n scope: string\n id_token?: string\n}\n\nfunction tokenRequestErrorHandler({error, store}: TokenRequestError) {\n const invalidTargetErrorMessage = `You are not authorized to use the CLI to develop in the provided store${\n store ? `: ${store}` : '.'\n }`\n\n if (error === 'invalid_grant') {\n // There's an scenario when Identity returns \"invalid_grant\" when trying to refresh the token\n // using a valid refresh token. When that happens, we take the user through the authentication flow.\n return new InvalidGrantError()\n }\n if (error === 'invalid_request') {\n // There's an scenario when Identity returns \"invalid_request\" when exchanging an identity token.\n // This means the token is invalid. We clear the session and throw an error to let the caller know.\n return new InvalidRequestError()\n }\n if (error === 'invalid_target') {\n return new InvalidTargetError(invalidTargetErrorMessage, '', [\n 'Ensure you have logged in to the store using the Shopify admin at least once.',\n 'Ensure you are the store owner, or have a staff account if you are attempting to log in to a dev store.',\n 'Ensure you are using the permanent store domain, not a vanity domain.',\n ])\n }\n // eslint-disable-next-line @shopify/cli/no-error-factory-functions\n return new AbortError(error)\n}\n\ninterface TokenRequestError {\n error: string\n store?: string\n}\n\nasync function tokenRequest(params: Record<string, string>): Promise<Result<TokenRequestResult, TokenRequestError>> {\n const fqdn = await identityFqdn()\n const url = `https://${fqdn}/oauth/token`\n\n // Send OAuth parameters in the request body (per RFC 6749) rather than the\n // URL query string. This prevents sensitive credentials (subject_token,\n // refresh_token, device_code, client_id, etc.) from being written to\n // verbose CLI debug output or otherwise leaking through URLs.\n const body = new URLSearchParams(Object.entries(params)).toString()\n\n const res = await shopifyFetch(url, {\n method: 'POST',\n headers: {'Content-Type': 'application/x-www-form-urlencoded'},\n body,\n })\n try {\n const responseText = await res.text()\n\n const payload = JSON.parse(responseText)\n if (res.ok) return ok(payload)\n\n // Normalize malformed OAuth error responses before logging.\n const oauthError = typeof payload.error === 'string' && payload.error !== '' ? payload.error : 'unknown_error'\n const rawDescription = typeof payload.error_description === 'string' ? payload.error_description : ''\n const oauthDescription = rawDescription.slice(0, 200)\n\n outputDebug(\n `Token request to Identity failed with status ${res.status}: ${oauthError}${\n oauthDescription === '' ? '' : ` - ${oauthDescription}`\n }`,\n )\n\n return err({error: oauthError, store: params.store})\n } catch (error) {\n if (error instanceof SyntaxError) {\n throw new AbortError(\n `Received invalid response from authentication service (HTTP ${res.status}).`,\n 'The response could not be parsed as JSON. The service may be temporarily unavailable. Please try again.',\n )\n }\n throw error\n }\n}\n\nfunction buildIdentityToken(\n result: TokenRequestResult,\n existingUserId?: string,\n existingAlias?: string,\n): IdentityToken {\n const userId = existingUserId ?? (result.id_token ? jose.decodeJwt(result.id_token).sub! : undefined)\n\n if (!userId) {\n throw new BugError('Error setting userId for session. No id_token or pre-existing user ID provided.')\n }\n\n return {\n accessToken: result.access_token,\n refreshToken: result.refresh_token,\n expiresAt: new Date(Date.now() + result.expires_in * 1000),\n scopes: result.scope.split(' '),\n userId,\n alias: existingAlias,\n }\n}\n\nfunction buildApplicationToken(result: TokenRequestResult): ApplicationToken {\n return {\n accessToken: result.access_token,\n expiresAt: new Date(Date.now() + result.expires_in * 1000),\n scopes: result.scope.split(' '),\n }\n}\n"]}
1
+ {"version":3,"file":"exchange.js","sourceRoot":"","sources":["../../../../src/private/node/session/exchange.ts"],"names":[],"mappings":"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","sourcesContent":["import {ApplicationToken, IdentityToken} from './schema.js'\nimport {applicationId, clientId as getIdentityClientId} from './identity.js'\nimport {tokenExchangeScopes} from './scopes.js'\nimport {automationTokenAuthMethod} from './automation-token.js'\nimport {API} from '../api.js'\nimport {identityFqdn} from '../../../public/node/context/fqdn.js'\nimport {shopifyFetch} from '../../../public/node/http.js'\nimport {err, ok, Result} from '../../../public/node/result.js'\nimport {AbortError, BugError} from '../../../public/node/error.js'\nimport {setLastSeenAuthMethod, setLastSeenUserIdAfterAuth} from '../session.js'\nimport {nonRandomUUID} from '../../../public/node/crypto.js'\nimport {outputDebug} from '../../../public/node/output.js'\n\nimport * as jose from 'jose'\n\nexport class InvalidGrantError extends Error {}\nexport class InvalidRequestError extends Error {}\nclass InvalidTargetError extends AbortError {}\n\nexport interface ExchangeScopes {\n admin: string[]\n partners: string[]\n storefront: string[]\n businessPlatform: string[]\n appManagement: string[]\n}\n\n/**\n * Given an identity token, request an application token.\n * @param identityToken - access token obtained in a previous step\n * @param store - the store to use, only needed for admin API\n * @returns An array with the application access tokens.\n */\nexport async function exchangeAccessForApplicationTokens(\n identityToken: IdentityToken,\n scopes: ExchangeScopes,\n store?: string,\n): Promise<Record<string, ApplicationToken>> {\n const token = identityToken.accessToken\n\n const [partners, storefront, businessPlatform, admin, appManagement] = await Promise.all([\n requestAppToken('partners', token, scopes.partners),\n requestAppToken('storefront-renderer', token, scopes.storefront),\n requestAppToken('business-platform', token, scopes.businessPlatform),\n store ? requestAppToken('admin', token, scopes.admin, store) : {},\n requestAppToken('app-management', token, scopes.appManagement),\n ])\n\n return {\n ...partners,\n ...storefront,\n ...businessPlatform,\n ...admin,\n ...appManagement,\n }\n}\n\n/**\n * Given an expired access token, refresh it to get a new one.\n */\nexport async function refreshAccessToken(currentToken: IdentityToken): Promise<IdentityToken> {\n const clientId = getIdentityClientId()\n const params = {\n grant_type: 'refresh_token',\n access_token: currentToken.accessToken,\n refresh_token: currentToken.refreshToken,\n client_id: clientId,\n }\n const tokenResult = await tokenRequest(params)\n const value = tokenResult.mapError(tokenRequestErrorHandler).valueOrBug()\n return buildIdentityToken(value, currentToken.userId, currentToken.alias)\n}\n\n/**\n * Given a custom app automation token passed as ENV variable, request a valid API access token.\n * @param apiName - The API to exchange for the access token\n * @param token - The app automation token passed as ENV variable `SHOPIFY_APP_AUTOMATION_TOKEN`\n * @param scopes - The scopes to request with the access token\n * @returns An instance with the application access tokens.\n */\nasync function exchangeAppAutomationTokenForAccessToken(\n apiName: API,\n token: string,\n scopes: string[],\n): Promise<{accessToken: string; userId: string}> {\n const appId = applicationId(apiName)\n try {\n const newToken = await requestAppToken(apiName, token, scopes)\n\n const accessToken = newToken[appId]!.accessToken\n const userId = nonRandomUUID(token)\n setLastSeenUserIdAfterAuth(userId)\n setLastSeenAuthMethod(automationTokenAuthMethod())\n return {accessToken, userId}\n } catch (error) {\n const prettyName = apiName.replace(/-/g, ' ').replace(/\\b\\w/g, (char) => char.toUpperCase())\n const reason = error instanceof Error ? error.message.slice(0, 200) : 'unknown error'\n outputDebug(`Token exchange for the ${prettyName} API failed: ${reason}`)\n throw new AbortError(\n `The custom token provided can't be used for the ${prettyName} API.`,\n 'Ensure the token is correct and not expired.',\n )\n }\n}\n\n/**\n * Given a custom app automation token passed as ENV variable, request a valid Partners API token.\n * This token does not accept extra scopes, just the cli one.\n * @param token - The app automation token passed as ENV variable `SHOPIFY_APP_AUTOMATION_TOKEN`\n * @returns An instance with the application access tokens.\n */\nexport async function exchangeCustomPartnerToken(token: string): Promise<{accessToken: string; userId: string}> {\n return exchangeAppAutomationTokenForAccessToken('partners', token, tokenExchangeScopes('partners'))\n}\n\n/**\n * Given a custom app automation token passed as ENV variable, request a valid App Management API token.\n * @param token - The app automation token passed as ENV variable `SHOPIFY_APP_AUTOMATION_TOKEN`\n * @returns An instance with the application access tokens.\n */\nexport async function exchangeAppAutomationTokenForAppManagementAccessToken(\n token: string,\n): Promise<{accessToken: string; userId: string}> {\n return exchangeAppAutomationTokenForAccessToken('app-management', token, tokenExchangeScopes('app-management'))\n}\n\n/**\n * Given a custom app automation token passed as ENV variable, request a valid Business Platform API token.\n * @param token - The app automation token passed as ENV variable `SHOPIFY_APP_AUTOMATION_TOKEN`\n * @param scopes - The scopes to request. An empty list makes Identity issue every Business Platform scope the token holds.\n * @returns An instance with the application access tokens.\n */\nexport async function exchangeAppAutomationTokenForBusinessPlatformAccessToken(\n token: string,\n scopes: string[] = tokenExchangeScopes('business-platform'),\n): Promise<{accessToken: string; userId: string}> {\n return exchangeAppAutomationTokenForAccessToken('business-platform', token, scopes)\n}\n\nconst identityDeviceErrors = [\n 'authorization_pending',\n 'access_denied',\n 'expired_token',\n 'slow_down',\n 'unknown_failure',\n] as const\n\ntype IdentityDeviceError = (typeof identityDeviceErrors)[number]\n\n/**\n * Given a deviceCode obtained after starting a device identity flow, request an identity token.\n * @param deviceCode - The device code obtained after starting a device identity flow\n * @param scopes - The scopes to request\n * @returns An instance with the identity access tokens.\n */\nexport async function exchangeDeviceCodeForAccessToken(\n deviceCode: string,\n): Promise<Result<IdentityToken, IdentityDeviceError>> {\n const clientId = await getIdentityClientId()\n\n const params = {\n grant_type: 'urn:ietf:params:oauth:grant-type:device_code',\n device_code: deviceCode,\n client_id: clientId,\n }\n\n const tokenResult = await tokenRequest(params)\n if (tokenResult.isErr()) {\n const deviceError = identityDeviceErrors.find((code) => code === tokenResult.error.error) ?? 'unknown_failure'\n return err(deviceError)\n }\n const identityToken = buildIdentityToken(tokenResult.value)\n return ok(identityToken)\n}\n\nexport async function requestAppToken(\n api: API,\n token: string,\n scopes: string[] = [],\n store?: string,\n): Promise<Record<string, ApplicationToken>> {\n const appId = applicationId(api)\n const clientId = await getIdentityClientId()\n\n const params = {\n grant_type: 'urn:ietf:params:oauth:grant-type:token-exchange',\n requested_token_type: 'urn:ietf:params:oauth:token-type:access_token',\n subject_token_type: 'urn:ietf:params:oauth:token-type:access_token',\n client_id: clientId,\n audience: appId,\n scope: scopes.join(' '),\n subject_token: token,\n ...(api === 'admin' && {destination: `https://${store}/admin`, store}),\n }\n\n let identifier = appId\n if (api === 'admin' && store) {\n identifier = `${store}-${appId}`\n }\n const tokenResult = await tokenRequest(params)\n const value = tokenResult.mapError(tokenRequestErrorHandler).valueOrBug()\n const appToken = buildApplicationToken(value)\n return {[identifier]: appToken}\n}\n\ninterface TokenRequestResult {\n access_token: string\n expires_in: number\n refresh_token: string\n scope: string\n id_token?: string\n}\n\nfunction tokenRequestErrorHandler({error, store}: TokenRequestError) {\n const invalidTargetErrorMessage = `You are not authorized to use the CLI to develop in the provided store${\n store ? `: ${store}` : '.'\n }`\n\n if (error === 'invalid_grant') {\n // There's an scenario when Identity returns \"invalid_grant\" when trying to refresh the token\n // using a valid refresh token. When that happens, we take the user through the authentication flow.\n return new InvalidGrantError()\n }\n if (error === 'invalid_request') {\n // There's an scenario when Identity returns \"invalid_request\" when exchanging an identity token.\n // This means the token is invalid. We clear the session and throw an error to let the caller know.\n return new InvalidRequestError()\n }\n if (error === 'invalid_target') {\n return new InvalidTargetError(invalidTargetErrorMessage, '', [\n 'Ensure you have logged in to the store using the Shopify admin at least once.',\n 'Ensure you are the store owner, or have a staff account if you are attempting to log in to a dev store.',\n 'Ensure you are using the permanent store domain, not a vanity domain.',\n ])\n }\n // eslint-disable-next-line @shopify/cli/no-error-factory-functions\n return new AbortError(error)\n}\n\ninterface TokenRequestError {\n error: string\n store?: string\n}\n\nasync function tokenRequest(params: Record<string, string>): Promise<Result<TokenRequestResult, TokenRequestError>> {\n const fqdn = await identityFqdn()\n const url = `https://${fqdn}/oauth/token`\n\n // Send OAuth parameters in the request body (per RFC 6749) rather than the\n // URL query string. This prevents sensitive credentials (subject_token,\n // refresh_token, device_code, client_id, etc.) from being written to\n // verbose CLI debug output or otherwise leaking through URLs.\n const body = new URLSearchParams(Object.entries(params)).toString()\n\n const res = await shopifyFetch(url, {\n method: 'POST',\n headers: {'Content-Type': 'application/x-www-form-urlencoded'},\n body,\n })\n try {\n const responseText = await res.text()\n\n const payload = JSON.parse(responseText)\n if (res.ok) return ok(payload)\n\n // Normalize malformed OAuth error responses before logging.\n const oauthError = typeof payload.error === 'string' && payload.error !== '' ? payload.error : 'unknown_error'\n const rawDescription = typeof payload.error_description === 'string' ? payload.error_description : ''\n const oauthDescription = rawDescription.slice(0, 200)\n\n outputDebug(\n `Token request to Identity failed with status ${res.status}: ${oauthError}${\n oauthDescription === '' ? '' : ` - ${oauthDescription}`\n }`,\n )\n\n return err({error: oauthError, store: params.store})\n } catch (error) {\n if (error instanceof SyntaxError) {\n throw new AbortError(\n `Received invalid response from authentication service (HTTP ${res.status}).`,\n 'The response could not be parsed as JSON. The service may be temporarily unavailable. Please try again.',\n )\n }\n throw error\n }\n}\n\nfunction buildIdentityToken(\n result: TokenRequestResult,\n existingUserId?: string,\n existingAlias?: string,\n): IdentityToken {\n const userId = existingUserId ?? (result.id_token ? jose.decodeJwt(result.id_token).sub! : undefined)\n\n if (!userId) {\n throw new BugError('Error setting userId for session. No id_token or pre-existing user ID provided.')\n }\n\n return {\n accessToken: result.access_token,\n refreshToken: result.refresh_token,\n expiresAt: new Date(Date.now() + result.expires_in * 1000),\n scopes: result.scope.split(' '),\n userId,\n alias: existingAlias,\n }\n}\n\nfunction buildApplicationToken(result: TokenRequestResult): ApplicationToken {\n return {\n accessToken: result.access_token,\n expiresAt: new Date(Date.now() + result.expires_in * 1000),\n scopes: result.scope.split(' '),\n }\n}\n"]}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Resolves the permanent `.myshopify.com` domain of a store.
3
+ *
4
+ * A store can be reached through several domains (a renamed `.myshopify.com` domain, a custom domain), but some
5
+ * Shopify services only recognise the store's permanent domain. The Theme Access app is one of them: its passwords
6
+ * are tied to the permanent domain, so a request made with any other domain fails with a 401.
7
+ *
8
+ * The permanent domain is read from the store's public `/meta.json` endpoint, which needs no authentication and is
9
+ * served even when the storefront is password protected. When the lookup fails for any reason, the domain is
10
+ * returned unchanged, so callers behave exactly as they would without this lookup.
11
+ *
12
+ * Results are cached for the lifetime of the process.
13
+ *
14
+ * @param storeFqdn - The store domain the user provided, for example `my-store.myshopify.com`.
15
+ * @returns The store's permanent domain, or `storeFqdn` when it cannot be determined.
16
+ */
17
+ export declare function resolvePermanentStoreFqdn(storeFqdn: string): Promise<string>;
18
+ /**
19
+ * Clears the in-process cache used by {@link resolvePermanentStoreFqdn}. Intended for tests.
20
+ */
21
+ export declare function clearPermanentStoreFqdnCache(): void;
@@ -0,0 +1,72 @@
1
+ import { serviceEnvironment } from '../context/service.js';
2
+ import { extractMyshopifyHandle } from '../../../public/common/url.js';
3
+ import { fetch } from '../../../public/node/http.js';
4
+ import { outputContent, outputDebug, outputInfo, outputToken } from '../../../public/node/output.js';
5
+ /**
6
+ * How long to wait for a store's public `/meta.json` before giving up and using the domain as given.
7
+ */
8
+ const PERMANENT_DOMAIN_LOOKUP_TIMEOUT_MS = 5000;
9
+ const resolvedStoreFqdns = new Map();
10
+ /**
11
+ * Resolves the permanent `.myshopify.com` domain of a store.
12
+ *
13
+ * A store can be reached through several domains (a renamed `.myshopify.com` domain, a custom domain), but some
14
+ * Shopify services only recognise the store's permanent domain. The Theme Access app is one of them: its passwords
15
+ * are tied to the permanent domain, so a request made with any other domain fails with a 401.
16
+ *
17
+ * The permanent domain is read from the store's public `/meta.json` endpoint, which needs no authentication and is
18
+ * served even when the storefront is password protected. When the lookup fails for any reason, the domain is
19
+ * returned unchanged, so callers behave exactly as they would without this lookup.
20
+ *
21
+ * Results are cached for the lifetime of the process.
22
+ *
23
+ * @param storeFqdn - The store domain the user provided, for example `my-store.myshopify.com`.
24
+ * @returns The store's permanent domain, or `storeFqdn` when it cannot be determined.
25
+ */
26
+ export async function resolvePermanentStoreFqdn(storeFqdn) {
27
+ if (serviceEnvironment() === 'local')
28
+ return storeFqdn;
29
+ const cached = resolvedStoreFqdns.get(storeFqdn);
30
+ if (cached)
31
+ return cached;
32
+ const permanentStoreFqdn = await fetchPermanentStoreFqdn(storeFqdn);
33
+ resolvedStoreFqdns.set(storeFqdn, permanentStoreFqdn);
34
+ if (permanentStoreFqdn !== storeFqdn) {
35
+ resolvedStoreFqdns.set(permanentStoreFqdn, permanentStoreFqdn);
36
+ outputInfo(outputContent `Using the permanent domain ${outputToken.raw(permanentStoreFqdn)} for ${outputToken.raw(storeFqdn)}.`);
37
+ }
38
+ return permanentStoreFqdn;
39
+ }
40
+ /**
41
+ * Clears the in-process cache used by {@link resolvePermanentStoreFqdn}. Intended for tests.
42
+ */
43
+ export function clearPermanentStoreFqdnCache() {
44
+ resolvedStoreFqdns.clear();
45
+ }
46
+ async function fetchPermanentStoreFqdn(storeFqdn) {
47
+ const url = `https://${storeFqdn}/meta.json`;
48
+ try {
49
+ const response = await fetch(url, { headers: { Accept: 'application/json' } }, { useNetworkLevelRetry: false, useAbortSignal: true, timeoutMs: PERMANENT_DOMAIN_LOOKUP_TIMEOUT_MS });
50
+ if (!response.ok) {
51
+ outputDebug(`Could not look up the permanent domain of ${storeFqdn}: ${url} returned HTTP ${response.status}`);
52
+ return storeFqdn;
53
+ }
54
+ const body = (await response.json());
55
+ const permanentStoreFqdn = typeof body?.myshopify_domain === 'string' ? body.myshopify_domain.toLowerCase() : '';
56
+ // Accept only a bare `<handle>.myshopify.com`: `extractMyshopifyHandle` also accepts a URL with a path, so
57
+ // compare the round trip to reject anything but the domain itself.
58
+ const handle = extractMyshopifyHandle(permanentStoreFqdn);
59
+ if (!handle || `${handle}.myshopify.com` !== permanentStoreFqdn) {
60
+ outputDebug(`Could not look up the permanent domain of ${storeFqdn}: ${url} has no valid myshopify_domain`);
61
+ return storeFqdn;
62
+ }
63
+ return permanentStoreFqdn;
64
+ // eslint-disable-next-line no-catch-all/no-catch-all
65
+ }
66
+ catch (error) {
67
+ const message = error instanceof Error ? error.message : String(error);
68
+ outputDebug(`Could not look up the permanent domain of ${storeFqdn}: ${message}`);
69
+ return storeFqdn;
70
+ }
71
+ }
72
+ //# sourceMappingURL=permanent-store-domain.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permanent-store-domain.js","sourceRoot":"","sources":["../../../../src/private/node/session/permanent-store-domain.ts"],"names":[],"mappings":"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","sourcesContent":["import {serviceEnvironment} from '../context/service.js'\nimport {extractMyshopifyHandle} from '../../../public/common/url.js'\nimport {fetch} from '../../../public/node/http.js'\nimport {outputContent, outputDebug, outputInfo, outputToken} from '../../../public/node/output.js'\n\n/**\n * How long to wait for a store's public `/meta.json` before giving up and using the domain as given.\n */\nconst PERMANENT_DOMAIN_LOOKUP_TIMEOUT_MS = 5000\n\nconst resolvedStoreFqdns = new Map<string, string>()\n\n/**\n * Resolves the permanent `.myshopify.com` domain of a store.\n *\n * A store can be reached through several domains (a renamed `.myshopify.com` domain, a custom domain), but some\n * Shopify services only recognise the store's permanent domain. The Theme Access app is one of them: its passwords\n * are tied to the permanent domain, so a request made with any other domain fails with a 401.\n *\n * The permanent domain is read from the store's public `/meta.json` endpoint, which needs no authentication and is\n * served even when the storefront is password protected. When the lookup fails for any reason, the domain is\n * returned unchanged, so callers behave exactly as they would without this lookup.\n *\n * Results are cached for the lifetime of the process.\n *\n * @param storeFqdn - The store domain the user provided, for example `my-store.myshopify.com`.\n * @returns The store's permanent domain, or `storeFqdn` when it cannot be determined.\n */\nexport async function resolvePermanentStoreFqdn(storeFqdn: string): Promise<string> {\n if (serviceEnvironment() === 'local') return storeFqdn\n\n const cached = resolvedStoreFqdns.get(storeFqdn)\n if (cached) return cached\n\n const permanentStoreFqdn = await fetchPermanentStoreFqdn(storeFqdn)\n resolvedStoreFqdns.set(storeFqdn, permanentStoreFqdn)\n\n if (permanentStoreFqdn !== storeFqdn) {\n resolvedStoreFqdns.set(permanentStoreFqdn, permanentStoreFqdn)\n outputInfo(\n outputContent`Using the permanent domain ${outputToken.raw(permanentStoreFqdn)} for ${outputToken.raw(\n storeFqdn,\n )}.`,\n )\n }\n return permanentStoreFqdn\n}\n\n/**\n * Clears the in-process cache used by {@link resolvePermanentStoreFqdn}. Intended for tests.\n */\nexport function clearPermanentStoreFqdnCache(): void {\n resolvedStoreFqdns.clear()\n}\n\nasync function fetchPermanentStoreFqdn(storeFqdn: string): Promise<string> {\n const url = `https://${storeFqdn}/meta.json`\n try {\n const response = await fetch(\n url,\n {headers: {Accept: 'application/json'}},\n {useNetworkLevelRetry: false, useAbortSignal: true, timeoutMs: PERMANENT_DOMAIN_LOOKUP_TIMEOUT_MS},\n )\n if (!response.ok) {\n outputDebug(`Could not look up the permanent domain of ${storeFqdn}: ${url} returned HTTP ${response.status}`)\n return storeFqdn\n }\n\n const body = (await response.json()) as {myshopify_domain?: unknown} | null\n const permanentStoreFqdn = typeof body?.myshopify_domain === 'string' ? body.myshopify_domain.toLowerCase() : ''\n // Accept only a bare `<handle>.myshopify.com`: `extractMyshopifyHandle` also accepts a URL with a path, so\n // compare the round trip to reject anything but the domain itself.\n const handle = extractMyshopifyHandle(permanentStoreFqdn)\n if (!handle || `${handle}.myshopify.com` !== permanentStoreFqdn) {\n outputDebug(`Could not look up the permanent domain of ${storeFqdn}: ${url} has no valid myshopify_domain`)\n return storeFqdn\n }\n\n return permanentStoreFqdn\n // eslint-disable-next-line no-catch-all/no-catch-all\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n outputDebug(`Could not look up the permanent domain of ${storeFqdn}: ${message}`)\n return storeFqdn\n }\n}\n"]}
@@ -61,7 +61,7 @@ export interface OAuthSession {
61
61
  appManagement?: string;
62
62
  userId: string;
63
63
  }
64
- type AuthMethod = 'partners_token' | 'device_auth' | 'theme_access_token' | 'custom_app_token' | 'none';
64
+ type AuthMethod = 'partners_token' | 'service_account_token' | 'device_auth' | 'theme_access_token' | 'custom_app_token' | 'none';
65
65
  /**
66
66
  * Retrieves a stable user identifier for analytics, or `'unknown'` if none applies.
67
67
  *
@@ -82,7 +82,8 @@ export declare function setLastSeenUserIdAfterAuth(id: string): void;
82
82
  * This function checks for the authentication method in the following order:
83
83
  * 1. Returns the cached auth method if it's not 'none'.
84
84
  * 2. Checks for a cached session, which implies 'device_auth' was used.
85
- * 3. Checks for a partners token in the environment.
85
+ * 3. Checks for an automation token in the environment: `service_account_token` for SHOPIFY_SERVICE_ACCOUNT_TOKEN,
86
+ * `partners_token` for the app and Partner variables.
86
87
  * 4. Checks for a theme password in the environment.
87
88
  * 5. If none of the above are true, returns 'none'.
88
89
  *
@@ -4,6 +4,7 @@ import { allDefaultScopes, apiScopes } from './session/scopes.js';
4
4
  import { exchangeAccessForApplicationTokens, exchangeCustomPartnerToken, refreshAccessToken, InvalidGrantError, InvalidRequestError, } from './session/exchange.js';
5
5
  import * as sessionStore from './session/store.js';
6
6
  import { pollForDeviceAuthorization, requestDeviceAuthorization } from './session/device-authorization.js';
7
+ import { automationTokenAuthMethod, automationTokenVariablesProblem } from './session/automation-token.js';
7
8
  import { isThemeAccessSession } from './api/rest.js';
8
9
  import { getCurrentSessionId, setCurrentSessionId } from './conf-store.js';
9
10
  import { UserEmailQueryString } from './api/graphql/business-platform-destinations/user-email.js';
@@ -12,7 +13,7 @@ import { themeToken } from '../../public/node/context/local.js';
12
13
  import { AbortError } from '../../public/node/error.js';
13
14
  import { normalizeStoreFqdn, identityFqdn } from '../../public/node/context/fqdn.js';
14
15
  import { getIdentityTokenInformation, getAppAutomationToken } from '../../public/node/environment.js';
15
- import { logout } from '../../public/node/session.js';
16
+ import { ensureNoServiceAccountToken, logout } from '../../public/node/session.js';
16
17
  import { nonRandomUUID } from '../../public/node/crypto.js';
17
18
  import { isEmpty } from '../../public/common/object.js';
18
19
  import { businessPlatformRequest } from '../../public/node/api/business-platform.js';
@@ -69,7 +70,8 @@ export function setLastSeenUserIdAfterAuth(id) {
69
70
  * This function checks for the authentication method in the following order:
70
71
  * 1. Returns the cached auth method if it's not 'none'.
71
72
  * 2. Checks for a cached session, which implies 'device_auth' was used.
72
- * 3. Checks for a partners token in the environment.
73
+ * 3. Checks for an automation token in the environment: `service_account_token` for SHOPIFY_SERVICE_ACCOUNT_TOKEN,
74
+ * `partners_token` for the app and Partner variables.
73
75
  * 4. Checks for a theme password in the environment.
74
76
  * 5. If none of the above are true, returns 'none'.
75
77
  *
@@ -82,7 +84,7 @@ export async function getLastSeenAuthMethod() {
82
84
  return 'device_auth';
83
85
  const appAutomationToken = getAppAutomationToken();
84
86
  if (appAutomationToken)
85
- return 'partners_token';
87
+ return automationTokenAuthMethod();
86
88
  const themePassword = themeToken();
87
89
  if (themePassword) {
88
90
  return isThemeAccessSession({ token: themePassword, storeFqdn: '' }) ? 'theme_access_token' : 'custom_app_token';
@@ -104,6 +106,13 @@ export function setCommandSessionId(sessionId) {
104
106
  * @returns An instance with the access tokens organized by application.
105
107
  */
106
108
  export async function ensureAuthenticated(applications, _env, { forceRefresh = false, noPrompt = false, forceNewSession = false } = {}) {
109
+ // Commands that support automation tokens exchange them before calling this function, and getAppAutomationToken
110
+ // returns no token when the variables are invalid, so an invalid environment always reaches this check before any
111
+ // login starts.
112
+ const variablesProblem = automationTokenVariablesProblem();
113
+ if (variablesProblem)
114
+ throw new AbortError(variablesProblem.message, variablesProblem.tryMessage);
115
+ ensureNoServiceAccountToken();
107
116
  const fqdn = await identityFqdn();
108
117
  const previousStoreFqdn = applications.adminApi?.storeFqdn;
109
118
  if (previousStoreFqdn) {
@@ -1 +1 @@
1
- {"version":3,"file":"session.js","sourceRoot":"","sources":["../../../src/private/node/session.ts"],"names":[],"mappings":"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","sourcesContent":["import {applicationId} from './session/identity.js'\nimport {validateSession} from './session/validate.js'\nimport {allDefaultScopes, apiScopes} from './session/scopes.js'\nimport {\n exchangeAccessForApplicationTokens,\n exchangeCustomPartnerToken,\n ExchangeScopes,\n refreshAccessToken,\n InvalidGrantError,\n InvalidRequestError,\n} from './session/exchange.js'\nimport {IdentityToken, Session, Sessions} from './session/schema.js'\nimport * as sessionStore from './session/store.js'\nimport {pollForDeviceAuthorization, requestDeviceAuthorization} from './session/device-authorization.js'\nimport {isThemeAccessSession} from './api/rest.js'\nimport {getCurrentSessionId, setCurrentSessionId} from './conf-store.js'\nimport {UserEmailQueryString, UserEmailQuery} from './api/graphql/business-platform-destinations/user-email.js'\nimport {outputContent, outputToken, outputDebug, outputCompleted} from '../../public/node/output.js'\nimport {themeToken} from '../../public/node/context/local.js'\nimport {AbortError} from '../../public/node/error.js'\nimport {normalizeStoreFqdn, identityFqdn} from '../../public/node/context/fqdn.js'\nimport {getIdentityTokenInformation, getAppAutomationToken} from '../../public/node/environment.js'\nimport {AdminSession, logout} from '../../public/node/session.js'\nimport {nonRandomUUID} from '../../public/node/crypto.js'\nimport {isEmpty} from '../../public/common/object.js'\nimport {businessPlatformRequest} from '../../public/node/api/business-platform.js'\n\n/**\n * Fetches the user's email from the Business Platform API\n * @param businessPlatformToken - The business platform token\n * @returns The user's email address or undefined if not found\n */\nasync function fetchEmail(businessPlatformToken: string | undefined): Promise<string | undefined> {\n if (!businessPlatformToken) return undefined\n\n try {\n const userEmailResult = await businessPlatformRequest<UserEmailQuery>(UserEmailQueryString, businessPlatformToken)\n return userEmailResult.currentUserAccount?.email ?? undefined\n // eslint-disable-next-line no-catch-all/no-catch-all\n } catch (error) {\n outputDebug(outputContent`Failed to fetch user email: ${(error as Error).message ?? String(error)}`)\n return undefined\n }\n}\n\n/**\n * A scope supported by the Shopify Admin API.\n */\nexport type AdminAPIScope = 'graphql' | 'themes' | 'collaborator'\n\n/**\n * It represents the options to authenticate against the Shopify Admin API.\n */\n\ninterface AdminAPIOAuthOptions {\n /** Store to request permissions for. */\n storeFqdn: string\n /** List of scopes to request permissions for. */\n scopes: AdminAPIScope[]\n}\n\n/**\n * A scope supported by the Partners API.\n */\nexport type PartnersAPIScope = 'cli'\ninterface PartnersAPIOAuthOptions {\n /** List of scopes to request permissions for. */\n scopes: PartnersAPIScope[]\n}\n\n/**\n * A scope supported by the Developer Platform API.\n */\nexport type AppManagementAPIScope = 'https://api.shopify.com/auth/organization.apps.manage'\ninterface AppManagementAPIOauthOptions {\n /** List of scopes to request permissions for. */\n scopes: AppManagementAPIScope[]\n}\n\n/**\n * A scope supported by the Storefront Renderer API.\n */\nexport type StorefrontRendererScope = 'devtools' | 'graphql'\ninterface StorefrontRendererAPIOAuthOptions {\n /** List of scopes to request permissions for. */\n scopes: StorefrontRendererScope[]\n}\n\nexport type BusinessPlatformScope = 'destinations'\ninterface BusinessPlatformAPIOAuthOptions {\n /** List of scopes to request permissions for. */\n scopes: BusinessPlatformScope[]\n}\n\n/**\n * It represents the authentication requirements and\n * is the input necessary to trigger the authentication\n * flow.\n */\nexport interface OAuthApplications {\n adminApi?: AdminAPIOAuthOptions\n storefrontRendererApi?: StorefrontRendererAPIOAuthOptions\n partnersApi?: PartnersAPIOAuthOptions\n businessPlatformApi?: BusinessPlatformAPIOAuthOptions\n appManagementApi?: AppManagementAPIOauthOptions\n}\n\nexport interface OAuthSession {\n admin?: AdminSession\n partners?: string\n storefront?: string\n businessPlatform?: string\n appManagement?: string\n userId: string\n}\n\ntype AuthMethod = 'partners_token' | 'device_auth' | 'theme_access_token' | 'custom_app_token' | 'none'\n\nlet userId: undefined | string\nlet authMethod: AuthMethod = 'none'\nlet commandSessionId: string | undefined\n\n/**\n * Retrieves a stable user identifier for analytics, or `'unknown'` if none applies.\n *\n * Evaluation order:\n * 1. If an app automation token or theme token is used, returns a deterministic UUID\n * derived from that secret.\n * 2. Otherwise, if `setLastSeenUserIdAfterAuth` was called (e.g. after OAuth), returns that value.\n * 3. Otherwise, if a persisted CLI session id is available, returns it.\n * 4. Otherwise returns `'unknown'`.\n *\n * @returns A Promise that resolves to the user ID as a string.\n */\nexport async function getLastSeenUserIdAfterAuth(): Promise<string> {\n const customToken = getAppAutomationToken() ?? themeToken()\n if (customToken) return nonRandomUUID(customToken)\n\n if (userId) return userId\n\n const currentSessionId = getCurrentSessionId()\n if (currentSessionId) return currentSessionId\n\n return 'unknown'\n}\n\nexport function setLastSeenUserIdAfterAuth(id: string) {\n userId = id\n}\n\n/**\n * Retrieves the last seen authentication method used in the current session.\n *\n * This function checks for the authentication method in the following order:\n * 1. Returns the cached auth method if it's not 'none'.\n * 2. Checks for a cached session, which implies 'device_auth' was used.\n * 3. Checks for a partners token in the environment.\n * 4. Checks for a theme password in the environment.\n * 5. If none of the above are true, returns 'none'.\n *\n * @returns A Promise that resolves to the last seen authentication method as an AuthMethod type.\n */\nexport async function getLastSeenAuthMethod(): Promise<AuthMethod> {\n if (authMethod !== 'none') return authMethod\n\n if (getCurrentSessionId()) return 'device_auth'\n\n const appAutomationToken = getAppAutomationToken()\n if (appAutomationToken) return 'partners_token'\n\n const themePassword = themeToken()\n if (themePassword) {\n return isThemeAccessSession({token: themePassword, storeFqdn: ''}) ? 'theme_access_token' : 'custom_app_token'\n }\n\n return 'none'\n}\n\nexport function setLastSeenAuthMethod(method: AuthMethod) {\n authMethod = method\n}\n\nexport function setCommandSessionId(sessionId: string | undefined) {\n commandSessionId = sessionId\n}\n\nexport interface EnsureAuthenticatedAdditionalOptions {\n noPrompt?: boolean\n forceRefresh?: boolean\n forceNewSession?: boolean\n}\n\n/**\n * This method ensures that we have a valid session to authenticate against the given applications using the provided scopes.\n *\n * @param applications - An object containing the applications we need to be authenticated with.\n * @param _env - Optional environment variables to use.\n * @param options - Optional extra options to use.\n * @returns An instance with the access tokens organized by application.\n */\nexport async function ensureAuthenticated(\n applications: OAuthApplications,\n _env?: NodeJS.ProcessEnv,\n {forceRefresh = false, noPrompt = false, forceNewSession = false}: EnsureAuthenticatedAdditionalOptions = {},\n): Promise<OAuthSession> {\n const fqdn = await identityFqdn()\n\n const previousStoreFqdn = applications.adminApi?.storeFqdn\n if (previousStoreFqdn) {\n const normalizedStoreName = normalizeStoreFqdn(previousStoreFqdn)\n if (previousStoreFqdn === applications.adminApi?.storeFqdn) {\n applications.adminApi.storeFqdn = normalizedStoreName\n }\n }\n\n const sessions = (await sessionStore.fetch()) ?? {}\n\n let currentSessionId = forceNewSession ? undefined : (commandSessionId ?? getCurrentSessionId())\n if (!currentSessionId && !commandSessionId) {\n const userIds = Object.keys(sessions[fqdn] ?? {})\n if (userIds.length > 0) currentSessionId = userIds[0]\n }\n const currentSession: Session | undefined =\n currentSessionId && !forceNewSession ? sessions[fqdn]?.[currentSessionId] : undefined\n const scopes = getFlattenScopes(applications)\n\n outputDebug(outputContent`Validating existing session against the scopes:\n${outputToken.json(scopes)}\nFor applications:\n${outputToken.json(applications)}\n`)\n\n const validationResult = await validateSession(scopes, applications, currentSession)\n\n let newSession = {}\n\n if (validationResult === 'needs_full_auth') {\n await throwOnNoPrompt(noPrompt)\n outputDebug(outputContent`Initiating the full authentication flow...`)\n newSession = await executeCompleteFlow(applications, currentSession?.identity.alias)\n } else if (validationResult === 'needs_refresh' || forceRefresh) {\n outputDebug(outputContent`The current session is valid but needs refresh. Refreshing...`)\n try {\n newSession = await refreshTokens(currentSession!, applications)\n } catch (error) {\n if (error instanceof InvalidGrantError) {\n await throwOnNoPrompt(noPrompt)\n newSession = await executeCompleteFlow(applications, currentSession?.identity.alias)\n } else if (error instanceof InvalidRequestError) {\n await sessionStore.remove()\n throw new AbortError('\\nError validating auth session', \"We've cleared the current session, please try again\")\n } else {\n throw error\n }\n }\n }\n\n const completeSession = {...currentSession, ...newSession} as Session\n const newSessionId = completeSession.identity.userId\n const updatedSessions: Sessions = {\n ...sessions,\n [fqdn]: {...sessions[fqdn], [newSessionId]: completeSession},\n }\n\n // Save the new session info if it has changed\n if (!isEmpty(newSession)) {\n await sessionStore.store(updatedSessions)\n if (!commandSessionId) setCurrentSessionId(newSessionId)\n }\n\n const tokens = await tokensFor(applications, completeSession)\n\n const envToken = getAppAutomationToken()\n if (envToken && applications.partnersApi) {\n tokens.partners = (await exchangeCustomPartnerToken(envToken)).accessToken\n }\n\n setLastSeenAuthMethod(envToken ? 'partners_token' : 'device_auth')\n setLastSeenUserIdAfterAuth(tokens.userId)\n return tokens\n}\n\nasync function throwOnNoPrompt(noPrompt: boolean) {\n if (!noPrompt) return\n await logout()\n throw new AbortError(\n `The currently available CLI credentials are invalid.\n\nThe CLI is currently unable to prompt for reauthentication.`,\n 'Restart the CLI process you were running. If in an interactive terminal, you will be prompted to reauthenticate. If in a non-interactive terminal, ensure the correct credentials are available in the program environment.',\n )\n}\n\n/**\n * Execute the full authentication flow.\n *\n * @param applications - An object containing the applications we need to be authenticated with.\n * @param existingAlias - Optional alias from a previous session to preserve if the email fetch fails.\n */\nasync function executeCompleteFlow(applications: OAuthApplications, existingAlias?: string): Promise<Session> {\n const scopes = getFlattenScopes(applications)\n const exchangeScopes = getExchangeScopes(applications)\n const store = applications.adminApi?.storeFqdn\n\n let identityToken: IdentityToken\n const identityTokenInformation = getIdentityTokenInformation()\n if (identityTokenInformation) {\n identityToken = buildIdentityTokenFromEnv(scopes, identityTokenInformation)\n } else {\n // Request a device code to authorize without a browser redirect.\n outputDebug(outputContent`Requesting device authorization code...`)\n const deviceAuth = await requestDeviceAuthorization(scopes)\n\n // Poll for the identity token\n outputDebug(outputContent`Starting polling for the identity token...`)\n identityToken = await pollForDeviceAuthorization(deviceAuth.deviceCode, deviceAuth.interval)\n }\n\n // Exchange identity token for application tokens\n outputDebug(outputContent`CLI token received. Exchanging it for application tokens...`)\n const result = await exchangeAccessForApplicationTokens(identityToken, exchangeScopes, store)\n\n // Preserve existing alias if available, otherwise try fetching email\n const businessPlatformToken = result[applicationId('business-platform')]?.accessToken\n const alias = existingAlias ?? (await fetchEmail(businessPlatformToken)) ?? identityToken.userId\n\n const session: Session = {\n identity: {\n ...identityToken,\n alias,\n },\n applications: result,\n }\n\n outputCompleted(`Logged in.`)\n\n return session\n}\n\n/**\n * Refresh the tokens for a given session.\n *\n * @param session - The session to refresh.\n */\nasync function refreshTokens(session: Session, applications: OAuthApplications): Promise<Session> {\n // Refresh Identity Token\n const identityToken = await refreshAccessToken(session.identity)\n // Exchange new identity token for application tokens\n const exchangeScopes = getExchangeScopes(applications)\n const applicationTokens = await exchangeAccessForApplicationTokens(\n identityToken,\n exchangeScopes,\n applications.adminApi?.storeFqdn,\n )\n\n return {\n identity: {...identityToken, alias: session.identity.alias},\n applications: applicationTokens,\n }\n}\n\n/**\n * Get the application tokens for a given session.\n *\n * @param applications - An object containing the applications we need the tokens for.\n * @param session - The current session.\n * @param fqdn - The identity FQDN.\n */\nasync function tokensFor(applications: OAuthApplications, session: Session): Promise<OAuthSession> {\n const tokens: OAuthSession = {\n userId: session.identity.userId,\n }\n\n if (applications.adminApi) {\n const appId = applicationId('admin')\n const realAppId = `${applications.adminApi.storeFqdn}-${appId}`\n const token = session.applications[realAppId]?.accessToken\n if (token) {\n tokens.admin = {token, storeFqdn: applications.adminApi.storeFqdn}\n }\n }\n\n if (applications.partnersApi) {\n const appId = applicationId('partners')\n tokens.partners = session.applications[appId]?.accessToken\n }\n\n if (applications.storefrontRendererApi) {\n const appId = applicationId('storefront-renderer')\n tokens.storefront = session.applications[appId]?.accessToken\n }\n\n if (applications.businessPlatformApi) {\n const appId = applicationId('business-platform')\n tokens.businessPlatform = session.applications[appId]?.accessToken\n }\n\n if (applications.appManagementApi) {\n const appId = applicationId('app-management')\n tokens.appManagement = session.applications[appId]?.accessToken\n }\n\n return tokens\n}\n\n// Scope Helpers\n/**\n * Get a flattened array of scopes for the given applications.\n *\n * @param apps - An object containing the applications we need the scopes for.\n * @returns A flattened array of scopes.\n */\nfunction getFlattenScopes(apps: OAuthApplications): string[] {\n const admin = apps.adminApi?.scopes ?? []\n const partner = apps.partnersApi?.scopes ?? []\n const storefront = apps.storefrontRendererApi?.scopes ?? []\n const businessPlatform = apps.businessPlatformApi?.scopes ?? []\n const appManagement = apps.appManagementApi?.scopes ?? []\n const requestedScopes = [...admin, ...partner, ...storefront, ...businessPlatform, ...appManagement]\n return allDefaultScopes(requestedScopes)\n}\n\n/**\n * Get the scopes for the given applications.\n *\n * @param apps - An object containing the applications we need the scopes for.\n * @returns An object containing the scopes for each application.\n */\nfunction getExchangeScopes(apps: OAuthApplications): ExchangeScopes {\n const adminScope = apps.adminApi?.scopes ?? []\n const partnerScope = apps.partnersApi?.scopes ?? []\n const storefrontScopes = apps.storefrontRendererApi?.scopes ?? []\n const businessPlatformScopes = apps.businessPlatformApi?.scopes ?? []\n const appManagementScopes = apps.appManagementApi?.scopes ?? []\n return {\n admin: apiScopes('admin', adminScope),\n partners: apiScopes('partners', partnerScope),\n storefront: apiScopes('storefront-renderer', storefrontScopes),\n businessPlatform: apiScopes('business-platform', businessPlatformScopes),\n appManagement: apiScopes('app-management', appManagementScopes),\n }\n}\n\nfunction buildIdentityTokenFromEnv(\n scopes: string[],\n identityTokenInformation: {accessToken: string; refreshToken: string; userId: string},\n) {\n return {\n ...identityTokenInformation,\n expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),\n scopes,\n alias: undefined,\n }\n}\n"]}
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../../src/private/node/session.ts"],"names":[],"mappings":"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","sourcesContent":["import {applicationId} from './session/identity.js'\nimport {validateSession} from './session/validate.js'\nimport {allDefaultScopes, apiScopes} from './session/scopes.js'\nimport {\n exchangeAccessForApplicationTokens,\n exchangeCustomPartnerToken,\n ExchangeScopes,\n refreshAccessToken,\n InvalidGrantError,\n InvalidRequestError,\n} from './session/exchange.js'\nimport {IdentityToken, Session, Sessions} from './session/schema.js'\nimport * as sessionStore from './session/store.js'\nimport {pollForDeviceAuthorization, requestDeviceAuthorization} from './session/device-authorization.js'\nimport {automationTokenAuthMethod, automationTokenVariablesProblem} from './session/automation-token.js'\nimport {isThemeAccessSession} from './api/rest.js'\nimport {getCurrentSessionId, setCurrentSessionId} from './conf-store.js'\nimport {UserEmailQueryString, UserEmailQuery} from './api/graphql/business-platform-destinations/user-email.js'\nimport {outputContent, outputToken, outputDebug, outputCompleted} from '../../public/node/output.js'\nimport {themeToken} from '../../public/node/context/local.js'\nimport {AbortError} from '../../public/node/error.js'\nimport {normalizeStoreFqdn, identityFqdn} from '../../public/node/context/fqdn.js'\nimport {getIdentityTokenInformation, getAppAutomationToken} from '../../public/node/environment.js'\nimport {AdminSession, ensureNoServiceAccountToken, logout} from '../../public/node/session.js'\nimport {nonRandomUUID} from '../../public/node/crypto.js'\nimport {isEmpty} from '../../public/common/object.js'\nimport {businessPlatformRequest} from '../../public/node/api/business-platform.js'\n\n/**\n * Fetches the user's email from the Business Platform API\n * @param businessPlatformToken - The business platform token\n * @returns The user's email address or undefined if not found\n */\nasync function fetchEmail(businessPlatformToken: string | undefined): Promise<string | undefined> {\n if (!businessPlatformToken) return undefined\n\n try {\n const userEmailResult = await businessPlatformRequest<UserEmailQuery>(UserEmailQueryString, businessPlatformToken)\n return userEmailResult.currentUserAccount?.email ?? undefined\n // eslint-disable-next-line no-catch-all/no-catch-all\n } catch (error) {\n outputDebug(outputContent`Failed to fetch user email: ${(error as Error).message ?? String(error)}`)\n return undefined\n }\n}\n\n/**\n * A scope supported by the Shopify Admin API.\n */\nexport type AdminAPIScope = 'graphql' | 'themes' | 'collaborator'\n\n/**\n * It represents the options to authenticate against the Shopify Admin API.\n */\n\ninterface AdminAPIOAuthOptions {\n /** Store to request permissions for. */\n storeFqdn: string\n /** List of scopes to request permissions for. */\n scopes: AdminAPIScope[]\n}\n\n/**\n * A scope supported by the Partners API.\n */\nexport type PartnersAPIScope = 'cli'\ninterface PartnersAPIOAuthOptions {\n /** List of scopes to request permissions for. */\n scopes: PartnersAPIScope[]\n}\n\n/**\n * A scope supported by the Developer Platform API.\n */\nexport type AppManagementAPIScope = 'https://api.shopify.com/auth/organization.apps.manage'\ninterface AppManagementAPIOauthOptions {\n /** List of scopes to request permissions for. */\n scopes: AppManagementAPIScope[]\n}\n\n/**\n * A scope supported by the Storefront Renderer API.\n */\nexport type StorefrontRendererScope = 'devtools' | 'graphql'\ninterface StorefrontRendererAPIOAuthOptions {\n /** List of scopes to request permissions for. */\n scopes: StorefrontRendererScope[]\n}\n\nexport type BusinessPlatformScope = 'destinations'\ninterface BusinessPlatformAPIOAuthOptions {\n /** List of scopes to request permissions for. */\n scopes: BusinessPlatformScope[]\n}\n\n/**\n * It represents the authentication requirements and\n * is the input necessary to trigger the authentication\n * flow.\n */\nexport interface OAuthApplications {\n adminApi?: AdminAPIOAuthOptions\n storefrontRendererApi?: StorefrontRendererAPIOAuthOptions\n partnersApi?: PartnersAPIOAuthOptions\n businessPlatformApi?: BusinessPlatformAPIOAuthOptions\n appManagementApi?: AppManagementAPIOauthOptions\n}\n\nexport interface OAuthSession {\n admin?: AdminSession\n partners?: string\n storefront?: string\n businessPlatform?: string\n appManagement?: string\n userId: string\n}\n\ntype AuthMethod =\n | 'partners_token'\n | 'service_account_token'\n | 'device_auth'\n | 'theme_access_token'\n | 'custom_app_token'\n | 'none'\n\nlet userId: undefined | string\nlet authMethod: AuthMethod = 'none'\nlet commandSessionId: string | undefined\n\n/**\n * Retrieves a stable user identifier for analytics, or `'unknown'` if none applies.\n *\n * Evaluation order:\n * 1. If an app automation token or theme token is used, returns a deterministic UUID\n * derived from that secret.\n * 2. Otherwise, if `setLastSeenUserIdAfterAuth` was called (e.g. after OAuth), returns that value.\n * 3. Otherwise, if a persisted CLI session id is available, returns it.\n * 4. Otherwise returns `'unknown'`.\n *\n * @returns A Promise that resolves to the user ID as a string.\n */\nexport async function getLastSeenUserIdAfterAuth(): Promise<string> {\n const customToken = getAppAutomationToken() ?? themeToken()\n if (customToken) return nonRandomUUID(customToken)\n\n if (userId) return userId\n\n const currentSessionId = getCurrentSessionId()\n if (currentSessionId) return currentSessionId\n\n return 'unknown'\n}\n\nexport function setLastSeenUserIdAfterAuth(id: string) {\n userId = id\n}\n\n/**\n * Retrieves the last seen authentication method used in the current session.\n *\n * This function checks for the authentication method in the following order:\n * 1. Returns the cached auth method if it's not 'none'.\n * 2. Checks for a cached session, which implies 'device_auth' was used.\n * 3. Checks for an automation token in the environment: `service_account_token` for SHOPIFY_SERVICE_ACCOUNT_TOKEN,\n * `partners_token` for the app and Partner variables.\n * 4. Checks for a theme password in the environment.\n * 5. If none of the above are true, returns 'none'.\n *\n * @returns A Promise that resolves to the last seen authentication method as an AuthMethod type.\n */\nexport async function getLastSeenAuthMethod(): Promise<AuthMethod> {\n if (authMethod !== 'none') return authMethod\n\n if (getCurrentSessionId()) return 'device_auth'\n\n const appAutomationToken = getAppAutomationToken()\n if (appAutomationToken) return automationTokenAuthMethod()\n\n const themePassword = themeToken()\n if (themePassword) {\n return isThemeAccessSession({token: themePassword, storeFqdn: ''}) ? 'theme_access_token' : 'custom_app_token'\n }\n\n return 'none'\n}\n\nexport function setLastSeenAuthMethod(method: AuthMethod) {\n authMethod = method\n}\n\nexport function setCommandSessionId(sessionId: string | undefined) {\n commandSessionId = sessionId\n}\n\nexport interface EnsureAuthenticatedAdditionalOptions {\n noPrompt?: boolean\n forceRefresh?: boolean\n forceNewSession?: boolean\n}\n\n/**\n * This method ensures that we have a valid session to authenticate against the given applications using the provided scopes.\n *\n * @param applications - An object containing the applications we need to be authenticated with.\n * @param _env - Optional environment variables to use.\n * @param options - Optional extra options to use.\n * @returns An instance with the access tokens organized by application.\n */\nexport async function ensureAuthenticated(\n applications: OAuthApplications,\n _env?: NodeJS.ProcessEnv,\n {forceRefresh = false, noPrompt = false, forceNewSession = false}: EnsureAuthenticatedAdditionalOptions = {},\n): Promise<OAuthSession> {\n // Commands that support automation tokens exchange them before calling this function, and getAppAutomationToken\n // returns no token when the variables are invalid, so an invalid environment always reaches this check before any\n // login starts.\n const variablesProblem = automationTokenVariablesProblem()\n if (variablesProblem) throw new AbortError(variablesProblem.message, variablesProblem.tryMessage)\n ensureNoServiceAccountToken()\n\n const fqdn = await identityFqdn()\n\n const previousStoreFqdn = applications.adminApi?.storeFqdn\n if (previousStoreFqdn) {\n const normalizedStoreName = normalizeStoreFqdn(previousStoreFqdn)\n if (previousStoreFqdn === applications.adminApi?.storeFqdn) {\n applications.adminApi.storeFqdn = normalizedStoreName\n }\n }\n\n const sessions = (await sessionStore.fetch()) ?? {}\n\n let currentSessionId = forceNewSession ? undefined : (commandSessionId ?? getCurrentSessionId())\n if (!currentSessionId && !commandSessionId) {\n const userIds = Object.keys(sessions[fqdn] ?? {})\n if (userIds.length > 0) currentSessionId = userIds[0]\n }\n const currentSession: Session | undefined =\n currentSessionId && !forceNewSession ? sessions[fqdn]?.[currentSessionId] : undefined\n const scopes = getFlattenScopes(applications)\n\n outputDebug(outputContent`Validating existing session against the scopes:\n${outputToken.json(scopes)}\nFor applications:\n${outputToken.json(applications)}\n`)\n\n const validationResult = await validateSession(scopes, applications, currentSession)\n\n let newSession = {}\n\n if (validationResult === 'needs_full_auth') {\n await throwOnNoPrompt(noPrompt)\n outputDebug(outputContent`Initiating the full authentication flow...`)\n newSession = await executeCompleteFlow(applications, currentSession?.identity.alias)\n } else if (validationResult === 'needs_refresh' || forceRefresh) {\n outputDebug(outputContent`The current session is valid but needs refresh. Refreshing...`)\n try {\n newSession = await refreshTokens(currentSession!, applications)\n } catch (error) {\n if (error instanceof InvalidGrantError) {\n await throwOnNoPrompt(noPrompt)\n newSession = await executeCompleteFlow(applications, currentSession?.identity.alias)\n } else if (error instanceof InvalidRequestError) {\n await sessionStore.remove()\n throw new AbortError('\\nError validating auth session', \"We've cleared the current session, please try again\")\n } else {\n throw error\n }\n }\n }\n\n const completeSession = {...currentSession, ...newSession} as Session\n const newSessionId = completeSession.identity.userId\n const updatedSessions: Sessions = {\n ...sessions,\n [fqdn]: {...sessions[fqdn], [newSessionId]: completeSession},\n }\n\n // Save the new session info if it has changed\n if (!isEmpty(newSession)) {\n await sessionStore.store(updatedSessions)\n if (!commandSessionId) setCurrentSessionId(newSessionId)\n }\n\n const tokens = await tokensFor(applications, completeSession)\n\n const envToken = getAppAutomationToken()\n if (envToken && applications.partnersApi) {\n tokens.partners = (await exchangeCustomPartnerToken(envToken)).accessToken\n }\n\n setLastSeenAuthMethod(envToken ? 'partners_token' : 'device_auth')\n setLastSeenUserIdAfterAuth(tokens.userId)\n return tokens\n}\n\nasync function throwOnNoPrompt(noPrompt: boolean) {\n if (!noPrompt) return\n await logout()\n throw new AbortError(\n `The currently available CLI credentials are invalid.\n\nThe CLI is currently unable to prompt for reauthentication.`,\n 'Restart the CLI process you were running. If in an interactive terminal, you will be prompted to reauthenticate. If in a non-interactive terminal, ensure the correct credentials are available in the program environment.',\n )\n}\n\n/**\n * Execute the full authentication flow.\n *\n * @param applications - An object containing the applications we need to be authenticated with.\n * @param existingAlias - Optional alias from a previous session to preserve if the email fetch fails.\n */\nasync function executeCompleteFlow(applications: OAuthApplications, existingAlias?: string): Promise<Session> {\n const scopes = getFlattenScopes(applications)\n const exchangeScopes = getExchangeScopes(applications)\n const store = applications.adminApi?.storeFqdn\n\n let identityToken: IdentityToken\n const identityTokenInformation = getIdentityTokenInformation()\n if (identityTokenInformation) {\n identityToken = buildIdentityTokenFromEnv(scopes, identityTokenInformation)\n } else {\n // Request a device code to authorize without a browser redirect.\n outputDebug(outputContent`Requesting device authorization code...`)\n const deviceAuth = await requestDeviceAuthorization(scopes)\n\n // Poll for the identity token\n outputDebug(outputContent`Starting polling for the identity token...`)\n identityToken = await pollForDeviceAuthorization(deviceAuth.deviceCode, deviceAuth.interval)\n }\n\n // Exchange identity token for application tokens\n outputDebug(outputContent`CLI token received. Exchanging it for application tokens...`)\n const result = await exchangeAccessForApplicationTokens(identityToken, exchangeScopes, store)\n\n // Preserve existing alias if available, otherwise try fetching email\n const businessPlatformToken = result[applicationId('business-platform')]?.accessToken\n const alias = existingAlias ?? (await fetchEmail(businessPlatformToken)) ?? identityToken.userId\n\n const session: Session = {\n identity: {\n ...identityToken,\n alias,\n },\n applications: result,\n }\n\n outputCompleted(`Logged in.`)\n\n return session\n}\n\n/**\n * Refresh the tokens for a given session.\n *\n * @param session - The session to refresh.\n */\nasync function refreshTokens(session: Session, applications: OAuthApplications): Promise<Session> {\n // Refresh Identity Token\n const identityToken = await refreshAccessToken(session.identity)\n // Exchange new identity token for application tokens\n const exchangeScopes = getExchangeScopes(applications)\n const applicationTokens = await exchangeAccessForApplicationTokens(\n identityToken,\n exchangeScopes,\n applications.adminApi?.storeFqdn,\n )\n\n return {\n identity: {...identityToken, alias: session.identity.alias},\n applications: applicationTokens,\n }\n}\n\n/**\n * Get the application tokens for a given session.\n *\n * @param applications - An object containing the applications we need the tokens for.\n * @param session - The current session.\n * @param fqdn - The identity FQDN.\n */\nasync function tokensFor(applications: OAuthApplications, session: Session): Promise<OAuthSession> {\n const tokens: OAuthSession = {\n userId: session.identity.userId,\n }\n\n if (applications.adminApi) {\n const appId = applicationId('admin')\n const realAppId = `${applications.adminApi.storeFqdn}-${appId}`\n const token = session.applications[realAppId]?.accessToken\n if (token) {\n tokens.admin = {token, storeFqdn: applications.adminApi.storeFqdn}\n }\n }\n\n if (applications.partnersApi) {\n const appId = applicationId('partners')\n tokens.partners = session.applications[appId]?.accessToken\n }\n\n if (applications.storefrontRendererApi) {\n const appId = applicationId('storefront-renderer')\n tokens.storefront = session.applications[appId]?.accessToken\n }\n\n if (applications.businessPlatformApi) {\n const appId = applicationId('business-platform')\n tokens.businessPlatform = session.applications[appId]?.accessToken\n }\n\n if (applications.appManagementApi) {\n const appId = applicationId('app-management')\n tokens.appManagement = session.applications[appId]?.accessToken\n }\n\n return tokens\n}\n\n// Scope Helpers\n/**\n * Get a flattened array of scopes for the given applications.\n *\n * @param apps - An object containing the applications we need the scopes for.\n * @returns A flattened array of scopes.\n */\nfunction getFlattenScopes(apps: OAuthApplications): string[] {\n const admin = apps.adminApi?.scopes ?? []\n const partner = apps.partnersApi?.scopes ?? []\n const storefront = apps.storefrontRendererApi?.scopes ?? []\n const businessPlatform = apps.businessPlatformApi?.scopes ?? []\n const appManagement = apps.appManagementApi?.scopes ?? []\n const requestedScopes = [...admin, ...partner, ...storefront, ...businessPlatform, ...appManagement]\n return allDefaultScopes(requestedScopes)\n}\n\n/**\n * Get the scopes for the given applications.\n *\n * @param apps - An object containing the applications we need the scopes for.\n * @returns An object containing the scopes for each application.\n */\nfunction getExchangeScopes(apps: OAuthApplications): ExchangeScopes {\n const adminScope = apps.adminApi?.scopes ?? []\n const partnerScope = apps.partnersApi?.scopes ?? []\n const storefrontScopes = apps.storefrontRendererApi?.scopes ?? []\n const businessPlatformScopes = apps.businessPlatformApi?.scopes ?? []\n const appManagementScopes = apps.appManagementApi?.scopes ?? []\n return {\n admin: apiScopes('admin', adminScope),\n partners: apiScopes('partners', partnerScope),\n storefront: apiScopes('storefront-renderer', storefrontScopes),\n businessPlatform: apiScopes('business-platform', businessPlatformScopes),\n appManagement: apiScopes('app-management', appManagementScopes),\n }\n}\n\nfunction buildIdentityTokenFromEnv(\n scopes: string[],\n identityTokenInformation: {accessToken: string; refreshToken: string; userId: string},\n) {\n return {\n ...identityTokenInformation,\n expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),\n scopes,\n alias: undefined,\n }\n}\n"]}
@@ -0,0 +1,31 @@
1
+ import { AbortError } from '../../../public/node/error.js';
2
+ /**
3
+ * The abort that ends a Trust Battery challenge: the session cannot answer it, or
4
+ * the request was challenged again after the verification.
5
+ */
6
+ export declare class TrustChallengeError extends AbortError {
7
+ }
8
+ /**
9
+ * Runs a theme Admin API request and, when Core answers with a Trust Battery
10
+ * challenge, waits for the user to complete it in the browser before retrying once.
11
+ *
12
+ * A request that starts while another request's challenge is still being completed
13
+ * waits for that challenge first, so it is sent once instead of being challenged
14
+ * itself and then retried. A request that was already in flight when a challenge
15
+ * was completed retries without a second prompt, unless another prompt is open:
16
+ * then it waits for that prompt too.
17
+ *
18
+ * @param request - The request to run and possibly retry.
19
+ * @returns The result of the request.
20
+ */
21
+ export declare function withTrustChallengeRetry<T>(request: () => Promise<T>): Promise<T>;
22
+ /**
23
+ * Extracts the challenge URL from a Trust Battery `CHALLENGE_REQUIRED` GraphQL error.
24
+ *
25
+ * Only URLs on the Admin host are trusted. Anything else is not treated as a
26
+ * challenge and the original error surfaces through normal error handling.
27
+ *
28
+ * @param error - The error thrown by an Admin API request.
29
+ * @returns The challenge URL, or undefined when the error is not a trust challenge.
30
+ */
31
+ export declare function trustChallengeUrl(error: unknown): Promise<string | undefined>;
@@ -0,0 +1,143 @@
1
+ import { extractGraphQLErrors } from '../api/graphql.js';
2
+ import { commandEventOutputMode } from '../command-event-context.js';
3
+ import { recordEvent } from '../../../public/node/analytics.js';
4
+ import { adminFqdn } from '../../../public/node/context/fqdn.js';
5
+ import { AbortError } from '../../../public/node/error.js';
6
+ import { outputDebug } from '../../../public/node/output.js';
7
+ import { isCI, openURL } from '../../../public/node/system.js';
8
+ import { isTTY, keypress, renderInfo, renderSuccess } from '../../../public/node/ui.js';
9
+ const TRUST_CHALLENGE_ERROR_CODE = 'CHALLENGE_REQUIRED';
10
+ // Enter or Space. `theme dev` binds other single keys on the same stdin to its own shortcuts.
11
+ const CONTINUE_KEYS = ['\r', '\n', ' '];
12
+ /**
13
+ * The abort that ends a Trust Battery challenge: the session cannot answer it, or
14
+ * the request was challenged again after the verification.
15
+ */
16
+ export class TrustChallengeError extends AbortError {
17
+ }
18
+ let pendingChallenge;
19
+ let completedChallenges = 0;
20
+ /**
21
+ * Runs a theme Admin API request and, when Core answers with a Trust Battery
22
+ * challenge, waits for the user to complete it in the browser before retrying once.
23
+ *
24
+ * A request that starts while another request's challenge is still being completed
25
+ * waits for that challenge first, so it is sent once instead of being challenged
26
+ * itself and then retried. A request that was already in flight when a challenge
27
+ * was completed retries without a second prompt, unless another prompt is open:
28
+ * then it waits for that prompt too.
29
+ *
30
+ * @param request - The request to run and possibly retry.
31
+ * @returns The result of the request.
32
+ */
33
+ export async function withTrustChallengeRetry(request) {
34
+ if (pendingChallenge)
35
+ await pendingChallenge;
36
+ const completedChallengesAtStart = completedChallenges;
37
+ const attempt = await attemptRequest(request);
38
+ if (!attempt.challenged)
39
+ return attempt.result;
40
+ const verifiedSinceStart = completedChallenges > completedChallengesAtStart;
41
+ if (pendingChallenge || !verifiedSinceStart)
42
+ await completeTrustChallenge(attempt.challengeUrl);
43
+ const retry = await attemptRequest(request);
44
+ if (!retry.challenged)
45
+ return retry.result;
46
+ recordEvent('theme-api:trust-challenge:retry-challenged');
47
+ throw new TrustChallengeError('Shopify still needs to verify your identity before it can continue.', undefined, [
48
+ ['Complete the verification at', { link: { url: retry.challengeUrl } }, 'then run the command again.'],
49
+ ]);
50
+ }
51
+ /**
52
+ * Runs the request once. A Trust Battery challenge becomes a result with the challenge URL;
53
+ * any other error is thrown.
54
+ */
55
+ async function attemptRequest(request) {
56
+ try {
57
+ return { challenged: false, result: await request() };
58
+ }
59
+ catch (error) {
60
+ const challengeUrl = await trustChallengeUrl(error);
61
+ if (!challengeUrl)
62
+ throw error;
63
+ return { challenged: true, challengeUrl };
64
+ }
65
+ }
66
+ /**
67
+ * Extracts the challenge URL from a Trust Battery `CHALLENGE_REQUIRED` GraphQL error.
68
+ *
69
+ * Only URLs on the Admin host are trusted. Anything else is not treated as a
70
+ * challenge and the original error surfaces through normal error handling.
71
+ *
72
+ * @param error - The error thrown by an Admin API request.
73
+ * @returns The challenge URL, or undefined when the error is not a trust challenge.
74
+ */
75
+ export async function trustChallengeUrl(error) {
76
+ const challengeError = extractGraphQLErrors(error).find((graphQLError) => graphQLError.extensions?.code === TRUST_CHALLENGE_ERROR_CODE);
77
+ const redirectTo = challengeError?.extensions?.redirect_to;
78
+ if (typeof redirectTo !== 'string')
79
+ return undefined;
80
+ if (!(await isAdminUrl(redirectTo))) {
81
+ outputDebug(`Ignoring trust challenge with an unexpected URL host: ${redirectTo}`);
82
+ return undefined;
83
+ }
84
+ return redirectTo;
85
+ }
86
+ async function isAdminUrl(url) {
87
+ try {
88
+ const parsedUrl = new URL(url);
89
+ return parsedUrl.protocol === 'https:' && parsedUrl.hostname === (await adminFqdn());
90
+ // eslint-disable-next-line no-catch-all/no-catch-all
91
+ }
92
+ catch {
93
+ return false;
94
+ }
95
+ }
96
+ /**
97
+ * Concurrent theme requests (for example the parallel batches of `theme push`)
98
+ * all receive the same challenge. Share one prompt so the browser opens once and
99
+ * every waiting request retries after the same verification.
100
+ */
101
+ async function completeTrustChallenge(challengeUrl) {
102
+ pendingChallenge ??= promptForTrustChallenge(challengeUrl)
103
+ .then(() => {
104
+ completedChallenges += 1;
105
+ })
106
+ .finally(() => {
107
+ pendingChallenge = undefined;
108
+ });
109
+ return pendingChallenge;
110
+ }
111
+ async function promptForTrustChallenge(challengeUrl) {
112
+ recordEvent('theme-api:trust-challenge:required');
113
+ if (isCI() || !isTTY() || commandEventOutputMode() === 'json') {
114
+ recordEvent('theme-api:trust-challenge:non-interactive');
115
+ throw new TrustChallengeError('Shopify needs to verify your identity before it can continue.', undefined, [
116
+ [
117
+ 'Open',
118
+ { link: { url: challengeUrl } },
119
+ "in a browser where you're logged in to Shopify with the same account you use in Shopify CLI, complete the verification, then run the command again.",
120
+ ],
121
+ ]);
122
+ }
123
+ const opened = await openURL(challengeUrl);
124
+ recordEvent(opened ? 'theme-api:trust-challenge:browser-opened' : 'theme-api:trust-challenge:browser-not-opened');
125
+ // Everything the user needs is inside the banner: long-running commands such as
126
+ // `theme push` redraw an Ink progress bar that erases any plain line written after it.
127
+ renderInfo({
128
+ headline: 'Verify your identity to continue.',
129
+ body: opened
130
+ ? 'A verification page opened in your browser.'
131
+ : 'Shopify needs to verify your identity before it can continue.',
132
+ link: opened ? undefined : { label: 'Open the verification page', url: challengeUrl },
133
+ nextSteps: [
134
+ ['Complete the verification in your browser, logged in with the same account you use in Shopify CLI.'],
135
+ ['Press', { userInput: 'Enter' }, 'here once you have completed it.'],
136
+ ],
137
+ orderedNextSteps: true,
138
+ });
139
+ await keypress(process.stdin, undefined, { keys: CONTINUE_KEYS });
140
+ recordEvent('theme-api:trust-challenge:acknowledged');
141
+ renderSuccess({ headline: 'Continuing.' });
142
+ }
143
+ //# sourceMappingURL=trust-challenge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trust-challenge.js","sourceRoot":"","sources":["../../../../src/private/node/themes/trust-challenge.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,oBAAoB,EAAC,MAAM,mBAAmB,CAAA;AACtD,OAAO,EAAC,sBAAsB,EAAC,MAAM,6BAA6B,CAAA;AAClE,OAAO,EAAC,WAAW,EAAC,MAAM,mCAAmC,CAAA;AAC7D,OAAO,EAAC,SAAS,EAAC,MAAM,sCAAsC,CAAA;AAC9D,OAAO,EAAC,UAAU,EAAC,MAAM,+BAA+B,CAAA;AACxD,OAAO,EAAC,WAAW,EAAC,MAAM,gCAAgC,CAAA;AAC1D,OAAO,EAAC,IAAI,EAAE,OAAO,EAAC,MAAM,gCAAgC,CAAA;AAC5D,OAAO,EAAC,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,aAAa,EAAC,MAAM,4BAA4B,CAAA;AAErF,MAAM,0BAA0B,GAAG,oBAAoB,CAAA;AACvD,8FAA8F;AAC9F,MAAM,aAAa,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC,CAAA;AAEvC;;;GAGG;AACH,MAAM,OAAO,mBAAoB,SAAQ,UAAU;CAAG;AAEtD,IAAI,gBAA2C,CAAA;AAC/C,IAAI,mBAAmB,GAAG,CAAC,CAAA;AAE3B;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAI,OAAyB;IACxE,IAAI,gBAAgB;QAAE,MAAM,gBAAgB,CAAA;IAC5C,MAAM,0BAA0B,GAAG,mBAAmB,CAAA;IAEtD,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,OAAO,CAAC,CAAA;IAC7C,IAAI,CAAC,OAAO,CAAC,UAAU;QAAE,OAAO,OAAO,CAAC,MAAM,CAAA;IAE9C,MAAM,kBAAkB,GAAG,mBAAmB,GAAG,0BAA0B,CAAA;IAC3E,IAAI,gBAAgB,IAAI,CAAC,kBAAkB;QAAE,MAAM,sBAAsB,CAAC,OAAO,CAAC,YAAY,CAAC,CAAA;IAE/F,MAAM,KAAK,GAAG,MAAM,cAAc,CAAC,OAAO,CAAC,CAAA;IAC3C,IAAI,CAAC,KAAK,CAAC,UAAU;QAAE,OAAO,KAAK,CAAC,MAAM,CAAA;IAE1C,WAAW,CAAC,4CAA4C,CAAC,CAAA;IACzD,MAAM,IAAI,mBAAmB,CAAC,qEAAqE,EAAE,SAAS,EAAE;QAC9G,CAAC,8BAA8B,EAAE,EAAC,IAAI,EAAE,EAAC,GAAG,EAAE,KAAK,CAAC,YAAY,EAAC,EAAC,EAAE,6BAA6B,CAAC;KACnG,CAAC,CAAA;AACJ,CAAC;AAID;;;GAGG;AACH,KAAK,UAAU,cAAc,CAAI,OAAyB;IACxD,IAAI,CAAC;QACH,OAAO,EAAC,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,OAAO,EAAE,EAAC,CAAA;IACrD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,YAAY,GAAG,MAAM,iBAAiB,CAAC,KAAK,CAAC,CAAA;QACnD,IAAI,CAAC,YAAY;YAAE,MAAM,KAAK,CAAA;QAC9B,OAAO,EAAC,UAAU,EAAE,IAAI,EAAE,YAAY,EAAC,CAAA;IACzC,CAAC;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,KAAc;IACpD,MAAM,cAAc,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC,IAAI,CACrD,CAAC,YAAY,EAAE,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,IAAI,KAAK,0BAA0B,CAC/E,CAAA;IACD,MAAM,UAAU,GAAG,cAAc,EAAE,UAAU,EAAE,WAAW,CAAA;IAC1D,IAAI,OAAO,UAAU,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAA;IAEpD,IAAI,CAAC,CAAC,MAAM,UAAU,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC;QACpC,WAAW,CAAC,yDAAyD,UAAU,EAAE,CAAC,CAAA;QAClF,OAAO,SAAS,CAAA;IAClB,CAAC;IAED,OAAO,UAAU,CAAA;AACnB,CAAC;AAED,KAAK,UAAU,UAAU,CAAC,GAAW;IACnC,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAA;QAC9B,OAAO,SAAS,CAAC,QAAQ,KAAK,QAAQ,IAAI,SAAS,CAAC,QAAQ,KAAK,CAAC,MAAM,SAAS,EAAE,CAAC,CAAA;QACpF,qDAAqD;IACvD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,sBAAsB,CAAC,YAAoB;IACxD,gBAAgB,KAAK,uBAAuB,CAAC,YAAY,CAAC;SACvD,IAAI,CAAC,GAAG,EAAE;QACT,mBAAmB,IAAI,CAAC,CAAA;IAC1B,CAAC,CAAC;SACD,OAAO,CAAC,GAAG,EAAE;QACZ,gBAAgB,GAAG,SAAS,CAAA;IAC9B,CAAC,CAAC,CAAA;IACJ,OAAO,gBAAgB,CAAA;AACzB,CAAC;AAED,KAAK,UAAU,uBAAuB,CAAC,YAAoB;IACzD,WAAW,CAAC,oCAAoC,CAAC,CAAA;IAEjD,IAAI,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,sBAAsB,EAAE,KAAK,MAAM,EAAE,CAAC;QAC9D,WAAW,CAAC,2CAA2C,CAAC,CAAA;QACxD,MAAM,IAAI,mBAAmB,CAAC,+DAA+D,EAAE,SAAS,EAAE;YACxG;gBACE,MAAM;gBACN,EAAC,IAAI,EAAE,EAAC,GAAG,EAAE,YAAY,EAAC,EAAC;gBAC3B,qJAAqJ;aACtJ;SACF,CAAC,CAAA;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,YAAY,CAAC,CAAA;IAC1C,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,0CAA0C,CAAC,CAAC,CAAC,8CAA8C,CAAC,CAAA;IAEjH,gFAAgF;IAChF,uFAAuF;IACvF,UAAU,CAAC;QACT,QAAQ,EAAE,mCAAmC;QAC7C,IAAI,EAAE,MAAM;YACV,CAAC,CAAC,6CAA6C;YAC/C,CAAC,CAAC,+DAA+D;QACnE,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAC,KAAK,EAAE,4BAA4B,EAAE,GAAG,EAAE,YAAY,EAAC;QACnF,SAAS,EAAE;YACT,CAAC,oGAAoG,CAAC;YACtG,CAAC,OAAO,EAAE,EAAC,SAAS,EAAE,OAAO,EAAC,EAAE,kCAAkC,CAAC;SACpE;QACD,gBAAgB,EAAE,IAAI;KACvB,CAAC,CAAA;IAEF,MAAM,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,SAAS,EAAE,EAAC,IAAI,EAAE,aAAa,EAAC,CAAC,CAAA;IAE/D,WAAW,CAAC,wCAAwC,CAAC,CAAA;IACrD,aAAa,CAAC,EAAC,QAAQ,EAAE,aAAa,EAAC,CAAC,CAAA;AAC1C,CAAC","sourcesContent":["import {extractGraphQLErrors} from '../api/graphql.js'\nimport {commandEventOutputMode} from '../command-event-context.js'\nimport {recordEvent} from '../../../public/node/analytics.js'\nimport {adminFqdn} from '../../../public/node/context/fqdn.js'\nimport {AbortError} from '../../../public/node/error.js'\nimport {outputDebug} from '../../../public/node/output.js'\nimport {isCI, openURL} from '../../../public/node/system.js'\nimport {isTTY, keypress, renderInfo, renderSuccess} from '../../../public/node/ui.js'\n\nconst TRUST_CHALLENGE_ERROR_CODE = 'CHALLENGE_REQUIRED'\n// Enter or Space. `theme dev` binds other single keys on the same stdin to its own shortcuts.\nconst CONTINUE_KEYS = ['\\r', '\\n', ' ']\n\n/**\n * The abort that ends a Trust Battery challenge: the session cannot answer it, or\n * the request was challenged again after the verification.\n */\nexport class TrustChallengeError extends AbortError {}\n\nlet pendingChallenge: Promise<void> | undefined\nlet completedChallenges = 0\n\n/**\n * Runs a theme Admin API request and, when Core answers with a Trust Battery\n * challenge, waits for the user to complete it in the browser before retrying once.\n *\n * A request that starts while another request's challenge is still being completed\n * waits for that challenge first, so it is sent once instead of being challenged\n * itself and then retried. A request that was already in flight when a challenge\n * was completed retries without a second prompt, unless another prompt is open:\n * then it waits for that prompt too.\n *\n * @param request - The request to run and possibly retry.\n * @returns The result of the request.\n */\nexport async function withTrustChallengeRetry<T>(request: () => Promise<T>): Promise<T> {\n if (pendingChallenge) await pendingChallenge\n const completedChallengesAtStart = completedChallenges\n\n const attempt = await attemptRequest(request)\n if (!attempt.challenged) return attempt.result\n\n const verifiedSinceStart = completedChallenges > completedChallengesAtStart\n if (pendingChallenge || !verifiedSinceStart) await completeTrustChallenge(attempt.challengeUrl)\n\n const retry = await attemptRequest(request)\n if (!retry.challenged) return retry.result\n\n recordEvent('theme-api:trust-challenge:retry-challenged')\n throw new TrustChallengeError('Shopify still needs to verify your identity before it can continue.', undefined, [\n ['Complete the verification at', {link: {url: retry.challengeUrl}}, 'then run the command again.'],\n ])\n}\n\ntype RequestAttempt<T> = {challenged: false; result: T} | {challenged: true; challengeUrl: string}\n\n/**\n * Runs the request once. A Trust Battery challenge becomes a result with the challenge URL;\n * any other error is thrown.\n */\nasync function attemptRequest<T>(request: () => Promise<T>): Promise<RequestAttempt<T>> {\n try {\n return {challenged: false, result: await request()}\n } catch (error) {\n const challengeUrl = await trustChallengeUrl(error)\n if (!challengeUrl) throw error\n return {challenged: true, challengeUrl}\n }\n}\n\n/**\n * Extracts the challenge URL from a Trust Battery `CHALLENGE_REQUIRED` GraphQL error.\n *\n * Only URLs on the Admin host are trusted. Anything else is not treated as a\n * challenge and the original error surfaces through normal error handling.\n *\n * @param error - The error thrown by an Admin API request.\n * @returns The challenge URL, or undefined when the error is not a trust challenge.\n */\nexport async function trustChallengeUrl(error: unknown): Promise<string | undefined> {\n const challengeError = extractGraphQLErrors(error).find(\n (graphQLError) => graphQLError.extensions?.code === TRUST_CHALLENGE_ERROR_CODE,\n )\n const redirectTo = challengeError?.extensions?.redirect_to\n if (typeof redirectTo !== 'string') return undefined\n\n if (!(await isAdminUrl(redirectTo))) {\n outputDebug(`Ignoring trust challenge with an unexpected URL host: ${redirectTo}`)\n return undefined\n }\n\n return redirectTo\n}\n\nasync function isAdminUrl(url: string): Promise<boolean> {\n try {\n const parsedUrl = new URL(url)\n return parsedUrl.protocol === 'https:' && parsedUrl.hostname === (await adminFqdn())\n // eslint-disable-next-line no-catch-all/no-catch-all\n } catch {\n return false\n }\n}\n\n/**\n * Concurrent theme requests (for example the parallel batches of `theme push`)\n * all receive the same challenge. Share one prompt so the browser opens once and\n * every waiting request retries after the same verification.\n */\nasync function completeTrustChallenge(challengeUrl: string): Promise<void> {\n pendingChallenge ??= promptForTrustChallenge(challengeUrl)\n .then(() => {\n completedChallenges += 1\n })\n .finally(() => {\n pendingChallenge = undefined\n })\n return pendingChallenge\n}\n\nasync function promptForTrustChallenge(challengeUrl: string): Promise<void> {\n recordEvent('theme-api:trust-challenge:required')\n\n if (isCI() || !isTTY() || commandEventOutputMode() === 'json') {\n recordEvent('theme-api:trust-challenge:non-interactive')\n throw new TrustChallengeError('Shopify needs to verify your identity before it can continue.', undefined, [\n [\n 'Open',\n {link: {url: challengeUrl}},\n \"in a browser where you're logged in to Shopify with the same account you use in Shopify CLI, complete the verification, then run the command again.\",\n ],\n ])\n }\n\n const opened = await openURL(challengeUrl)\n recordEvent(opened ? 'theme-api:trust-challenge:browser-opened' : 'theme-api:trust-challenge:browser-not-opened')\n\n // Everything the user needs is inside the banner: long-running commands such as\n // `theme push` redraw an Ink progress bar that erases any plain line written after it.\n renderInfo({\n headline: 'Verify your identity to continue.',\n body: opened\n ? 'A verification page opened in your browser.'\n : 'Shopify needs to verify your identity before it can continue.',\n link: opened ? undefined : {label: 'Open the verification page', url: challengeUrl},\n nextSteps: [\n ['Complete the verification in your browser, logged in with the same account you use in Shopify CLI.'],\n ['Press', {userInput: 'Enter'}, 'here once you have completed it.'],\n ],\n orderedNextSteps: true,\n })\n\n await keypress(process.stdin, undefined, {keys: CONTINUE_KEYS})\n\n recordEvent('theme-api:trust-challenge:acknowledged')\n renderSuccess({headline: 'Continuing.'})\n}\n"]}