@shopify/cli-kit 5.0.0 → 5.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/private/node/session/exchange.d.ts +3 -0
- package/dist/private/node/session/exchange.js +93 -5
- package/dist/private/node/session/exchange.js.map +1 -1
- package/dist/private/node/ui/components/Table/Table.d.ts +8 -0
- package/dist/private/node/ui/components/Table/Table.js +18 -2
- package/dist/private/node/ui/components/Table/Table.js.map +1 -1
- package/dist/public/common/version.d.ts +1 -1
- package/dist/public/common/version.js +1 -1
- package/dist/public/common/version.js.map +1 -1
- package/dist/public/node/session.d.ts +2 -1
- package/dist/public/node/session.js +8 -5
- package/dist/public/node/session.js.map +1 -1
- package/dist/public/node/ui.js +13 -3
- package/dist/public/node/ui.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -4,6 +4,9 @@ import { Result } from '../../../public/node/result.js';
|
|
|
4
4
|
export declare class InvalidGrantError extends Error {
|
|
5
5
|
}
|
|
6
6
|
export declare class InvalidRequestError extends Error {
|
|
7
|
+
/** Identity's `error_description`, when it sent one. Says what about the request was invalid. */
|
|
8
|
+
readonly description: string;
|
|
9
|
+
constructor(description?: string);
|
|
7
10
|
}
|
|
8
11
|
export interface ExchangeScopes {
|
|
9
12
|
admin: string[];
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { applicationId, clientId as getIdentityClientId } from './identity.js';
|
|
2
2
|
import { tokenExchangeScopes } from './scopes.js';
|
|
3
|
-
import { automationTokenAuthMethod } from './automation-token.js';
|
|
3
|
+
import { automationTokenAuthMethod, automationTokenVariable } from './automation-token.js';
|
|
4
|
+
import { environmentVariables } from '../constants.js';
|
|
4
5
|
import { identityFqdn } from '../../../public/node/context/fqdn.js';
|
|
5
6
|
import { shopifyFetch } from '../../../public/node/http.js';
|
|
6
7
|
import { err, ok } from '../../../public/node/result.js';
|
|
@@ -12,9 +13,96 @@ import * as jose from 'jose';
|
|
|
12
13
|
export class InvalidGrantError extends Error {
|
|
13
14
|
}
|
|
14
15
|
export class InvalidRequestError extends Error {
|
|
16
|
+
/** Identity's `error_description`, when it sent one. Says what about the request was invalid. */
|
|
17
|
+
description;
|
|
18
|
+
constructor(description = '') {
|
|
19
|
+
super();
|
|
20
|
+
this.description = description;
|
|
21
|
+
}
|
|
15
22
|
}
|
|
16
23
|
class InvalidTargetError extends AbortError {
|
|
17
24
|
}
|
|
25
|
+
function automationTokenExchangeFailure(error) {
|
|
26
|
+
if (!(error instanceof InvalidRequestError))
|
|
27
|
+
return 'unknown';
|
|
28
|
+
// Identity's two ways of saying the token holds no scope the exchange asked for. Its third 'scope' message,
|
|
29
|
+
// "specified scopes cannot be granted to this audience", is about the CLI's own request, so it stays generic.
|
|
30
|
+
if (error.description.includes("Invalid 'scope' value: new scopes must not be specified") ||
|
|
31
|
+
error.description.includes("Invalid 'scope' value: no useable scope")) {
|
|
32
|
+
return 'missing_access';
|
|
33
|
+
}
|
|
34
|
+
if (error.description.includes("Invalid 'subject_token' value"))
|
|
35
|
+
return 'rejected_token';
|
|
36
|
+
return 'unknown';
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* The capability a service account needs for the scopes an exchange asks for, named as the Shopify admin names it.
|
|
40
|
+
*/
|
|
41
|
+
function capabilityForScopes(scopes) {
|
|
42
|
+
if (scopes.includes('https://api.shopify.com/auth/organization.apps.manage'))
|
|
43
|
+
return 'App development';
|
|
44
|
+
if (scopes.includes('https://api.shopify.com/auth/organization.store-management'))
|
|
45
|
+
return 'Dev store management';
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The error shown when an automation token can't be exchanged for an API's access token.
|
|
50
|
+
*
|
|
51
|
+
* Says what Identity refused, so a token that merely lacks a capability isn't reported as wrong or expired.
|
|
52
|
+
*/
|
|
53
|
+
class AutomationTokenExchangeError extends AbortError {
|
|
54
|
+
constructor(apiName, scopes, cause) {
|
|
55
|
+
const { message, tryMessage } = automationTokenExchangeMessages(apiName, scopes, cause);
|
|
56
|
+
super(message, tryMessage);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function automationTokenExchangeMessages(apiName, scopes, cause) {
|
|
60
|
+
const prettyName = apiName.replace(/-/g, ' ').replace(/\b\w/g, (char) => char.toUpperCase());
|
|
61
|
+
const variable = automationTokenVariable() ?? environmentVariables.serviceAccountToken;
|
|
62
|
+
const admin = ['in the Shopify admin under', { subdued: 'Settings > Users > Security' }];
|
|
63
|
+
switch (automationTokenExchangeFailure(cause)) {
|
|
64
|
+
case 'missing_access': {
|
|
65
|
+
const capability = capabilityForScopes(scopes);
|
|
66
|
+
const access = capability ? `${capability} access` : `the access the ${prettyName} API needs`;
|
|
67
|
+
if (variable === environmentVariables.serviceAccountToken) {
|
|
68
|
+
return {
|
|
69
|
+
message: `The service account behind ${variable} doesn't have ${access}.`,
|
|
70
|
+
tryMessage: [
|
|
71
|
+
'Give it',
|
|
72
|
+
...(capability ? [{ bold: capability }, 'access'] : ['that access']),
|
|
73
|
+
...admin,
|
|
74
|
+
{ char: ',' },
|
|
75
|
+
'or use a token from a service account that has it.',
|
|
76
|
+
],
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
// Setting the new variable beside the old one is refused as a conflict, so say to unset it too.
|
|
80
|
+
return {
|
|
81
|
+
message: `The token in ${variable} doesn't have ${access}.`,
|
|
82
|
+
tryMessage: [
|
|
83
|
+
'Use a service account token that has it: create one',
|
|
84
|
+
...admin,
|
|
85
|
+
{ char: ',' },
|
|
86
|
+
'set it in',
|
|
87
|
+
{ command: environmentVariables.serviceAccountToken },
|
|
88
|
+
'and unset',
|
|
89
|
+
{ command: variable },
|
|
90
|
+
{ char: '.' },
|
|
91
|
+
],
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
case 'rejected_token':
|
|
95
|
+
return {
|
|
96
|
+
message: `The token in ${variable} was rejected: it has been revoked, has expired, or isn't a valid token.`,
|
|
97
|
+
tryMessage: ['Check the value, or generate a new token', ...admin, { char: '.' }],
|
|
98
|
+
};
|
|
99
|
+
case 'unknown':
|
|
100
|
+
return {
|
|
101
|
+
message: `The custom token provided can't be used for the ${prettyName} API.`,
|
|
102
|
+
tryMessage: 'Ensure the token is correct and not expired.',
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
}
|
|
18
106
|
/**
|
|
19
107
|
* Given an identity token, request an application token.
|
|
20
108
|
* @param identityToken - access token obtained in a previous step
|
|
@@ -74,7 +162,7 @@ async function exchangeAppAutomationTokenForAccessToken(apiName, token, scopes)
|
|
|
74
162
|
const prettyName = apiName.replace(/-/g, ' ').replace(/\b\w/g, (char) => char.toUpperCase());
|
|
75
163
|
const reason = error instanceof Error ? error.message.slice(0, 200) : 'unknown error';
|
|
76
164
|
outputDebug(`Token exchange for the ${prettyName} API failed: ${reason}`);
|
|
77
|
-
throw new
|
|
165
|
+
throw new AutomationTokenExchangeError(apiName, scopes, error);
|
|
78
166
|
}
|
|
79
167
|
}
|
|
80
168
|
/**
|
|
@@ -153,7 +241,7 @@ export async function requestAppToken(api, token, scopes = [], store) {
|
|
|
153
241
|
const appToken = buildApplicationToken(value);
|
|
154
242
|
return { [identifier]: appToken };
|
|
155
243
|
}
|
|
156
|
-
function tokenRequestErrorHandler({ error, store }) {
|
|
244
|
+
function tokenRequestErrorHandler({ error, description, store }) {
|
|
157
245
|
const invalidTargetErrorMessage = `You are not authorized to use the CLI to develop in the provided store${store ? `: ${store}` : '.'}`;
|
|
158
246
|
if (error === 'invalid_grant') {
|
|
159
247
|
// There's an scenario when Identity returns "invalid_grant" when trying to refresh the token
|
|
@@ -163,7 +251,7 @@ function tokenRequestErrorHandler({ error, store }) {
|
|
|
163
251
|
if (error === 'invalid_request') {
|
|
164
252
|
// There's an scenario when Identity returns "invalid_request" when exchanging an identity token.
|
|
165
253
|
// This means the token is invalid. We clear the session and throw an error to let the caller know.
|
|
166
|
-
return new InvalidRequestError();
|
|
254
|
+
return new InvalidRequestError(description);
|
|
167
255
|
}
|
|
168
256
|
if (error === 'invalid_target') {
|
|
169
257
|
return new InvalidTargetError(invalidTargetErrorMessage, '', [
|
|
@@ -198,7 +286,7 @@ async function tokenRequest(params) {
|
|
|
198
286
|
const rawDescription = typeof payload.error_description === 'string' ? payload.error_description : '';
|
|
199
287
|
const oauthDescription = rawDescription.slice(0, 200);
|
|
200
288
|
outputDebug(`Token request to Identity failed with status ${res.status}: ${oauthError}${oauthDescription === '' ? '' : ` - ${oauthDescription}`}`);
|
|
201
|
-
return err({ error: oauthError, store: params.store });
|
|
289
|
+
return err({ error: oauthError, description: oauthDescription, store: params.store });
|
|
202
290
|
}
|
|
203
291
|
catch (error) {
|
|
204
292
|
if (error instanceof SyntaxError) {
|
|
@@ -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 {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"]}
|
|
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, automationTokenVariable} from './automation-token.js'\nimport {environmentVariables} from '../constants.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 {type TokenItem} from '../../../public/node/ui.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 {\n /** Identity's `error_description`, when it sent one. Says what about the request was invalid. */\n readonly description: string\n\n constructor(description = '') {\n super()\n this.description = description\n }\n}\nclass InvalidTargetError extends AbortError {}\n\n/**\n * Why Identity refused to exchange an automation token, read from its `error_description`.\n *\n * - `missing_access`: the token doesn't hold a scope the exchange asked for, so the account lacks that capability.\n * - `rejected_token`: the token itself was refused: revoked, expired, malformed, or not a token Identity knows.\n * - `unknown`: anything else, including a network failure.\n */\ntype AutomationTokenExchangeFailure = 'missing_access' | 'rejected_token' | 'unknown'\n\nfunction automationTokenExchangeFailure(error: unknown): AutomationTokenExchangeFailure {\n if (!(error instanceof InvalidRequestError)) return 'unknown'\n // Identity's two ways of saying the token holds no scope the exchange asked for. Its third 'scope' message,\n // \"specified scopes cannot be granted to this audience\", is about the CLI's own request, so it stays generic.\n if (\n error.description.includes(\"Invalid 'scope' value: new scopes must not be specified\") ||\n error.description.includes(\"Invalid 'scope' value: no useable scope\")\n ) {\n return 'missing_access'\n }\n if (error.description.includes(\"Invalid 'subject_token' value\")) return 'rejected_token'\n return 'unknown'\n}\n\n/**\n * The capability a service account needs for the scopes an exchange asks for, named as the Shopify admin names it.\n */\nfunction capabilityForScopes(scopes: string[]): string | undefined {\n if (scopes.includes('https://api.shopify.com/auth/organization.apps.manage')) return 'App development'\n if (scopes.includes('https://api.shopify.com/auth/organization.store-management')) return 'Dev store management'\n return undefined\n}\n\n/**\n * The error shown when an automation token can't be exchanged for an API's access token.\n *\n * Says what Identity refused, so a token that merely lacks a capability isn't reported as wrong or expired.\n */\nclass AutomationTokenExchangeError extends AbortError {\n constructor(apiName: API, scopes: string[], cause: unknown) {\n const {message, tryMessage} = automationTokenExchangeMessages(apiName, scopes, cause)\n super(message, tryMessage)\n }\n}\n\nfunction automationTokenExchangeMessages(\n apiName: API,\n scopes: string[],\n cause: unknown,\n): {message: string; tryMessage: TokenItem} {\n const prettyName = apiName.replace(/-/g, ' ').replace(/\\b\\w/g, (char) => char.toUpperCase())\n const variable = automationTokenVariable() ?? environmentVariables.serviceAccountToken\n const admin: TokenItem = ['in the Shopify admin under', {subdued: 'Settings > Users > Security'}]\n\n switch (automationTokenExchangeFailure(cause)) {\n case 'missing_access': {\n const capability = capabilityForScopes(scopes)\n const access = capability ? `${capability} access` : `the access the ${prettyName} API needs`\n if (variable === environmentVariables.serviceAccountToken) {\n return {\n message: `The service account behind ${variable} doesn't have ${access}.`,\n tryMessage: [\n 'Give it',\n ...(capability ? [{bold: capability}, 'access'] : ['that access']),\n ...admin,\n {char: ','},\n 'or use a token from a service account that has it.',\n ],\n }\n }\n // Setting the new variable beside the old one is refused as a conflict, so say to unset it too.\n return {\n message: `The token in ${variable} doesn't have ${access}.`,\n tryMessage: [\n 'Use a service account token that has it: create one',\n ...admin,\n {char: ','},\n 'set it in',\n {command: environmentVariables.serviceAccountToken},\n 'and unset',\n {command: variable},\n {char: '.'},\n ],\n }\n }\n case 'rejected_token':\n return {\n message: `The token in ${variable} was rejected: it has been revoked, has expired, or isn't a valid token.`,\n tryMessage: ['Check the value, or generate a new token', ...admin, {char: '.'}],\n }\n case 'unknown':\n return {\n message: `The custom token provided can't be used for the ${prettyName} API.`,\n tryMessage: 'Ensure the token is correct and not expired.',\n }\n }\n}\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 AutomationTokenExchangeError(apiName, scopes, error)\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, description, 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(description)\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 /** Identity's `error_description`, truncated. Empty when it sent none. */\n description: 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, description: oauthDescription, 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"]}
|
|
@@ -11,5 +11,13 @@ export interface TableProps<T extends ScalarDict> {
|
|
|
11
11
|
rows: T[];
|
|
12
12
|
columns: TableColumn<T>;
|
|
13
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* The width the table needs to show every cell on one line: the widest value or header of each column, plus the
|
|
16
|
+
* gap between columns.
|
|
17
|
+
*
|
|
18
|
+
* @param props - The table's rows and columns.
|
|
19
|
+
* @returns The width in characters.
|
|
20
|
+
*/
|
|
21
|
+
export declare function tableWidth<T extends ScalarDict>(props: TableProps<T>): number;
|
|
14
22
|
declare function Table<T extends ScalarDict>({ rows, columns: columnsConfiguration }: TableProps<T>): React.JSX.Element;
|
|
15
23
|
export { Table };
|
|
@@ -2,8 +2,9 @@ import { Row } from './Row.js';
|
|
|
2
2
|
import { unstyled } from '../../../../../public/node/output.js';
|
|
3
3
|
import React from 'react';
|
|
4
4
|
import { Box } from 'ink';
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
const COLUMN_GAP = 2;
|
|
6
|
+
function tableColumns({ rows, columns: columnsConfiguration }) {
|
|
7
|
+
return Object.entries(columnsConfiguration).map(([key, { header, color }]) => {
|
|
7
8
|
const headerWidth = String(header ?? key).length;
|
|
8
9
|
const columnWidths = rows.map((row) => {
|
|
9
10
|
const value = row[key];
|
|
@@ -18,6 +19,21 @@ function Table({ rows, columns: columnsConfiguration }) {
|
|
|
18
19
|
color,
|
|
19
20
|
};
|
|
20
21
|
});
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* The width the table needs to show every cell on one line: the widest value or header of each column, plus the
|
|
25
|
+
* gap between columns.
|
|
26
|
+
*
|
|
27
|
+
* @param props - The table's rows and columns.
|
|
28
|
+
* @returns The width in characters.
|
|
29
|
+
*/
|
|
30
|
+
export function tableWidth(props) {
|
|
31
|
+
const columns = tableColumns(props);
|
|
32
|
+
const gaps = Math.max(columns.length - 1, 0) * COLUMN_GAP;
|
|
33
|
+
return columns.reduce((width, column) => width + column.width, gaps);
|
|
34
|
+
}
|
|
35
|
+
function Table({ rows, columns: columnsConfiguration }) {
|
|
36
|
+
const columns = tableColumns({ rows, columns: columnsConfiguration });
|
|
21
37
|
const headings = Object.entries(columnsConfiguration).reduce((headings, [column, { header }]) => ({ ...headings, [column]: header ?? column }), {});
|
|
22
38
|
return (React.createElement(Box, { flexDirection: "column" },
|
|
23
39
|
React.createElement(Row, { rowKey: "heading", fillerChar: " ", columns: columns, data: headings, ignoreColumnColor: true }),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Table.js","sourceRoot":"","sources":["../../../../../../src/private/node/ui/components/Table/Table.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAC,GAAG,EAAC,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"Table.js","sourceRoot":"","sources":["../../../../../../src/private/node/ui/components/Table/Table.tsx"],"names":[],"mappings":"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","sourcesContent":["import {Row} from './Row.js'\nimport ScalarDict from './ScalarDict.js'\nimport {Column} from './Column.js'\nimport {unstyled} from '../../../../../public/node/output.js'\nimport React from 'react'\nimport {Box} from 'ink'\nimport {ForegroundColor} from 'chalk'\n\nexport type TableColumn<T> = {\n [column in keyof T]: {header?: string; color?: ForegroundColor | 'dim'}\n}\n\nexport interface TableProps<T extends ScalarDict> {\n rows: T[]\n columns: TableColumn<T>\n}\n\nconst COLUMN_GAP = 2\n\nfunction tableColumns<T extends ScalarDict>({rows, columns: columnsConfiguration}: TableProps<T>): Column<T>[] {\n return Object.entries(columnsConfiguration).map(([key, {header, color}]) => {\n const headerWidth = String(header ?? key).length\n const columnWidths = rows.map((row) => {\n const value = row[key]\n\n if (value === undefined || value === null) {\n return 0\n }\n\n return unstyled(String(value)).length\n })\n\n return {\n name: key,\n width: Math.max(...columnWidths, headerWidth),\n color,\n }\n })\n}\n\n/**\n * The width the table needs to show every cell on one line: the widest value or header of each column, plus the\n * gap between columns.\n *\n * @param props - The table's rows and columns.\n * @returns The width in characters.\n */\nexport function tableWidth<T extends ScalarDict>(props: TableProps<T>): number {\n const columns = tableColumns(props)\n const gaps = Math.max(columns.length - 1, 0) * COLUMN_GAP\n return columns.reduce((width, column) => width + column.width, gaps)\n}\n\nfunction Table<T extends ScalarDict>({rows, columns: columnsConfiguration}: TableProps<T>) {\n const columns = tableColumns({rows, columns: columnsConfiguration})\n const headings = Object.entries(columnsConfiguration).reduce(\n (headings, [column, {header}]) => ({...headings, [column]: header ?? column}),\n {},\n )\n\n return (\n <Box flexDirection=\"column\">\n <Row rowKey=\"heading\" fillerChar=\" \" columns={columns} data={headings} ignoreColumnColor />\n <Row rowKey=\"separator\" fillerChar=\"─\" columns={columns} data={{}} ignoreColumnColor />\n {rows.map((row, index) => {\n const key = `row-${index}`\n\n return (\n <Box flexDirection=\"column\" key={key}>\n <Row rowKey={`data-${key}`} fillerChar=\" \" columns={columns} data={row} />\n </Box>\n )\n })}\n </Box>\n )\n}\n\nexport {Table}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const CLI_KIT_VERSION = "5.0.
|
|
1
|
+
export declare const CLI_KIT_VERSION = "5.0.1";
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const CLI_KIT_VERSION = '5.0.
|
|
1
|
+
export const CLI_KIT_VERSION = '5.0.1';
|
|
2
2
|
//# sourceMappingURL=version.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.js","sourceRoot":"","sources":["../../../src/public/common/version.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,eAAe,GAAG,OAAO,CAAA","sourcesContent":["export const CLI_KIT_VERSION = '5.0.
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../../../src/public/common/version.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,eAAe,GAAG,OAAO,CAAA","sourcesContent":["export const CLI_KIT_VERSION = '5.0.1'\n"]}
|
|
@@ -181,7 +181,8 @@ export declare function logout(): Promise<void>;
|
|
|
181
181
|
*
|
|
182
182
|
* See `ensureAuthenticatedAdmin` for access on behalf of a user.
|
|
183
183
|
*
|
|
184
|
-
* @param storeFqdn - Store
|
|
184
|
+
* @param storeFqdn - Store handle or domain, normalized with `normalizeStoreFqdn`. Unsupported URL components are
|
|
185
|
+
* rejected before the client secret is sent.
|
|
185
186
|
* @param clientId - Client ID of the app.
|
|
186
187
|
* @param clientSecret - Client secret of the app.
|
|
187
188
|
* @returns The access token for the Admin API.
|
|
@@ -2,6 +2,7 @@ import { shopifyFetch } from './http.js';
|
|
|
2
2
|
import { nonRandomUUID } from './crypto.js';
|
|
3
3
|
import { getAppAutomationToken } from './environment.js';
|
|
4
4
|
import { AbortError, BugError } from './error.js';
|
|
5
|
+
import { normalizeStoreFqdn } from './context/fqdn.js';
|
|
5
6
|
import { outputContent, outputToken, outputDebug } from './output.js';
|
|
6
7
|
import * as sessionStore from '../../private/node/session/store.js';
|
|
7
8
|
import { automationTokenVariable } from '../../private/node/session/automation-token.js';
|
|
@@ -271,18 +272,20 @@ export function logout() {
|
|
|
271
272
|
*
|
|
272
273
|
* See `ensureAuthenticatedAdmin` for access on behalf of a user.
|
|
273
274
|
*
|
|
274
|
-
* @param storeFqdn - Store
|
|
275
|
+
* @param storeFqdn - Store handle or domain, normalized with `normalizeStoreFqdn`. Unsupported URL components are
|
|
276
|
+
* rejected before the client secret is sent.
|
|
275
277
|
* @param clientId - Client ID of the app.
|
|
276
278
|
* @param clientSecret - Client secret of the app.
|
|
277
279
|
* @returns The access token for the Admin API.
|
|
278
280
|
*/
|
|
279
281
|
export async function ensureAuthenticatedAdminAsApp(storeFqdn, clientId, clientSecret) {
|
|
282
|
+
const normalizedStoreFqdn = normalizeStoreFqdn(storeFqdn);
|
|
280
283
|
const bodyData = {
|
|
281
284
|
client_id: clientId,
|
|
282
285
|
client_secret: clientSecret,
|
|
283
286
|
grant_type: 'client_credentials',
|
|
284
287
|
};
|
|
285
|
-
const tokenResponse = await shopifyFetch(`https://${
|
|
288
|
+
const tokenResponse = await shopifyFetch(`https://${normalizedStoreFqdn}/admin/oauth/access_token`, {
|
|
286
289
|
method: 'POST',
|
|
287
290
|
headers: {
|
|
288
291
|
'Content-Type': 'application/json',
|
|
@@ -292,13 +295,13 @@ export async function ensureAuthenticatedAdminAsApp(storeFqdn, clientId, clientS
|
|
|
292
295
|
const body = await tokenResponse.text();
|
|
293
296
|
if (tokenResponse.status === 400) {
|
|
294
297
|
if (body.includes('app_not_installed')) {
|
|
295
|
-
throw new AbortError(outputContent `App is not installed on ${outputToken.green(
|
|
298
|
+
throw new AbortError(outputContent `App is not installed on ${outputToken.green(normalizedStoreFqdn)}. Try running ${outputToken.genericShellCommand(`shopify app dev`)} to connect your app to the shop.`);
|
|
296
299
|
}
|
|
297
|
-
throw new AbortError(`Failed to get access token for app ${clientId} on store ${
|
|
300
|
+
throw new AbortError(`Failed to get access token for app ${clientId} on store ${normalizedStoreFqdn}: ${tokenResponse.statusText}`);
|
|
298
301
|
}
|
|
299
302
|
try {
|
|
300
303
|
const tokenJson = JSON.parse(body);
|
|
301
|
-
return { token: tokenJson.access_token, storeFqdn };
|
|
304
|
+
return { token: tokenJson.access_token, storeFqdn: normalizedStoreFqdn };
|
|
302
305
|
}
|
|
303
306
|
catch (error) {
|
|
304
307
|
if (error instanceof SyntaxError) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../../src/public/node/session.ts"],"names":[],"mappings":"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","sourcesContent":["import {shopifyFetch} from './http.js'\nimport {nonRandomUUID} from './crypto.js'\nimport {getAppAutomationToken} from './environment.js'\nimport {AbortError, BugError} from './error.js'\nimport {outputContent, outputToken, outputDebug} from './output.js'\nimport * as sessionStore from '../../private/node/session/store.js'\nimport {automationTokenVariable} from '../../private/node/session/automation-token.js'\nimport {environmentVariables} from '../../private/node/constants.js'\nimport {\n exchangeCustomPartnerToken,\n exchangeAppAutomationTokenForAppManagementAccessToken,\n exchangeAppAutomationTokenForBusinessPlatformAccessToken,\n} from '../../private/node/session/exchange.js'\nimport {\n AdminAPIScope,\n AppManagementAPIScope,\n BusinessPlatformScope,\n EnsureAuthenticatedAdditionalOptions,\n PartnersAPIScope,\n StorefrontRendererScope,\n ensureAuthenticated,\n setCommandSessionId,\n setLastSeenAuthMethod,\n setLastSeenUserIdAfterAuth,\n} from '../../private/node/session.js'\nimport {isThemeAccessSession} from '../../private/node/api/rest.js'\nimport {resolvePermanentStoreFqdn} from '../../private/node/session/permanent-store-domain.js'\n\n/**\n * Session Object to access the Admin API, includes the token and the store FQDN.\n */\nexport interface AdminSession {\n token: string\n storeFqdn: string\n}\n\n/**\n * Session Object for Partners API and App Management API access.\n */\nexport interface Session {\n token: string\n businessPlatformToken: string\n accountInfo: AccountInfo\n userId: string\n}\n\nexport type AccountInfo = UserAccountInfo | ServiceAccountInfo | UnknownAccountInfo\n\n/**\n * Records the user ID that should be attached to command analytics for this process.\n *\n * @param userId - User identifier to report on the command analytics event.\n */\nexport function setLastSeenUserId(userId: string): void {\n setLastSeenUserIdAfterAuth(userId)\n}\n\n/**\n * Finds a stored Shopify account session by alias without changing the current session.\n *\n * @param alias - The account alias to find.\n * @returns The matching session ID, or undefined if no session matches.\n */\nexport async function findSessionIdByAlias(alias: string): Promise<string | undefined> {\n return sessionStore.findSessionByAlias(alias)\n}\n\n/**\n * Selects a stored Shopify account session by alias for the current command process.\n *\n * @param alias - The account alias to select. Passing undefined clears the command selection.\n */\nexport async function setCurrentSessionAlias(alias?: string): Promise<void> {\n if (!alias) {\n setCommandSessionId(undefined)\n return\n }\n\n const sessionId = await findSessionIdByAlias(alias)\n if (!sessionId) {\n throw new AbortError(\n outputContent`No authenticated account found for alias ${outputToken.yellow(alias)}.`,\n outputContent`Run ${outputToken.genericShellCommand(`shopify auth login`)} first.`,\n )\n }\n setCommandSessionId(sessionId)\n}\n\ninterface UserAccountInfo {\n type: 'UserAccount'\n email: string\n}\n\ninterface ServiceAccountInfo {\n type: 'ServiceAccount'\n orgName: string\n}\n\ninterface UnknownAccountInfo {\n type: 'UnknownAccount'\n}\n\n/**\n * Type guard to check if an account is a UserAccount.\n *\n * @param account - The account to check.\n * @returns True if the account is a UserAccount.\n */\nexport function isUserAccount(account: AccountInfo): account is UserAccountInfo {\n return account.type === 'UserAccount'\n}\n\n/**\n * Type guard to check if an account is a ServiceAccount.\n *\n * @param account - The account to check.\n * @returns True if the account is a ServiceAccount.\n */\nexport function isServiceAccount(account: AccountInfo): account is ServiceAccountInfo {\n return account.type === 'ServiceAccount'\n}\n\n/**\n * Ensure that we have a valid session with no particular scopes.\n *\n * @param env - Optional environment variables to use.\n * @param options - Optional extra options to use.\n * @returns The user ID.\n */\nexport async function ensureAuthenticatedUser(\n env = process.env,\n options: EnsureAuthenticatedAdditionalOptions = {},\n): Promise<{userId: string}> {\n outputDebug(outputContent`Ensuring that the user is authenticated with no particular scopes`)\n const tokens = await ensureAuthenticated({}, env, options)\n return {userId: tokens.userId}\n}\n\n/**\n * Ensure that we have a valid session to access the Partners API.\n * If SHOPIFY_CLI_PARTNERS_TOKEN exists, that token will be used to obtain a valid Partners Token\n * If SHOPIFY_CLI_PARTNERS_TOKEN exists, scopes will be ignored.\n *\n * @param scopes - Optional array of extra scopes to authenticate with.\n * @param env - Optional environment variables to use.\n * @param options - Optional extra options to use.\n * @returns The access token for the Partners API.\n */\nexport async function ensureAuthenticatedPartners(\n scopes: PartnersAPIScope[] = [],\n env = process.env,\n options: EnsureAuthenticatedAdditionalOptions = {},\n): Promise<{token: string; userId: string}> {\n outputDebug(outputContent`Ensuring that the user is authenticated with the Partners API with the following scopes:\n${outputToken.json(scopes)}\n`)\n const envToken = getAppAutomationToken()\n if (envToken) {\n const result = await exchangeCustomPartnerToken(envToken)\n return {token: result.accessToken, userId: result.userId}\n }\n const tokens = await ensureAuthenticated({partnersApi: {scopes}}, env, options)\n if (!tokens.partners) {\n throw new BugError('No partners token found after ensuring authenticated')\n }\n return {token: tokens.partners, userId: tokens.userId}\n}\n\n/**\n * Ensure that we have a valid session to access the App Management API.\n *\n * @param options - Optional extra options to use.\n * @param appManagementScopes - Optional array of extra scopes to authenticate with.\n * @param businessPlatformScopes - Optional array of extra scopes to authenticate with.\n * @param env - Optional environment variables to use.\n * @returns The access token for the App Management API.\n */\nexport async function ensureAuthenticatedAppManagementAndBusinessPlatform(\n options: EnsureAuthenticatedAdditionalOptions = {},\n appManagementScopes: AppManagementAPIScope[] = [],\n businessPlatformScopes: BusinessPlatformScope[] = [],\n env = process.env,\n): Promise<{appManagementToken: string; userId: string; businessPlatformToken: string}> {\n outputDebug(outputContent`Ensuring that the user is authenticated with the App Management API with the following scopes:\n${outputToken.json(appManagementScopes)}\n`)\n\n const envToken = getAppAutomationToken()\n if (envToken) {\n const appManagmentToken = await exchangeAppAutomationTokenForAppManagementAccessToken(envToken)\n const businessPlatformToken = await exchangeAppAutomationTokenForBusinessPlatformAccessToken(envToken)\n\n return {\n appManagementToken: appManagmentToken.accessToken,\n userId: appManagmentToken.userId,\n businessPlatformToken: businessPlatformToken.accessToken,\n }\n }\n\n const tokens = await ensureAuthenticated(\n {appManagementApi: {scopes: appManagementScopes}, businessPlatformApi: {scopes: businessPlatformScopes}},\n env,\n options,\n )\n if (!tokens.appManagement || !tokens.businessPlatform) {\n throw new BugError('No App Management or Business Platform token found after ensuring authenticated')\n }\n\n return {\n appManagementToken: tokens.appManagement,\n userId: tokens.userId,\n businessPlatformToken: tokens.businessPlatform,\n }\n}\n\n/**\n * Ensure that we have a valid session to access the Storefront API.\n *\n * @param scopes - Optional array of extra scopes to authenticate with.\n * @param password - Optional password to use.\n * @param options - Optional extra options to use.\n * @returns The access token for the Storefront API.\n */\nexport async function ensureAuthenticatedStorefront(\n scopes: StorefrontRendererScope[] = [],\n password: string | undefined = undefined,\n options: EnsureAuthenticatedAdditionalOptions = {},\n): Promise<string> {\n if (password) {\n const session = {token: password, storeFqdn: ''}\n const authMethod = isThemeAccessSession(session) ? 'theme_access_token' : 'custom_app_token'\n setLastSeenAuthMethod(authMethod)\n setLastSeenUserIdAfterAuth(nonRandomUUID(password))\n return password\n }\n\n outputDebug(outputContent`Ensuring that the user is authenticated with the Storefront API with the following scopes:\n${outputToken.json(scopes)}\n`)\n const tokens = await ensureAuthenticated({storefrontRendererApi: {scopes}}, process.env, options)\n if (!tokens.storefront) {\n throw new BugError('No storefront token found after ensuring authenticated')\n }\n return tokens.storefront\n}\n\n/**\n * Ensure that we have a valid Admin session for the given store.\n *\n * @param store - Store fqdn to request auth for.\n * @param scopes - Optional array of extra scopes to authenticate with.\n * @param options - Optional extra options to use.\n * @returns The access token for the Admin API.\n */\nexport async function ensureAuthenticatedAdmin(\n store: string,\n scopes: AdminAPIScope[] = [],\n options: EnsureAuthenticatedAdditionalOptions = {},\n): Promise<AdminSession> {\n outputDebug(outputContent`Ensuring that the user is authenticated with the Admin API with the following scopes for the store ${outputToken.raw(\n store,\n )}:\n${outputToken.json(scopes)}\n`)\n const tokens = await ensureAuthenticated({adminApi: {scopes, storeFqdn: store}}, process.env, {\n ...options,\n })\n if (!tokens.admin) {\n throw new BugError('No admin token found after ensuring authenticated')\n }\n return tokens.admin\n}\n\n/**\n * Ensure that we have a valid session to access the Theme API.\n * If a password is provided, that token will be used against Theme Access API.\n * Otherwise, it will ensure that the user is authenticated with the Admin API.\n *\n * Theme Access passwords only work with the store's permanent `.myshopify.com` domain, so when the password is a\n * Theme Access password the returned session uses the permanent domain of `store`, even if `store` is another of the\n * store's domains.\n *\n * @param store - Store fqdn to request auth for.\n * @param password - Password generated from Theme Access app.\n * @param scopes - Optional array of extra scopes to authenticate with.\n * @param options - Optional extra options to use.\n * @returns The access token and store.\n */\nexport async function ensureAuthenticatedThemes(\n store: string,\n password: string | undefined,\n scopes: AdminAPIScope[] = [],\n options: EnsureAuthenticatedAdditionalOptions = {},\n): Promise<AdminSession> {\n outputDebug(outputContent`Ensuring that the user is authenticated with the Theme API with the following scopes:\n${outputToken.json(scopes)}\n`)\n if (password) {\n const session = {token: password, storeFqdn: store}\n const themeAccess = isThemeAccessSession(session)\n setLastSeenAuthMethod(themeAccess ? 'theme_access_token' : 'custom_app_token')\n setLastSeenUserIdAfterAuth(nonRandomUUID(password))\n if (themeAccess) session.storeFqdn = await resolvePermanentStoreFqdn(store)\n return session\n }\n return ensureAuthenticatedAdmin(store, scopes, options)\n}\n\n/**\n * Options for `ensureAuthenticatedBusinessPlatform`.\n */\nexport interface EnsureAuthenticatedBusinessPlatformOptions extends EnsureAuthenticatedAdditionalOptions {\n /**\n * Authenticate with the automation token set in the environment, when there is one, instead of the\n * logged-in user. Only commands that support automation tokens opt in; other callers, such as\n * Hydrogen's login, keep using the user's session.\n */\n allowAutomationToken?: boolean\n}\n\n/**\n * Ensure that we have a valid session to access the Business Platform API.\n *\n * @param scopes - Optional array of extra scopes to authenticate with. Ignored when an automation token is used.\n * @param options - Optional extra options to use.\n * @returns The access token for the Business Platform API.\n */\nexport async function ensureAuthenticatedBusinessPlatform(\n scopes: BusinessPlatformScope[] = [],\n options: EnsureAuthenticatedBusinessPlatformOptions = {},\n): Promise<string> {\n outputDebug(outputContent`Ensuring that the user is authenticated with the Business Platform API with the following scopes:\n${outputToken.json(scopes)}\n`)\n const {allowAutomationToken = false, ...authenticationOptions} = options\n const automationToken = allowAutomationToken ? getAppAutomationToken() : undefined\n if (automationToken) {\n // Request no explicit scopes, so Identity issues every Business Platform scope the token holds. The organizations\n // API admits a CLI token only with a scope such as organization.store-management, and naming that scope would\n // fail the exchange for app and Partner tokens, which don't hold it.\n const result = await exchangeAppAutomationTokenForBusinessPlatformAccessToken(automationToken, [])\n return result.accessToken\n }\n\n const tokens = await ensureAuthenticated({businessPlatformApi: {scopes}}, process.env, authenticationOptions)\n if (!tokens.businessPlatform) {\n throw new BugError('No business-platform token found after ensuring authenticated')\n }\n return tokens.businessPlatform\n}\n\n/**\n * Whether SHOPIFY_SERVICE_ACCOUNT_TOKEN is set, which makes it the automation token the CLI authenticates with.\n *\n * True even when it's empty or set together with another automation token variable. Commands use this to skip logins\n * saved on this machine, and authentication then reports any problem with the variables.\n *\n * @returns True when SHOPIFY_SERVICE_ACCOUNT_TOKEN is set.\n */\nexport function isServiceAccountTokenSet(): boolean {\n return automationTokenVariable() === environmentVariables.serviceAccountToken\n}\n\n/**\n * Fails when SHOPIFY_SERVICE_ACCOUNT_TOKEN is set, for commands that can't run with that token.\n *\n * Service account tokens can't log in as a user or call a store's Admin API, so commands that need either\n * refuse to run instead of quietly using the person's own login. `ensureAuthenticated` runs this check before any\n * login. Commands that run on a login saved by `shopify store auth` call it before loading that login.\n *\n * @throws AbortError when SHOPIFY_SERVICE_ACCOUNT_TOKEN is set.\n */\nexport function ensureNoServiceAccountToken(): void {\n if (!isServiceAccountTokenSet()) return\n const variable = environmentVariables.serviceAccountToken\n\n throw new AbortError(`This command can't use ${variable}.`, `Unset ${variable} to run it with your Shopify account.`)\n}\n\n/**\n * Logout from Shopify.\n *\n * @returns A promise that resolves when the logout is complete.\n */\nexport function logout(): Promise<void> {\n return sessionStore.remove()\n}\n\n/**\n * Ensure that we have a valid Admin session for the given store, with access on behalf of the app.\n *\n * See `ensureAuthenticatedAdmin` for access on behalf of a user.\n *\n * @param storeFqdn - Store fqdn to request auth for.\n * @param clientId - Client ID of the app.\n * @param clientSecret - Client secret of the app.\n * @returns The access token for the Admin API.\n */\nexport async function ensureAuthenticatedAdminAsApp(\n storeFqdn: string,\n clientId: string,\n clientSecret: string,\n): Promise<AdminSession> {\n const bodyData = {\n client_id: clientId,\n client_secret: clientSecret,\n grant_type: 'client_credentials',\n }\n const tokenResponse = await shopifyFetch(\n `https://${storeFqdn}/admin/oauth/access_token`,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(bodyData),\n },\n 'slow-request',\n )\n\n const body = await tokenResponse.text()\n\n if (tokenResponse.status === 400) {\n if (body.includes('app_not_installed')) {\n throw new AbortError(\n outputContent`App is not installed on ${outputToken.green(\n storeFqdn,\n )}. Try running ${outputToken.genericShellCommand(`shopify app dev`)} to connect your app to the shop.`,\n )\n }\n throw new AbortError(\n `Failed to get access token for app ${clientId} on store ${storeFqdn}: ${tokenResponse.statusText}`,\n )\n }\n try {\n const tokenJson = JSON.parse(body) as {access_token: string}\n return {token: tokenJson.access_token, storeFqdn}\n } catch (error) {\n if (error instanceof SyntaxError) {\n throw new AbortError(\n `Received invalid response from admin authentication service (HTTP ${tokenResponse.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"]}
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../../src/public/node/session.ts"],"names":[],"mappings":"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","sourcesContent":["import {shopifyFetch} from './http.js'\nimport {nonRandomUUID} from './crypto.js'\nimport {getAppAutomationToken} from './environment.js'\nimport {AbortError, BugError} from './error.js'\nimport {normalizeStoreFqdn} from './context/fqdn.js'\nimport {outputContent, outputToken, outputDebug} from './output.js'\nimport * as sessionStore from '../../private/node/session/store.js'\nimport {automationTokenVariable} from '../../private/node/session/automation-token.js'\nimport {environmentVariables} from '../../private/node/constants.js'\nimport {\n exchangeCustomPartnerToken,\n exchangeAppAutomationTokenForAppManagementAccessToken,\n exchangeAppAutomationTokenForBusinessPlatformAccessToken,\n} from '../../private/node/session/exchange.js'\nimport {\n AdminAPIScope,\n AppManagementAPIScope,\n BusinessPlatformScope,\n EnsureAuthenticatedAdditionalOptions,\n PartnersAPIScope,\n StorefrontRendererScope,\n ensureAuthenticated,\n setCommandSessionId,\n setLastSeenAuthMethod,\n setLastSeenUserIdAfterAuth,\n} from '../../private/node/session.js'\nimport {isThemeAccessSession} from '../../private/node/api/rest.js'\nimport {resolvePermanentStoreFqdn} from '../../private/node/session/permanent-store-domain.js'\n\n/**\n * Session Object to access the Admin API, includes the token and the store FQDN.\n */\nexport interface AdminSession {\n token: string\n storeFqdn: string\n}\n\n/**\n * Session Object for Partners API and App Management API access.\n */\nexport interface Session {\n token: string\n businessPlatformToken: string\n accountInfo: AccountInfo\n userId: string\n}\n\nexport type AccountInfo = UserAccountInfo | ServiceAccountInfo | UnknownAccountInfo\n\n/**\n * Records the user ID that should be attached to command analytics for this process.\n *\n * @param userId - User identifier to report on the command analytics event.\n */\nexport function setLastSeenUserId(userId: string): void {\n setLastSeenUserIdAfterAuth(userId)\n}\n\n/**\n * Finds a stored Shopify account session by alias without changing the current session.\n *\n * @param alias - The account alias to find.\n * @returns The matching session ID, or undefined if no session matches.\n */\nexport async function findSessionIdByAlias(alias: string): Promise<string | undefined> {\n return sessionStore.findSessionByAlias(alias)\n}\n\n/**\n * Selects a stored Shopify account session by alias for the current command process.\n *\n * @param alias - The account alias to select. Passing undefined clears the command selection.\n */\nexport async function setCurrentSessionAlias(alias?: string): Promise<void> {\n if (!alias) {\n setCommandSessionId(undefined)\n return\n }\n\n const sessionId = await findSessionIdByAlias(alias)\n if (!sessionId) {\n throw new AbortError(\n outputContent`No authenticated account found for alias ${outputToken.yellow(alias)}.`,\n outputContent`Run ${outputToken.genericShellCommand(`shopify auth login`)} first.`,\n )\n }\n setCommandSessionId(sessionId)\n}\n\ninterface UserAccountInfo {\n type: 'UserAccount'\n email: string\n}\n\ninterface ServiceAccountInfo {\n type: 'ServiceAccount'\n orgName: string\n}\n\ninterface UnknownAccountInfo {\n type: 'UnknownAccount'\n}\n\n/**\n * Type guard to check if an account is a UserAccount.\n *\n * @param account - The account to check.\n * @returns True if the account is a UserAccount.\n */\nexport function isUserAccount(account: AccountInfo): account is UserAccountInfo {\n return account.type === 'UserAccount'\n}\n\n/**\n * Type guard to check if an account is a ServiceAccount.\n *\n * @param account - The account to check.\n * @returns True if the account is a ServiceAccount.\n */\nexport function isServiceAccount(account: AccountInfo): account is ServiceAccountInfo {\n return account.type === 'ServiceAccount'\n}\n\n/**\n * Ensure that we have a valid session with no particular scopes.\n *\n * @param env - Optional environment variables to use.\n * @param options - Optional extra options to use.\n * @returns The user ID.\n */\nexport async function ensureAuthenticatedUser(\n env = process.env,\n options: EnsureAuthenticatedAdditionalOptions = {},\n): Promise<{userId: string}> {\n outputDebug(outputContent`Ensuring that the user is authenticated with no particular scopes`)\n const tokens = await ensureAuthenticated({}, env, options)\n return {userId: tokens.userId}\n}\n\n/**\n * Ensure that we have a valid session to access the Partners API.\n * If SHOPIFY_CLI_PARTNERS_TOKEN exists, that token will be used to obtain a valid Partners Token\n * If SHOPIFY_CLI_PARTNERS_TOKEN exists, scopes will be ignored.\n *\n * @param scopes - Optional array of extra scopes to authenticate with.\n * @param env - Optional environment variables to use.\n * @param options - Optional extra options to use.\n * @returns The access token for the Partners API.\n */\nexport async function ensureAuthenticatedPartners(\n scopes: PartnersAPIScope[] = [],\n env = process.env,\n options: EnsureAuthenticatedAdditionalOptions = {},\n): Promise<{token: string; userId: string}> {\n outputDebug(outputContent`Ensuring that the user is authenticated with the Partners API with the following scopes:\n${outputToken.json(scopes)}\n`)\n const envToken = getAppAutomationToken()\n if (envToken) {\n const result = await exchangeCustomPartnerToken(envToken)\n return {token: result.accessToken, userId: result.userId}\n }\n const tokens = await ensureAuthenticated({partnersApi: {scopes}}, env, options)\n if (!tokens.partners) {\n throw new BugError('No partners token found after ensuring authenticated')\n }\n return {token: tokens.partners, userId: tokens.userId}\n}\n\n/**\n * Ensure that we have a valid session to access the App Management API.\n *\n * @param options - Optional extra options to use.\n * @param appManagementScopes - Optional array of extra scopes to authenticate with.\n * @param businessPlatformScopes - Optional array of extra scopes to authenticate with.\n * @param env - Optional environment variables to use.\n * @returns The access token for the App Management API.\n */\nexport async function ensureAuthenticatedAppManagementAndBusinessPlatform(\n options: EnsureAuthenticatedAdditionalOptions = {},\n appManagementScopes: AppManagementAPIScope[] = [],\n businessPlatformScopes: BusinessPlatformScope[] = [],\n env = process.env,\n): Promise<{appManagementToken: string; userId: string; businessPlatformToken: string}> {\n outputDebug(outputContent`Ensuring that the user is authenticated with the App Management API with the following scopes:\n${outputToken.json(appManagementScopes)}\n`)\n\n const envToken = getAppAutomationToken()\n if (envToken) {\n const appManagmentToken = await exchangeAppAutomationTokenForAppManagementAccessToken(envToken)\n const businessPlatformToken = await exchangeAppAutomationTokenForBusinessPlatformAccessToken(envToken)\n\n return {\n appManagementToken: appManagmentToken.accessToken,\n userId: appManagmentToken.userId,\n businessPlatformToken: businessPlatformToken.accessToken,\n }\n }\n\n const tokens = await ensureAuthenticated(\n {appManagementApi: {scopes: appManagementScopes}, businessPlatformApi: {scopes: businessPlatformScopes}},\n env,\n options,\n )\n if (!tokens.appManagement || !tokens.businessPlatform) {\n throw new BugError('No App Management or Business Platform token found after ensuring authenticated')\n }\n\n return {\n appManagementToken: tokens.appManagement,\n userId: tokens.userId,\n businessPlatformToken: tokens.businessPlatform,\n }\n}\n\n/**\n * Ensure that we have a valid session to access the Storefront API.\n *\n * @param scopes - Optional array of extra scopes to authenticate with.\n * @param password - Optional password to use.\n * @param options - Optional extra options to use.\n * @returns The access token for the Storefront API.\n */\nexport async function ensureAuthenticatedStorefront(\n scopes: StorefrontRendererScope[] = [],\n password: string | undefined = undefined,\n options: EnsureAuthenticatedAdditionalOptions = {},\n): Promise<string> {\n if (password) {\n const session = {token: password, storeFqdn: ''}\n const authMethod = isThemeAccessSession(session) ? 'theme_access_token' : 'custom_app_token'\n setLastSeenAuthMethod(authMethod)\n setLastSeenUserIdAfterAuth(nonRandomUUID(password))\n return password\n }\n\n outputDebug(outputContent`Ensuring that the user is authenticated with the Storefront API with the following scopes:\n${outputToken.json(scopes)}\n`)\n const tokens = await ensureAuthenticated({storefrontRendererApi: {scopes}}, process.env, options)\n if (!tokens.storefront) {\n throw new BugError('No storefront token found after ensuring authenticated')\n }\n return tokens.storefront\n}\n\n/**\n * Ensure that we have a valid Admin session for the given store.\n *\n * @param store - Store fqdn to request auth for.\n * @param scopes - Optional array of extra scopes to authenticate with.\n * @param options - Optional extra options to use.\n * @returns The access token for the Admin API.\n */\nexport async function ensureAuthenticatedAdmin(\n store: string,\n scopes: AdminAPIScope[] = [],\n options: EnsureAuthenticatedAdditionalOptions = {},\n): Promise<AdminSession> {\n outputDebug(outputContent`Ensuring that the user is authenticated with the Admin API with the following scopes for the store ${outputToken.raw(\n store,\n )}:\n${outputToken.json(scopes)}\n`)\n const tokens = await ensureAuthenticated({adminApi: {scopes, storeFqdn: store}}, process.env, {\n ...options,\n })\n if (!tokens.admin) {\n throw new BugError('No admin token found after ensuring authenticated')\n }\n return tokens.admin\n}\n\n/**\n * Ensure that we have a valid session to access the Theme API.\n * If a password is provided, that token will be used against Theme Access API.\n * Otherwise, it will ensure that the user is authenticated with the Admin API.\n *\n * Theme Access passwords only work with the store's permanent `.myshopify.com` domain, so when the password is a\n * Theme Access password the returned session uses the permanent domain of `store`, even if `store` is another of the\n * store's domains.\n *\n * @param store - Store fqdn to request auth for.\n * @param password - Password generated from Theme Access app.\n * @param scopes - Optional array of extra scopes to authenticate with.\n * @param options - Optional extra options to use.\n * @returns The access token and store.\n */\nexport async function ensureAuthenticatedThemes(\n store: string,\n password: string | undefined,\n scopes: AdminAPIScope[] = [],\n options: EnsureAuthenticatedAdditionalOptions = {},\n): Promise<AdminSession> {\n outputDebug(outputContent`Ensuring that the user is authenticated with the Theme API with the following scopes:\n${outputToken.json(scopes)}\n`)\n if (password) {\n const session = {token: password, storeFqdn: store}\n const themeAccess = isThemeAccessSession(session)\n setLastSeenAuthMethod(themeAccess ? 'theme_access_token' : 'custom_app_token')\n setLastSeenUserIdAfterAuth(nonRandomUUID(password))\n if (themeAccess) session.storeFqdn = await resolvePermanentStoreFqdn(store)\n return session\n }\n return ensureAuthenticatedAdmin(store, scopes, options)\n}\n\n/**\n * Options for `ensureAuthenticatedBusinessPlatform`.\n */\nexport interface EnsureAuthenticatedBusinessPlatformOptions extends EnsureAuthenticatedAdditionalOptions {\n /**\n * Authenticate with the automation token set in the environment, when there is one, instead of the\n * logged-in user. Only commands that support automation tokens opt in; other callers, such as\n * Hydrogen's login, keep using the user's session.\n */\n allowAutomationToken?: boolean\n}\n\n/**\n * Ensure that we have a valid session to access the Business Platform API.\n *\n * @param scopes - Optional array of extra scopes to authenticate with. Ignored when an automation token is used.\n * @param options - Optional extra options to use.\n * @returns The access token for the Business Platform API.\n */\nexport async function ensureAuthenticatedBusinessPlatform(\n scopes: BusinessPlatformScope[] = [],\n options: EnsureAuthenticatedBusinessPlatformOptions = {},\n): Promise<string> {\n outputDebug(outputContent`Ensuring that the user is authenticated with the Business Platform API with the following scopes:\n${outputToken.json(scopes)}\n`)\n const {allowAutomationToken = false, ...authenticationOptions} = options\n const automationToken = allowAutomationToken ? getAppAutomationToken() : undefined\n if (automationToken) {\n // Request no explicit scopes, so Identity issues every Business Platform scope the token holds. The organizations\n // API admits a CLI token only with a scope such as organization.store-management, and naming that scope would\n // fail the exchange for app and Partner tokens, which don't hold it.\n const result = await exchangeAppAutomationTokenForBusinessPlatformAccessToken(automationToken, [])\n return result.accessToken\n }\n\n const tokens = await ensureAuthenticated({businessPlatformApi: {scopes}}, process.env, authenticationOptions)\n if (!tokens.businessPlatform) {\n throw new BugError('No business-platform token found after ensuring authenticated')\n }\n return tokens.businessPlatform\n}\n\n/**\n * Whether SHOPIFY_SERVICE_ACCOUNT_TOKEN is set, which makes it the automation token the CLI authenticates with.\n *\n * True even when it's empty or set together with another automation token variable. Commands use this to skip logins\n * saved on this machine, and authentication then reports any problem with the variables.\n *\n * @returns True when SHOPIFY_SERVICE_ACCOUNT_TOKEN is set.\n */\nexport function isServiceAccountTokenSet(): boolean {\n return automationTokenVariable() === environmentVariables.serviceAccountToken\n}\n\n/**\n * Fails when SHOPIFY_SERVICE_ACCOUNT_TOKEN is set, for commands that can't run with that token.\n *\n * Service account tokens can't log in as a user or call a store's Admin API, so commands that need either\n * refuse to run instead of quietly using the person's own login. `ensureAuthenticated` runs this check before any\n * login. Commands that run on a login saved by `shopify store auth` call it before loading that login.\n *\n * @throws AbortError when SHOPIFY_SERVICE_ACCOUNT_TOKEN is set.\n */\nexport function ensureNoServiceAccountToken(): void {\n if (!isServiceAccountTokenSet()) return\n const variable = environmentVariables.serviceAccountToken\n\n throw new AbortError(`This command can't use ${variable}.`, `Unset ${variable} to run it with your Shopify account.`)\n}\n\n/**\n * Logout from Shopify.\n *\n * @returns A promise that resolves when the logout is complete.\n */\nexport function logout(): Promise<void> {\n return sessionStore.remove()\n}\n\n/**\n * Ensure that we have a valid Admin session for the given store, with access on behalf of the app.\n *\n * See `ensureAuthenticatedAdmin` for access on behalf of a user.\n *\n * @param storeFqdn - Store handle or domain, normalized with `normalizeStoreFqdn`. Unsupported URL components are\n * rejected before the client secret is sent.\n * @param clientId - Client ID of the app.\n * @param clientSecret - Client secret of the app.\n * @returns The access token for the Admin API.\n */\nexport async function ensureAuthenticatedAdminAsApp(\n storeFqdn: string,\n clientId: string,\n clientSecret: string,\n): Promise<AdminSession> {\n const normalizedStoreFqdn = normalizeStoreFqdn(storeFqdn)\n const bodyData = {\n client_id: clientId,\n client_secret: clientSecret,\n grant_type: 'client_credentials',\n }\n const tokenResponse = await shopifyFetch(\n `https://${normalizedStoreFqdn}/admin/oauth/access_token`,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(bodyData),\n },\n 'slow-request',\n )\n\n const body = await tokenResponse.text()\n\n if (tokenResponse.status === 400) {\n if (body.includes('app_not_installed')) {\n throw new AbortError(\n outputContent`App is not installed on ${outputToken.green(\n normalizedStoreFqdn,\n )}. Try running ${outputToken.genericShellCommand(`shopify app dev`)} to connect your app to the shop.`,\n )\n }\n throw new AbortError(\n `Failed to get access token for app ${clientId} on store ${normalizedStoreFqdn}: ${tokenResponse.statusText}`,\n )\n }\n try {\n const tokenJson = JSON.parse(body) as {access_token: string}\n return {token: tokenJson.access_token, storeFqdn: normalizedStoreFqdn}\n } catch (error) {\n if (error instanceof SyntaxError) {\n throw new AbortError(\n `Received invalid response from admin authentication service (HTTP ${tokenResponse.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"]}
|
package/dist/public/node/ui.js
CHANGED
|
@@ -7,10 +7,10 @@ import { terminalSupportsPrompting } from './system.js';
|
|
|
7
7
|
import { AbortController } from './abort.js';
|
|
8
8
|
import { runWithTimer } from './metadata.js';
|
|
9
9
|
import { ConcurrentOutput, runConcurrentProcessesForJson, } from '../../private/node/ui/components/ConcurrentOutput.js';
|
|
10
|
-
import { handleCtrlC, render, renderOnce } from '../../private/node/ui.js';
|
|
10
|
+
import { handleCtrlC, render, renderOnce, Stdout } from '../../private/node/ui.js';
|
|
11
11
|
import { alert } from '../../private/node/ui/alert.js';
|
|
12
12
|
import { FatalError } from '../../private/node/ui/components/FatalError.js';
|
|
13
|
-
import { Table } from '../../private/node/ui/components/Table/Table.js';
|
|
13
|
+
import { Table, tableWidth } from '../../private/node/ui/components/Table/Table.js';
|
|
14
14
|
import { tokenItemToString, } from '../../private/node/ui/components/token-item.js';
|
|
15
15
|
import { DangerousConfirmationPrompt, } from '../../private/node/ui/components/DangerousConfirmationPrompt.js';
|
|
16
16
|
import { SelectPrompt } from '../../private/node/ui/components/SelectPrompt.js';
|
|
@@ -375,8 +375,18 @@ export async function renderAutocompletePrompt({ renderOptions, ...props }, uiDe
|
|
|
375
375
|
* 3 John Smith jon@smith.com
|
|
376
376
|
*/
|
|
377
377
|
export function renderTable({ renderOptions, ...props }) {
|
|
378
|
-
return renderOnce(React.createElement(Table, { ...props }), { renderOptions });
|
|
378
|
+
return renderOnce(React.createElement(Table, { ...props }), { renderOptions: tableRenderOptions(props, renderOptions) });
|
|
379
379
|
}
|
|
380
|
+
// Output that isn't a terminal (a pipe, a file, a CI log) has no width, and Ink then lays the table out in 80
|
|
381
|
+
// columns, squeezing headers to "Typ" and wrapping cells onto extra lines. Give the table the width it needs
|
|
382
|
+
// instead; a terminal keeps its own width.
|
|
383
|
+
function tableRenderOptions(props, renderOptions) {
|
|
384
|
+
if (renderOptions?.stdout || process.stdout.columns !== undefined)
|
|
385
|
+
return renderOptions;
|
|
386
|
+
const columns = Math.max(DEFAULT_OUTPUT_COLUMNS, tableWidth(props));
|
|
387
|
+
return { ...renderOptions, stdout: new Stdout({ columns }) };
|
|
388
|
+
}
|
|
389
|
+
const DEFAULT_OUTPUT_COLUMNS = 80;
|
|
380
390
|
/**
|
|
381
391
|
* Runs async tasks and displays their progress to the console.
|
|
382
392
|
* @example
|