@shopify/cli-kit 3.92.0 → 3.93.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cli/api/graphql/admin/generated/find_development_theme_by_name.d.ts +16 -0
- package/dist/cli/api/graphql/admin/generated/find_development_theme_by_name.js +60 -0
- package/dist/cli/api/graphql/admin/generated/find_development_theme_by_name.js.map +1 -0
- package/dist/private/node/api/graphql.d.ts +10 -0
- package/dist/private/node/api/graphql.js +67 -5
- package/dist/private/node/api/graphql.js.map +1 -1
- package/dist/private/node/conf-store.d.ts +13 -0
- package/dist/private/node/conf-store.js +17 -3
- package/dist/private/node/conf-store.js.map +1 -1
- package/dist/private/node/constants.d.ts +3 -0
- package/dist/private/node/constants.js +3 -0
- package/dist/private/node/constants.js.map +1 -1
- package/dist/private/node/session/exchange.d.ts +22 -8
- package/dist/private/node/session/exchange.js +37 -14
- package/dist/private/node/session/exchange.js.map +1 -1
- package/dist/private/node/session/schema.d.ts +62 -62
- package/dist/private/node/session/scopes.js +3 -2
- package/dist/private/node/session/scopes.js.map +1 -1
- package/dist/private/node/session/store.d.ts +7 -0
- package/dist/private/node/session/store.js +17 -0
- package/dist/private/node/session/store.js.map +1 -1
- package/dist/private/node/session/validate.d.ts +5 -4
- package/dist/private/node/session/validate.js +34 -7
- package/dist/private/node/session/validate.js.map +1 -1
- package/dist/private/node/session.d.ts +1 -1
- package/dist/private/node/session.js +77 -32
- package/dist/private/node/session.js.map +1 -1
- package/dist/private/node/testing/ui.js +6 -4
- package/dist/private/node/testing/ui.js.map +1 -1
- package/dist/private/node/ui/components/AutocompletePrompt.test.js +5 -5
- package/dist/private/node/ui/components/AutocompletePrompt.test.js.map +1 -1
- package/dist/private/node/ui/components/ConcurrentOutput.js +5 -2
- package/dist/private/node/ui/components/ConcurrentOutput.js.map +1 -1
- package/dist/private/node/ui/components/ConcurrentOutput.test.js +2 -2
- package/dist/private/node/ui/components/ConcurrentOutput.test.js.map +1 -1
- package/dist/private/node/ui/components/SelectInput.test.js +10 -10
- package/dist/private/node/ui/components/SelectInput.test.js.map +1 -1
- package/dist/private/node/ui/components/Table/Table.js +2 -2
- package/dist/private/node/ui/components/Table/Table.js.map +1 -1
- package/dist/private/node/ui/components/Tasks.test.js +6 -6
- package/dist/private/node/ui/components/Tasks.test.js.map +1 -1
- package/dist/private/node/ui/components/TextAnimation.test.js +1 -1
- package/dist/private/node/ui/components/TextAnimation.test.js.map +1 -1
- package/dist/private/node/ui/components/TextInput.test.js +4 -4
- package/dist/private/node/ui/components/TextInput.test.js.map +1 -1
- package/dist/private/node/ui/components/TokenizedText.js +1 -0
- package/dist/private/node/ui/components/TokenizedText.js.map +1 -1
- package/dist/private/node/ui/hooks/use-select-state.js +1 -3
- package/dist/private/node/ui/hooks/use-select-state.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/analytics.js +4 -4
- package/dist/public/node/analytics.js.map +1 -1
- package/dist/public/node/api/admin.js +1 -3
- package/dist/public/node/api/admin.js.map +1 -1
- package/dist/public/node/api/rest-api-throttler.d.ts +14 -0
- package/dist/public/node/api/rest-api-throttler.js +14 -87
- package/dist/public/node/api/rest-api-throttler.js.map +1 -1
- package/dist/public/node/archiver.js +6 -7
- package/dist/public/node/archiver.js.map +1 -1
- package/dist/public/node/cli-launcher.d.ts +1 -1
- package/dist/public/node/cli-launcher.js +9 -9
- package/dist/public/node/cli-launcher.js.map +1 -1
- package/dist/public/node/cli.js +4 -1
- package/dist/public/node/cli.js.map +1 -1
- package/dist/public/node/context/fqdn.js +1 -0
- package/dist/public/node/context/fqdn.js.map +1 -1
- package/dist/public/node/context/local.d.ts +7 -0
- package/dist/public/node/context/local.js +9 -0
- package/dist/public/node/context/local.js.map +1 -1
- package/dist/public/node/dot-env.js +1 -1
- package/dist/public/node/dot-env.js.map +1 -1
- package/dist/public/node/environment.d.ts +4 -9
- package/dist/public/node/environment.js +6 -12
- package/dist/public/node/environment.js.map +1 -1
- package/dist/public/node/environments.js +4 -3
- package/dist/public/node/environments.js.map +1 -1
- package/dist/public/node/error-handler.js +1 -1
- package/dist/public/node/error-handler.js.map +1 -1
- package/dist/public/node/error.js +1 -0
- package/dist/public/node/error.js.map +1 -1
- package/dist/public/node/fs.d.ts +9 -1
- package/dist/public/node/fs.js +16 -4
- package/dist/public/node/fs.js.map +1 -1
- package/dist/public/node/git.d.ts +10 -4
- package/dist/public/node/git.js +101 -80
- package/dist/public/node/git.js.map +1 -1
- package/dist/public/node/global-context.js +1 -3
- package/dist/public/node/global-context.js.map +1 -1
- package/dist/public/node/hooks/postrun.d.ts +7 -0
- package/dist/public/node/hooks/postrun.js +61 -1
- package/dist/public/node/hooks/postrun.js.map +1 -1
- package/dist/public/node/hooks/prerun.d.ts +3 -2
- package/dist/public/node/hooks/prerun.js +9 -22
- package/dist/public/node/hooks/prerun.js.map +1 -1
- package/dist/public/node/http.js +1 -1
- package/dist/public/node/http.js.map +1 -1
- package/dist/public/node/import-extractor.d.ts +17 -0
- package/dist/public/node/import-extractor.js +84 -23
- package/dist/public/node/import-extractor.js.map +1 -1
- package/dist/public/node/is-global.d.ts +9 -1
- package/dist/public/node/is-global.js +55 -12
- package/dist/public/node/is-global.js.map +1 -1
- package/dist/public/node/mimes.js +1 -1
- package/dist/public/node/mimes.js.map +1 -1
- package/dist/public/node/node-package-manager.d.ts +1 -1
- package/dist/public/node/node-package-manager.js +4 -1
- package/dist/public/node/node-package-manager.js.map +1 -1
- package/dist/public/node/os.js +1 -0
- package/dist/public/node/os.js.map +1 -1
- package/dist/public/node/output.js +3 -2
- package/dist/public/node/output.js.map +1 -1
- package/dist/public/node/path.d.ts +11 -0
- package/dist/public/node/path.js +30 -1
- package/dist/public/node/path.js.map +1 -1
- package/dist/public/node/result.js +1 -0
- package/dist/public/node/result.js.map +1 -1
- package/dist/public/node/serial-batch-processor.js +1 -3
- package/dist/public/node/serial-batch-processor.js.map +1 -1
- package/dist/public/node/session-prompt.js +10 -2
- package/dist/public/node/session-prompt.js.map +1 -1
- package/dist/public/node/session.js +6 -6
- package/dist/public/node/session.js.map +1 -1
- package/dist/public/node/themes/api.d.ts +1 -0
- package/dist/public/node/themes/api.js +26 -0
- package/dist/public/node/themes/api.js.map +1 -1
- package/dist/public/node/themes/conf.js +3 -5
- package/dist/public/node/themes/conf.js.map +1 -1
- package/dist/public/node/themes/factories.js +1 -0
- package/dist/public/node/themes/factories.js.map +1 -1
- package/dist/public/node/themes/theme-manager.d.ts +2 -2
- package/dist/public/node/themes/theme-manager.js +13 -8
- package/dist/public/node/themes/theme-manager.js.map +1 -1
- package/dist/public/node/{toml.d.ts → toml/codec.d.ts} +1 -1
- package/dist/public/node/{toml.js → toml/codec.js} +1 -1
- package/dist/public/node/toml/codec.js.map +1 -0
- package/dist/public/node/toml/index.d.ts +1 -0
- package/dist/public/node/toml/index.js +2 -0
- package/dist/public/node/toml/index.js.map +1 -0
- package/dist/public/node/toml/toml-file.d.ts +88 -0
- package/dist/public/node/toml/toml-file.js +159 -0
- package/dist/public/node/toml/toml-file.js.map +1 -0
- package/dist/public/node/tree-kill.js +1 -1
- package/dist/public/node/tree-kill.js.map +1 -1
- package/dist/public/node/ui.js +1 -0
- package/dist/public/node/ui.js.map +1 -1
- package/dist/public/node/upgrade.d.ts +28 -1
- package/dist/public/node/upgrade.js +184 -16
- package/dist/public/node/upgrade.js.map +1 -1
- package/dist/public/node/version.d.ts +9 -0
- package/dist/public/node/version.js +16 -1
- package/dist/public/node/version.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +12 -12
- package/dist/public/node/custom-oclif-loader.d.ts +0 -6
- package/dist/public/node/custom-oclif-loader.js +0 -79
- package/dist/public/node/custom-oclif-loader.js.map +0 -1
- package/dist/public/node/toml.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scopes.js","sourceRoot":"","sources":["../../../../src/private/node/session/scopes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,OAAO,EAAM,MAAM,WAAW,CAAA;AACtC,OAAO,EAAC,QAAQ,EAAC,MAAM,+BAA+B,CAAA;AAEtD;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,cAAwB,EAAE;IACzD,IAAI,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;IAC/D,MAAM,GAAG,CAAC,QAAQ,EAAE,GAAG,MAAM,EAAE,GAAG,WAAW,CAAC,CAAC,GAAG,CAAC,cAAc,CAAC,CAAA;IAClE,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,CAAA;AACpC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,SAAS,CAAC,GAAQ,EAAE,cAAwB,EAAE;IAC5D,MAAM,MAAM,GAAG,CAAC,GAAG,gBAAgB,CAAC,GAAG,CAAC,EAAE,GAAG,WAAW,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,GAAG,CAAC,cAAc,CAAC,CAAA;IACjG,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,CAAA;AACpC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAAQ;IAC1C,QAAQ,GAAG,EAAE,CAAC;QACZ,KAAK,UAAU;YACb,OAAO,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAA;QAChC,KAAK,gBAAgB;YACnB,OAAO,CAAC,cAAc,CAAC,gBAAgB,CAAC,CAAC,CAAA;QAC3C,KAAK,mBAAmB;YACtB,OAAO,CAAC,cAAc,CAAC,cAAc,CAAC,CAAC,CAAA;QACzC;
|
|
1
|
+
{"version":3,"file":"scopes.js","sourceRoot":"","sources":["../../../../src/private/node/session/scopes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,OAAO,EAAM,MAAM,WAAW,CAAA;AACtC,OAAO,EAAC,QAAQ,EAAC,MAAM,+BAA+B,CAAA;AAEtD;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,cAAwB,EAAE;IACzD,IAAI,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;IAC/D,MAAM,GAAG,CAAC,QAAQ,EAAE,GAAG,MAAM,EAAE,GAAG,WAAW,CAAC,CAAC,GAAG,CAAC,cAAc,CAAC,CAAA;IAClE,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,CAAA;AACpC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,SAAS,CAAC,GAAQ,EAAE,cAAwB,EAAE;IAC5D,MAAM,MAAM,GAAG,CAAC,GAAG,gBAAgB,CAAC,GAAG,CAAC,EAAE,GAAG,WAAW,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,GAAG,CAAC,cAAc,CAAC,CAAA;IACjG,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,CAAA;AACpC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAAQ;IAC1C,QAAQ,GAAG,EAAE,CAAC;QACZ,KAAK,UAAU;YACb,OAAO,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAA;QAChC,KAAK,gBAAgB;YACnB,OAAO,CAAC,cAAc,CAAC,gBAAgB,CAAC,CAAC,CAAA;QAC3C,KAAK,mBAAmB;YACtB,OAAO,CAAC,cAAc,CAAC,cAAc,CAAC,CAAC,CAAA;QACzC,KAAK,OAAO,CAAC;QACb,KAAK,qBAAqB;YACxB,MAAM,IAAI,QAAQ,CAAC,yCAAyC,GAAG,EAAE,CAAC,CAAA;IACtE,CAAC;AACH,CAAC;AAED,SAAS,gBAAgB,CAAC,GAAQ;IAChC,QAAQ,GAAG,EAAE,CAAC;QACZ,KAAK,OAAO;YACV,OAAO,CAAC,SAAS,EAAE,QAAQ,EAAE,cAAc,CAAC,CAAA;QAC9C,KAAK,qBAAqB;YACxB,OAAO,CAAC,UAAU,EAAE,SAAS,CAAC,CAAA;QAChC,KAAK,UAAU;YACb,OAAO,CAAC,KAAK,CAAC,CAAA;QAChB,KAAK,mBAAmB;YACtB,OAAO,CAAC,cAAc,EAAE,kBAAkB,EAAE,uBAAuB,CAAC,CAAA;QACtE,KAAK,gBAAgB;YACnB,OAAO,CAAC,gBAAgB,CAAC,CAAA;QAC3B;YACE,MAAM,IAAI,QAAQ,CAAC,gBAAgB,GAAG,EAAE,CAAC,CAAA;IAC7C,CAAC;AACH,CAAC;AAED,SAAS,cAAc,CAAC,KAAa;IACnC,QAAQ,KAAK,EAAE,CAAC;QACd,KAAK,SAAS;YACZ,OAAO,iDAAiD,CAAA;QAC1D,KAAK,QAAQ;YACX,OAAO,gDAAgD,CAAA;QACzD,KAAK,cAAc;YACjB,OAAO,2EAA2E,CAAA;QACpF,KAAK,KAAK;YACR,OAAO,sDAAsD,CAAA;QAC/D,KAAK,UAAU;YACb,OAAO,gEAAgE,CAAA;QACzE,KAAK,cAAc;YACjB,OAAO,oDAAoD,CAAA;QAC7D,KAAK,kBAAkB;YACrB,OAAO,4DAA4D,CAAA;QACrE,KAAK,uBAAuB;YAC1B,OAAO,iEAAiE,CAAA;QAC1E,KAAK,gBAAgB;YACnB,OAAO,uDAAuD,CAAA;QAChE;YACE,OAAO,KAAK,CAAA;IAChB,CAAC;AACH,CAAC","sourcesContent":["import {allAPIs, API} from '../api.js'\nimport {BugError} from '../../../public/node/error.js'\n\n/**\n * Generate a flat array with all the default scopes for all the APIs plus\n * any custom scope defined by the user.\n * @param extraScopes - custom user-defined scopes\n * @returns Array of scopes\n */\nexport function allDefaultScopes(extraScopes: string[] = []): string[] {\n let scopes = allAPIs.map((api) => defaultApiScopes(api)).flat()\n scopes = ['openid', ...scopes, ...extraScopes].map(scopeTransform)\n return Array.from(new Set(scopes))\n}\n\n/**\n * Generate a flat array with the default scopes for the given API plus\n * any custom scope defined by the user\n * @param api - API to get the scopes for\n * @param extraScopes - custom user-defined scopes\n * @returns Array of scopes\n */\nexport function apiScopes(api: API, extraScopes: string[] = []): string[] {\n const scopes = [...defaultApiScopes(api), ...extraScopes.map(scopeTransform)].map(scopeTransform)\n return Array.from(new Set(scopes))\n}\n\n/**\n * Returns specific scopes required for token exchange with the given API.\n * @param api - API to get the scopes for\n * @returns Array of transformed scopes\n */\nexport function tokenExchangeScopes(api: API): string[] {\n switch (api) {\n case 'partners':\n return [scopeTransform('cli')]\n case 'app-management':\n return [scopeTransform('app-management')]\n case 'business-platform':\n return [scopeTransform('destinations')]\n case 'admin':\n case 'storefront-renderer':\n throw new BugError(`API not supported for token exchange: ${api}`)\n }\n}\n\nfunction defaultApiScopes(api: API): string[] {\n switch (api) {\n case 'admin':\n return ['graphql', 'themes', 'collaborator']\n case 'storefront-renderer':\n return ['devtools', 'graphql']\n case 'partners':\n return ['cli']\n case 'business-platform':\n return ['destinations', 'store-management', 'on-demand-user-access']\n case 'app-management':\n return ['app-management']\n default:\n throw new BugError(`Unknown API: ${api}`)\n }\n}\n\nfunction scopeTransform(scope: string): string {\n switch (scope) {\n case 'graphql':\n return 'https://api.shopify.com/auth/shop.admin.graphql'\n case 'themes':\n return 'https://api.shopify.com/auth/shop.admin.themes'\n case 'collaborator':\n return 'https://api.shopify.com/auth/partners.collaborator-relationships.readonly'\n case 'cli':\n return 'https://api.shopify.com/auth/partners.app.cli.access'\n case 'devtools':\n return 'https://api.shopify.com/auth/shop.storefront-renderer.devtools'\n case 'destinations':\n return 'https://api.shopify.com/auth/destinations.readonly'\n case 'store-management':\n return 'https://api.shopify.com/auth/organization.store-management'\n case 'on-demand-user-access':\n return 'https://api.shopify.com/auth/organization.on-demand-user-access'\n case 'app-management':\n return 'https://api.shopify.com/auth/organization.apps.manage'\n default:\n return scope\n }\n}\n"]}
|
|
@@ -21,6 +21,13 @@ export declare function remove(): Promise<void>;
|
|
|
21
21
|
* @returns The alias for the session if it exists, otherwise undefined.
|
|
22
22
|
*/
|
|
23
23
|
export declare function getSessionAlias(userId: string): Promise<string | undefined>;
|
|
24
|
+
/**
|
|
25
|
+
* Sets the alias for a given user's session and persists it.
|
|
26
|
+
*
|
|
27
|
+
* @param userId - The user ID of the session to update.
|
|
28
|
+
* @param alias - The new alias to set.
|
|
29
|
+
*/
|
|
30
|
+
export declare function setSessionAlias(userId: string, alias: string): Promise<void>;
|
|
24
31
|
/**
|
|
25
32
|
* Finds a session by its alias.
|
|
26
33
|
*
|
|
@@ -51,6 +51,23 @@ export async function getSessionAlias(userId) {
|
|
|
51
51
|
return undefined;
|
|
52
52
|
return sessions[fqdn][userId].identity.alias;
|
|
53
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* Sets the alias for a given user's session and persists it.
|
|
56
|
+
*
|
|
57
|
+
* @param userId - The user ID of the session to update.
|
|
58
|
+
* @param alias - The new alias to set.
|
|
59
|
+
*/
|
|
60
|
+
export async function setSessionAlias(userId, alias) {
|
|
61
|
+
const sessions = await fetch();
|
|
62
|
+
if (!sessions)
|
|
63
|
+
return;
|
|
64
|
+
const fqdn = await identityFqdn();
|
|
65
|
+
const session = sessions[fqdn]?.[userId];
|
|
66
|
+
if (!session)
|
|
67
|
+
return;
|
|
68
|
+
session.identity.alias = alias;
|
|
69
|
+
await store(sessions);
|
|
70
|
+
}
|
|
54
71
|
/**
|
|
55
72
|
* Finds a session by its alias.
|
|
56
73
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../../../src/private/node/session/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,cAAc,EAAC,MAAM,aAAa,CAAA;AAC1C,OAAO,EAAC,WAAW,EAAE,sBAAsB,EAAE,cAAc,EAAE,WAAW,EAAC,MAAM,kBAAkB,CAAA;AACjG,OAAO,EAAC,YAAY,EAAC,MAAM,sCAAsC,CAAA;AAGjE;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,QAAkB;IAC5C,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAA;IAC7C,WAAW,CAAC,YAAY,CAAC,CAAA;AAC3B,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,KAAK;IACzB,MAAM,OAAO,GAAG,WAAW,EAAE,CAAA;IAE7B,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IACvC,MAAM,cAAc,GAAG,MAAM,cAAc,CAAC,cAAc,CAAC,WAAW,CAAC,CAAA;IACvE,IAAI,cAAc,CAAC,OAAO,EAAE,CAAC;QAC3B,OAAO,cAAc,CAAC,IAAI,CAAA;IAC5B,CAAC;SAAM,CAAC;QACN,MAAM,MAAM,EAAE,CAAA;QACd,OAAO,SAAS,CAAA;IAClB,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,MAAM;IAC1B,cAAc,EAAE,CAAA;IAChB,sBAAsB,EAAE,CAAA;AAC1B,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,MAAc;IAClD,MAAM,QAAQ,GAAG,MAAM,KAAK,EAAE,CAAA;IAC9B,IAAI,CAAC,QAAQ;QAAE,OAAO,SAAS,CAAA;IAE/B,MAAM,IAAI,GAAG,MAAM,YAAY,EAAE,CAAA;IACjC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC;QAAE,OAAO,SAAS,CAAA;IAChE,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAA;AAC9C,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,KAAa;IACpD,MAAM,QAAQ,GAAG,MAAM,KAAK,EAAE,CAAA;IAC9B,IAAI,CAAC,QAAQ;QAAE,OAAO,SAAS,CAAA;IAE/B,MAAM,IAAI,GAAG,MAAM,YAAY,EAAE,CAAA;IACjC,MAAM,YAAY,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAA;IACnC,IAAI,CAAC,YAAY;QAAE,OAAO,SAAS,CAAA;IAEnC,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC;QAC7D,IAAI,OAAO,CAAC,QAAQ,CAAC,KAAK,KAAK,KAAK,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;YACzD,OAAO,MAAM,CAAA;QACf,CAAC;IACH,CAAC;IAED,OAAO,SAAS,CAAA;AAClB,CAAC","sourcesContent":["import {SessionsSchema} from './schema.js'\nimport {getSessions, removeCurrentSessionId, removeSessions, setSessions} from '../conf-store.js'\nimport {identityFqdn} from '../../../public/node/context/fqdn.js'\nimport type {Sessions} from './schema.js'\n\n/**\n * Serializes the session as a JSON and stores it in the system.\n * @param session - the session to store.\n */\nexport async function store(sessions: Sessions) {\n const jsonSessions = JSON.stringify(sessions)\n setSessions(jsonSessions)\n}\n\n/**\n * Fetches the sessions from the local storage and returns it.\n * If the format of the object is invalid, the method will discard it.\n * @returns Returns a promise that resolves with the sessions object if it exists and is valid.\n */\nexport async function fetch(): Promise<Sessions | undefined> {\n const content = getSessions()\n\n if (!content) {\n return undefined\n }\n const contentJson = JSON.parse(content)\n const parsedSessions = await SessionsSchema.safeParseAsync(contentJson)\n if (parsedSessions.success) {\n return parsedSessions.data\n } else {\n await remove()\n return undefined\n }\n}\n\n/**\n * Removes a session from the system.\n */\nexport async function remove() {\n removeSessions()\n removeCurrentSessionId()\n}\n\n/**\n * Gets the session alias for a given user ID.\n *\n * @param userId - The user ID of the session to get the alias for.\n * @returns The alias for the session if it exists, otherwise undefined.\n */\nexport async function getSessionAlias(userId: string): Promise<string | undefined> {\n const sessions = await fetch()\n if (!sessions) return undefined\n\n const fqdn = await identityFqdn()\n if (!sessions[fqdn] || !sessions[fqdn][userId]) return undefined\n return sessions[fqdn][userId].identity.alias\n}\n\n/**\n * Finds a session by its alias.\n *\n * @param alias - The alias to search for\n * @returns The user ID if found, otherwise undefined\n */\nexport async function findSessionByAlias(alias: string): Promise<string | undefined> {\n const sessions = await fetch()\n if (!sessions) return undefined\n\n const fqdn = await identityFqdn()\n const fqdnSessions = sessions[fqdn]\n if (!fqdnSessions) return undefined\n\n for (const [userId, session] of Object.entries(fqdnSessions)) {\n if (session.identity.alias === alias || userId === alias) {\n return userId\n }\n }\n\n return undefined\n}\n"]}
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../../../src/private/node/session/store.ts"],"names":[],"mappings":"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","sourcesContent":["import {SessionsSchema} from './schema.js'\nimport {getSessions, removeCurrentSessionId, removeSessions, setSessions} from '../conf-store.js'\nimport {identityFqdn} from '../../../public/node/context/fqdn.js'\nimport type {Session, Sessions} from './schema.js'\n\n/**\n * Serializes the session as a JSON and stores it in the system.\n * @param session - the session to store.\n */\nexport async function store(sessions: Sessions) {\n const jsonSessions = JSON.stringify(sessions)\n setSessions(jsonSessions)\n}\n\n/**\n * Fetches the sessions from the local storage and returns it.\n * If the format of the object is invalid, the method will discard it.\n * @returns Returns a promise that resolves with the sessions object if it exists and is valid.\n */\nexport async function fetch(): Promise<Sessions | undefined> {\n const content = getSessions()\n\n if (!content) {\n return undefined\n }\n const contentJson = JSON.parse(content)\n const parsedSessions = await SessionsSchema.safeParseAsync(contentJson)\n if (parsedSessions.success) {\n return parsedSessions.data\n } else {\n await remove()\n return undefined\n }\n}\n\n/**\n * Removes a session from the system.\n */\nexport async function remove() {\n removeSessions()\n removeCurrentSessionId()\n}\n\n/**\n * Gets the session alias for a given user ID.\n *\n * @param userId - The user ID of the session to get the alias for.\n * @returns The alias for the session if it exists, otherwise undefined.\n */\nexport async function getSessionAlias(userId: string): Promise<string | undefined> {\n const sessions = await fetch()\n if (!sessions) return undefined\n\n const fqdn = await identityFqdn()\n if (!sessions[fqdn] || !sessions[fqdn][userId]) return undefined\n return sessions[fqdn][userId].identity.alias\n}\n\n/**\n * Sets the alias for a given user's session and persists it.\n *\n * @param userId - The user ID of the session to update.\n * @param alias - The new alias to set.\n */\nexport async function setSessionAlias(userId: string, alias: string): Promise<void> {\n const sessions = await fetch()\n if (!sessions) return\n\n const fqdn = await identityFqdn()\n const session: Session | undefined = sessions[fqdn]?.[userId]\n if (!session) return\n\n session.identity.alias = alias\n await store(sessions)\n}\n\n/**\n * Finds a session by its alias.\n *\n * @param alias - The alias to search for\n * @returns The user ID if found, otherwise undefined\n */\nexport async function findSessionByAlias(alias: string): Promise<string | undefined> {\n const sessions = await fetch()\n if (!sessions) return undefined\n\n const fqdn = await identityFqdn()\n const fqdnSessions = sessions[fqdn]\n if (!fqdnSessions) return undefined\n\n for (const [userId, session] of Object.entries(fqdnSessions)) {\n if (session.identity.alias === alias || userId === alias) {\n return userId\n }\n }\n\n return undefined\n}\n"]}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { Session } from './schema.js';
|
|
2
|
+
import { OAuthApplications } from '../session.js';
|
|
2
3
|
type ValidationResult = 'needs_refresh' | 'needs_full_auth' | 'ok';
|
|
3
4
|
/**
|
|
4
|
-
* Validate if the current session is valid or we need to refresh/re-authenticate
|
|
5
|
-
* With PCAT, only the identity token needs validation - no per-application tokens.
|
|
5
|
+
* Validate if the current session is valid or we need to refresh/re-authenticate
|
|
6
6
|
* @param scopes - requested scopes to validate
|
|
7
|
-
* @param
|
|
7
|
+
* @param applications - requested applications
|
|
8
|
+
* @param session - current session with identity and application tokens
|
|
8
9
|
* @returns 'ok' if the session is valid, 'needs_full_auth' if we need to re-authenticate, 'needs_refresh' if we need to refresh the session
|
|
9
10
|
*/
|
|
10
|
-
export declare function validateSession(scopes: string[], session: Session | undefined): Promise<ValidationResult>;
|
|
11
|
+
export declare function validateSession(scopes: string[], applications: OAuthApplications, session: Session | undefined): Promise<ValidationResult>;
|
|
11
12
|
export {};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { validateCachedIdentityTokenStructure } from './schema.js';
|
|
2
|
+
import { applicationId } from './identity.js';
|
|
2
3
|
import { sessionConstants } from '../constants.js';
|
|
3
4
|
import { firstPartyDev } from '../../../public/node/context/local.js';
|
|
4
5
|
import { outputDebug } from '../../../public/node/output.js';
|
|
@@ -12,27 +13,53 @@ function validateScopes(requestedScopes, identity) {
|
|
|
12
13
|
return requestedScopes.every((scope) => currentScopes.includes(scope));
|
|
13
14
|
}
|
|
14
15
|
/**
|
|
15
|
-
* Validate if the current session is valid or we need to refresh/re-authenticate
|
|
16
|
-
* With PCAT, only the identity token needs validation - no per-application tokens.
|
|
16
|
+
* Validate if the current session is valid or we need to refresh/re-authenticate
|
|
17
17
|
* @param scopes - requested scopes to validate
|
|
18
|
-
* @param
|
|
18
|
+
* @param applications - requested applications
|
|
19
|
+
* @param session - current session with identity and application tokens
|
|
19
20
|
* @returns 'ok' if the session is valid, 'needs_full_auth' if we need to re-authenticate, 'needs_refresh' if we need to refresh the session
|
|
20
21
|
*/
|
|
21
|
-
export async function validateSession(scopes, session) {
|
|
22
|
+
export async function validateSession(scopes, applications, session) {
|
|
22
23
|
if (!session)
|
|
23
24
|
return 'needs_full_auth';
|
|
24
25
|
const scopesAreValid = validateScopes(scopes, session.identity);
|
|
25
26
|
if (!scopesAreValid)
|
|
26
27
|
return 'needs_full_auth';
|
|
28
|
+
let tokensAreExpired = isTokenExpired(session.identity);
|
|
29
|
+
if (applications.partnersApi) {
|
|
30
|
+
const appId = applicationId('partners');
|
|
31
|
+
const token = session.applications[appId];
|
|
32
|
+
tokensAreExpired = tokensAreExpired || isTokenExpired(token);
|
|
33
|
+
}
|
|
34
|
+
if (applications.appManagementApi) {
|
|
35
|
+
const appId = applicationId('app-management');
|
|
36
|
+
const token = session.applications[appId];
|
|
37
|
+
tokensAreExpired = tokensAreExpired || isTokenExpired(token);
|
|
38
|
+
}
|
|
39
|
+
if (applications.storefrontRendererApi) {
|
|
40
|
+
const appId = applicationId('storefront-renderer');
|
|
41
|
+
const token = session.applications[appId];
|
|
42
|
+
tokensAreExpired = tokensAreExpired || isTokenExpired(token);
|
|
43
|
+
}
|
|
44
|
+
if (applications.adminApi) {
|
|
45
|
+
const appId = applicationId('admin');
|
|
46
|
+
const realAppId = `${applications.adminApi.storeFqdn}-${appId}`;
|
|
47
|
+
const token = session.applications[realAppId];
|
|
48
|
+
tokensAreExpired = tokensAreExpired || isTokenExpired(token);
|
|
49
|
+
}
|
|
50
|
+
outputDebug(`- Token validation -> It's expired: ${tokensAreExpired}`);
|
|
27
51
|
if (!validateCachedIdentityTokenStructure(session.identity)) {
|
|
28
52
|
return 'needs_full_auth';
|
|
29
53
|
}
|
|
30
|
-
|
|
31
|
-
outputDebug(`- Token validation -> It's expired: ${expired}`);
|
|
32
|
-
if (expired)
|
|
54
|
+
if (tokensAreExpired)
|
|
33
55
|
return 'needs_refresh';
|
|
34
56
|
return 'ok';
|
|
35
57
|
}
|
|
58
|
+
function isTokenExpired(token) {
|
|
59
|
+
if (!token)
|
|
60
|
+
return true;
|
|
61
|
+
return token.expiresAt < expireThreshold();
|
|
62
|
+
}
|
|
36
63
|
function expireThreshold() {
|
|
37
64
|
return new Date(Date.now() + sessionConstants.expirationTimeMarginInMinutes * 60 * 1000);
|
|
38
65
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../../../../src/private/node/session/validate.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../../../../src/private/node/session/validate.ts"],"names":[],"mappings":"AAAA,OAAO,EAA2C,oCAAoC,EAAC,MAAM,aAAa,CAAA;AAE1G,OAAO,EAAC,aAAa,EAAC,MAAM,eAAe,CAAA;AAC3C,OAAO,EAAC,gBAAgB,EAAC,MAAM,iBAAiB,CAAA;AAChD,OAAO,EAAC,aAAa,EAAC,MAAM,uCAAuC,CAAA;AAEnE,OAAO,EAAC,WAAW,EAAC,MAAM,gCAAgC,CAAA;AAI1D;;GAEG;AACH,SAAS,cAAc,CAAC,eAAyB,EAAE,QAAuB;IACxE,MAAM,aAAa,GAAG,QAAQ,CAAC,MAAM,CAAA;IACrC,IAAI,aAAa,EAAE,KAAK,aAAa,CAAC,QAAQ,CAAC,UAAU,CAAC;QAAE,OAAO,KAAK,CAAA;IACxE,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,aAAa,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAA;AACxE,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,MAAgB,EAChB,YAA+B,EAC/B,OAA4B;IAE5B,IAAI,CAAC,OAAO;QAAE,OAAO,iBAAiB,CAAA;IACtC,MAAM,cAAc,GAAG,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAA;IAC/D,IAAI,CAAC,cAAc;QAAE,OAAO,iBAAiB,CAAA;IAC7C,IAAI,gBAAgB,GAAG,cAAc,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;IAEvD,IAAI,YAAY,CAAC,WAAW,EAAE,CAAC;QAC7B,MAAM,KAAK,GAAG,aAAa,CAAC,UAAU,CAAC,CAAA;QACvC,MAAM,KAAK,GAAG,OAAO,CAAC,YAAY,CAAC,KAAK,CAAE,CAAA;QAC1C,gBAAgB,GAAG,gBAAgB,IAAI,cAAc,CAAC,KAAK,CAAC,CAAA;IAC9D,CAAC;IAED,IAAI,YAAY,CAAC,gBAAgB,EAAE,CAAC;QAClC,MAAM,KAAK,GAAG,aAAa,CAAC,gBAAgB,CAAC,CAAA;QAC7C,MAAM,KAAK,GAAG,OAAO,CAAC,YAAY,CAAC,KAAK,CAAE,CAAA;QAC1C,gBAAgB,GAAG,gBAAgB,IAAI,cAAc,CAAC,KAAK,CAAC,CAAA;IAC9D,CAAC;IAED,IAAI,YAAY,CAAC,qBAAqB,EAAE,CAAC;QACvC,MAAM,KAAK,GAAG,aAAa,CAAC,qBAAqB,CAAC,CAAA;QAClD,MAAM,KAAK,GAAG,OAAO,CAAC,YAAY,CAAC,KAAK,CAAE,CAAA;QAC1C,gBAAgB,GAAG,gBAAgB,IAAI,cAAc,CAAC,KAAK,CAAC,CAAA;IAC9D,CAAC;IAED,IAAI,YAAY,CAAC,QAAQ,EAAE,CAAC;QAC1B,MAAM,KAAK,GAAG,aAAa,CAAC,OAAO,CAAC,CAAA;QACpC,MAAM,SAAS,GAAG,GAAG,YAAY,CAAC,QAAQ,CAAC,SAAS,IAAI,KAAK,EAAE,CAAA;QAC/D,MAAM,KAAK,GAAG,OAAO,CAAC,YAAY,CAAC,SAAS,CAAE,CAAA;QAC9C,gBAAgB,GAAG,gBAAgB,IAAI,cAAc,CAAC,KAAK,CAAC,CAAA;IAC9D,CAAC;IAED,WAAW,CAAC,uCAAuC,gBAAgB,EAAE,CAAC,CAAA;IAEtE,IAAI,CAAC,oCAAoC,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5D,OAAO,iBAAiB,CAAA;IAC1B,CAAC;IAED,IAAI,gBAAgB;QAAE,OAAO,eAAe,CAAA;IAE5C,OAAO,IAAI,CAAA;AACb,CAAC;AAED,SAAS,cAAc,CAAC,KAAuB;IAC7C,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAA;IACvB,OAAO,KAAK,CAAC,SAAS,GAAG,eAAe,EAAE,CAAA;AAC5C,CAAC;AAED,SAAS,eAAe;IACtB,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,gBAAgB,CAAC,6BAA6B,GAAG,EAAE,GAAG,IAAI,CAAC,CAAA;AAC1F,CAAC","sourcesContent":["import {ApplicationToken, IdentityToken, Session, validateCachedIdentityTokenStructure} from './schema.js'\n\nimport {applicationId} from './identity.js'\nimport {sessionConstants} from '../constants.js'\nimport {firstPartyDev} from '../../../public/node/context/local.js'\nimport {OAuthApplications} from '../session.js'\nimport {outputDebug} from '../../../public/node/output.js'\n\ntype ValidationResult = 'needs_refresh' | 'needs_full_auth' | 'ok'\n\n/**\n * Validate if an identity token is valid for the requested scopes\n */\nfunction validateScopes(requestedScopes: string[], identity: IdentityToken) {\n const currentScopes = identity.scopes\n if (firstPartyDev() !== currentScopes.includes('employee')) return false\n return requestedScopes.every((scope) => currentScopes.includes(scope))\n}\n\n/**\n * Validate if the current session is valid or we need to refresh/re-authenticate\n * @param scopes - requested scopes to validate\n * @param applications - requested applications\n * @param session - current session with identity and application tokens\n * @returns 'ok' if the session is valid, 'needs_full_auth' if we need to re-authenticate, 'needs_refresh' if we need to refresh the session\n */\nexport async function validateSession(\n scopes: string[],\n applications: OAuthApplications,\n session: Session | undefined,\n): Promise<ValidationResult> {\n if (!session) return 'needs_full_auth'\n const scopesAreValid = validateScopes(scopes, session.identity)\n if (!scopesAreValid) return 'needs_full_auth'\n let tokensAreExpired = isTokenExpired(session.identity)\n\n if (applications.partnersApi) {\n const appId = applicationId('partners')\n const token = session.applications[appId]!\n tokensAreExpired = tokensAreExpired || isTokenExpired(token)\n }\n\n if (applications.appManagementApi) {\n const appId = applicationId('app-management')\n const token = session.applications[appId]!\n tokensAreExpired = tokensAreExpired || isTokenExpired(token)\n }\n\n if (applications.storefrontRendererApi) {\n const appId = applicationId('storefront-renderer')\n const token = session.applications[appId]!\n tokensAreExpired = tokensAreExpired || isTokenExpired(token)\n }\n\n if (applications.adminApi) {\n const appId = applicationId('admin')\n const realAppId = `${applications.adminApi.storeFqdn}-${appId}`\n const token = session.applications[realAppId]!\n tokensAreExpired = tokensAreExpired || isTokenExpired(token)\n }\n\n outputDebug(`- Token validation -> It's expired: ${tokensAreExpired}`)\n\n if (!validateCachedIdentityTokenStructure(session.identity)) {\n return 'needs_full_auth'\n }\n\n if (tokensAreExpired) return 'needs_refresh'\n\n return 'ok'\n}\n\nfunction isTokenExpired(token: ApplicationToken): boolean {\n if (!token) return true\n return token.expiresAt < expireThreshold()\n}\n\nfunction expireThreshold(): Date {\n return new Date(Date.now() + sessionConstants.expirationTimeMarginInMinutes * 60 * 1000)\n}\n"]}
|
|
@@ -31,7 +31,7 @@ interface AppManagementAPIOauthOptions {
|
|
|
31
31
|
/**
|
|
32
32
|
* A scope supported by the Storefront Renderer API.
|
|
33
33
|
*/
|
|
34
|
-
export type StorefrontRendererScope = 'devtools';
|
|
34
|
+
export type StorefrontRendererScope = 'devtools' | 'graphql';
|
|
35
35
|
interface StorefrontRendererAPIOAuthOptions {
|
|
36
36
|
/** List of scopes to request permissions for. */
|
|
37
37
|
scopes: StorefrontRendererScope[];
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { applicationId } from './session/identity.js';
|
|
1
2
|
import { validateSession } from './session/validate.js';
|
|
2
|
-
import { allDefaultScopes } from './session/scopes.js';
|
|
3
|
-
import { exchangeCustomPartnerToken, refreshAccessToken, InvalidGrantError, InvalidRequestError, } from './session/exchange.js';
|
|
3
|
+
import { allDefaultScopes, apiScopes } from './session/scopes.js';
|
|
4
|
+
import { exchangeAccessForApplicationTokens, exchangeCustomPartnerToken, refreshAccessToken, InvalidGrantError, InvalidRequestError, } from './session/exchange.js';
|
|
4
5
|
import * as sessionStore from './session/store.js';
|
|
5
6
|
import { pollForDeviceAuthorization, requestDeviceAuthorization } from './session/device-authorization.js';
|
|
6
7
|
import { isThemeAccessSession } from './api/rest.js';
|
|
@@ -10,7 +11,7 @@ import { outputContent, outputToken, outputDebug, outputCompleted } from '../../
|
|
|
10
11
|
import { firstPartyDev, themeToken } from '../../public/node/context/local.js';
|
|
11
12
|
import { AbortError } from '../../public/node/error.js';
|
|
12
13
|
import { normalizeStoreFqdn, identityFqdn } from '../../public/node/context/fqdn.js';
|
|
13
|
-
import { getIdentityTokenInformation,
|
|
14
|
+
import { getIdentityTokenInformation, getAppAutomationToken } from '../../public/node/environment.js';
|
|
14
15
|
import { logout } from '../../public/node/session.js';
|
|
15
16
|
import { nonRandomUUID } from '../../public/node/crypto.js';
|
|
16
17
|
import { isEmpty } from '../../public/common/object.js';
|
|
@@ -25,7 +26,7 @@ async function fetchEmail(businessPlatformToken) {
|
|
|
25
26
|
return undefined;
|
|
26
27
|
try {
|
|
27
28
|
const userEmailResult = await businessPlatformRequest(UserEmailQueryString, businessPlatformToken);
|
|
28
|
-
return userEmailResult.currentUserAccount?.email;
|
|
29
|
+
return userEmailResult.currentUserAccount?.email ?? undefined;
|
|
29
30
|
// eslint-disable-next-line no-catch-all/no-catch-all
|
|
30
31
|
}
|
|
31
32
|
catch (error) {
|
|
@@ -53,7 +54,7 @@ export async function getLastSeenUserIdAfterAuth() {
|
|
|
53
54
|
const currentSessionId = getCurrentSessionId();
|
|
54
55
|
if (currentSessionId)
|
|
55
56
|
return currentSessionId;
|
|
56
|
-
const customToken =
|
|
57
|
+
const customToken = getAppAutomationToken() ?? themeToken();
|
|
57
58
|
return customToken ? nonRandomUUID(customToken) : 'unknown';
|
|
58
59
|
}
|
|
59
60
|
export function setLastSeenUserIdAfterAuth(id) {
|
|
@@ -76,8 +77,8 @@ export async function getLastSeenAuthMethod() {
|
|
|
76
77
|
return authMethod;
|
|
77
78
|
if (getCurrentSessionId())
|
|
78
79
|
return 'device_auth';
|
|
79
|
-
const
|
|
80
|
-
if (
|
|
80
|
+
const appAutomationToken = getAppAutomationToken();
|
|
81
|
+
if (appAutomationToken)
|
|
81
82
|
return 'partners_token';
|
|
82
83
|
const themePassword = themeToken();
|
|
83
84
|
if (themePassword) {
|
|
@@ -119,12 +120,12 @@ ${outputToken.json(scopes)}
|
|
|
119
120
|
For applications:
|
|
120
121
|
${outputToken.json(applications)}
|
|
121
122
|
`);
|
|
122
|
-
const validationResult = await validateSession(scopes, currentSession);
|
|
123
|
+
const validationResult = await validateSession(scopes, applications, currentSession);
|
|
123
124
|
let newSession = {};
|
|
124
125
|
if (validationResult === 'needs_full_auth') {
|
|
125
126
|
await throwOnNoPrompt(noPrompt);
|
|
126
127
|
outputDebug(outputContent `Initiating the full authentication flow...`);
|
|
127
|
-
newSession = await executeCompleteFlow(applications);
|
|
128
|
+
newSession = await executeCompleteFlow(applications, currentSession?.identity.alias);
|
|
128
129
|
}
|
|
129
130
|
else if (validationResult === 'needs_refresh' || forceRefresh) {
|
|
130
131
|
outputDebug(outputContent `The current session is valid but needs refresh. Refreshing...`);
|
|
@@ -134,7 +135,7 @@ ${outputToken.json(applications)}
|
|
|
134
135
|
catch (error) {
|
|
135
136
|
if (error instanceof InvalidGrantError) {
|
|
136
137
|
await throwOnNoPrompt(noPrompt);
|
|
137
|
-
newSession = await executeCompleteFlow(applications);
|
|
138
|
+
newSession = await executeCompleteFlow(applications, currentSession?.identity.alias);
|
|
138
139
|
}
|
|
139
140
|
else if (error instanceof InvalidRequestError) {
|
|
140
141
|
await sessionStore.remove();
|
|
@@ -157,8 +158,7 @@ ${outputToken.json(applications)}
|
|
|
157
158
|
setCurrentSessionId(newSessionId);
|
|
158
159
|
}
|
|
159
160
|
const tokens = await tokensFor(applications, completeSession);
|
|
160
|
-
|
|
161
|
-
const envToken = getPartnersToken();
|
|
161
|
+
const envToken = getAppAutomationToken();
|
|
162
162
|
if (envToken && applications.partnersApi) {
|
|
163
163
|
tokens.partners = (await exchangeCustomPartnerToken(envToken)).accessToken;
|
|
164
164
|
}
|
|
@@ -178,10 +178,12 @@ The CLI is currently unable to prompt for reauthentication.`, 'Restart the CLI p
|
|
|
178
178
|
* Execute the full authentication flow.
|
|
179
179
|
*
|
|
180
180
|
* @param applications - An object containing the applications we need to be authenticated with.
|
|
181
|
-
* @param
|
|
181
|
+
* @param existingAlias - Optional alias from a previous session to preserve if the email fetch fails.
|
|
182
182
|
*/
|
|
183
|
-
async function executeCompleteFlow(applications) {
|
|
183
|
+
async function executeCompleteFlow(applications, existingAlias) {
|
|
184
184
|
const scopes = getFlattenScopes(applications);
|
|
185
|
+
const exchangeScopes = getExchangeScopes(applications);
|
|
186
|
+
const store = applications.adminApi?.storeFqdn;
|
|
185
187
|
if (firstPartyDev()) {
|
|
186
188
|
outputDebug(outputContent `Authenticating as Shopify Employee...`);
|
|
187
189
|
scopes.push('employee');
|
|
@@ -199,16 +201,18 @@ async function executeCompleteFlow(applications) {
|
|
|
199
201
|
outputDebug(outputContent `Starting polling for the identity token...`);
|
|
200
202
|
identityToken = await pollForDeviceAuthorization(deviceAuth.deviceCode, deviceAuth.interval);
|
|
201
203
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
204
|
+
// Exchange identity token for application tokens
|
|
205
|
+
outputDebug(outputContent `CLI token received. Exchanging it for application tokens...`);
|
|
206
|
+
const result = await exchangeAccessForApplicationTokens(identityToken, exchangeScopes, store);
|
|
207
|
+
// Preserve existing alias if available, otherwise try fetching email
|
|
208
|
+
const businessPlatformToken = result[applicationId('business-platform')]?.accessToken;
|
|
209
|
+
const alias = existingAlias ?? (await fetchEmail(businessPlatformToken)) ?? identityToken.userId;
|
|
206
210
|
const session = {
|
|
207
211
|
identity: {
|
|
208
212
|
...identityToken,
|
|
209
213
|
alias,
|
|
210
214
|
},
|
|
211
|
-
applications:
|
|
215
|
+
applications: result,
|
|
212
216
|
};
|
|
213
217
|
outputCompleted(`Logged in.`);
|
|
214
218
|
return session;
|
|
@@ -218,32 +222,53 @@ async function executeCompleteFlow(applications) {
|
|
|
218
222
|
*
|
|
219
223
|
* @param session - The session to refresh.
|
|
220
224
|
*/
|
|
221
|
-
async function refreshTokens(session,
|
|
222
|
-
// Refresh Identity Token
|
|
225
|
+
async function refreshTokens(session, applications) {
|
|
226
|
+
// Refresh Identity Token
|
|
223
227
|
const identityToken = await refreshAccessToken(session.identity);
|
|
228
|
+
// Exchange new identity token for application tokens
|
|
229
|
+
const exchangeScopes = getExchangeScopes(applications);
|
|
230
|
+
const applicationTokens = await exchangeAccessForApplicationTokens(identityToken, exchangeScopes, applications.adminApi?.storeFqdn);
|
|
224
231
|
return {
|
|
225
|
-
identity: identityToken,
|
|
226
|
-
applications:
|
|
232
|
+
identity: { ...identityToken, alias: session.identity.alias },
|
|
233
|
+
applications: applicationTokens,
|
|
227
234
|
};
|
|
228
235
|
}
|
|
229
236
|
/**
|
|
230
237
|
* Get the application tokens for a given session.
|
|
231
|
-
* With PCAT, the identity token can be used directly for all APIs.
|
|
232
238
|
*
|
|
233
239
|
* @param applications - An object containing the applications we need the tokens for.
|
|
234
240
|
* @param session - The current session.
|
|
235
241
|
* @param fqdn - The identity FQDN.
|
|
236
242
|
*/
|
|
237
243
|
async function tokensFor(applications, session) {
|
|
238
|
-
const
|
|
239
|
-
return {
|
|
244
|
+
const tokens = {
|
|
240
245
|
userId: session.identity.userId,
|
|
241
|
-
admin: applications.adminApi ? { token, storeFqdn: applications.adminApi.storeFqdn } : undefined,
|
|
242
|
-
partners: applications.partnersApi ? token : undefined,
|
|
243
|
-
storefront: applications.storefrontRendererApi ? token : undefined,
|
|
244
|
-
businessPlatform: applications.businessPlatformApi ? token : undefined,
|
|
245
|
-
appManagement: applications.appManagementApi ? token : undefined,
|
|
246
246
|
};
|
|
247
|
+
if (applications.adminApi) {
|
|
248
|
+
const appId = applicationId('admin');
|
|
249
|
+
const realAppId = `${applications.adminApi.storeFqdn}-${appId}`;
|
|
250
|
+
const token = session.applications[realAppId]?.accessToken;
|
|
251
|
+
if (token) {
|
|
252
|
+
tokens.admin = { token, storeFqdn: applications.adminApi.storeFqdn };
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
if (applications.partnersApi) {
|
|
256
|
+
const appId = applicationId('partners');
|
|
257
|
+
tokens.partners = session.applications[appId]?.accessToken;
|
|
258
|
+
}
|
|
259
|
+
if (applications.storefrontRendererApi) {
|
|
260
|
+
const appId = applicationId('storefront-renderer');
|
|
261
|
+
tokens.storefront = session.applications[appId]?.accessToken;
|
|
262
|
+
}
|
|
263
|
+
if (applications.businessPlatformApi) {
|
|
264
|
+
const appId = applicationId('business-platform');
|
|
265
|
+
tokens.businessPlatform = session.applications[appId]?.accessToken;
|
|
266
|
+
}
|
|
267
|
+
if (applications.appManagementApi) {
|
|
268
|
+
const appId = applicationId('app-management');
|
|
269
|
+
tokens.appManagement = session.applications[appId]?.accessToken;
|
|
270
|
+
}
|
|
271
|
+
return tokens;
|
|
247
272
|
}
|
|
248
273
|
// Scope Helpers
|
|
249
274
|
/**
|
|
@@ -261,12 +286,32 @@ function getFlattenScopes(apps) {
|
|
|
261
286
|
const requestedScopes = [...admin, ...partner, ...storefront, ...businessPlatform, ...appManagement];
|
|
262
287
|
return allDefaultScopes(requestedScopes);
|
|
263
288
|
}
|
|
289
|
+
/**
|
|
290
|
+
* Get the scopes for the given applications.
|
|
291
|
+
*
|
|
292
|
+
* @param apps - An object containing the applications we need the scopes for.
|
|
293
|
+
* @returns An object containing the scopes for each application.
|
|
294
|
+
*/
|
|
295
|
+
function getExchangeScopes(apps) {
|
|
296
|
+
const adminScope = apps.adminApi?.scopes ?? [];
|
|
297
|
+
const partnerScope = apps.partnersApi?.scopes ?? [];
|
|
298
|
+
const storefrontScopes = apps.storefrontRendererApi?.scopes ?? [];
|
|
299
|
+
const businessPlatformScopes = apps.businessPlatformApi?.scopes ?? [];
|
|
300
|
+
const appManagementScopes = apps.appManagementApi?.scopes ?? [];
|
|
301
|
+
return {
|
|
302
|
+
admin: apiScopes('admin', adminScope),
|
|
303
|
+
partners: apiScopes('partners', partnerScope),
|
|
304
|
+
storefront: apiScopes('storefront-renderer', storefrontScopes),
|
|
305
|
+
businessPlatform: apiScopes('business-platform', businessPlatformScopes),
|
|
306
|
+
appManagement: apiScopes('app-management', appManagementScopes),
|
|
307
|
+
};
|
|
308
|
+
}
|
|
264
309
|
function buildIdentityTokenFromEnv(scopes, identityTokenInformation) {
|
|
265
310
|
return {
|
|
266
311
|
...identityTokenInformation,
|
|
267
312
|
expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
|
|
268
313
|
scopes,
|
|
269
|
-
alias:
|
|
314
|
+
alias: undefined,
|
|
270
315
|
};
|
|
271
316
|
}
|
|
272
317
|
//# sourceMappingURL=session.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../../src/private/node/session.ts"],"names":[],"mappings":"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","sourcesContent":["import {validateSession} from './session/validate.js'\nimport {allDefaultScopes} from './session/scopes.js'\nimport {\n exchangeCustomPartnerToken,\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 {firstPartyDev, 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, getPartnersToken} 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\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'\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'\n\n/**\n * Retrieves the user ID from the current session or returns 'unknown' if not found.\n *\n * This function performs the following steps:\n * 1. Checks for a cached user ID in memory (obtained in the current run).\n * 2. Attempts to fetch it from the local storage (from a previous auth session).\n * 3. Checks if a custom token was used (either as a theme password or partners token).\n * 4. If a custom token is present in the environment, generates a UUID and uses it as userId.\n * 5. If after all this we don't have a userId, then reports as 'unknown'.\n *\n * @returns A Promise that resolves to the user ID as a string.\n */\nexport async function getLastSeenUserIdAfterAuth(): Promise<string> {\n if (userId) return userId\n\n const currentSessionId = getCurrentSessionId()\n if (currentSessionId) return currentSessionId\n\n const customToken = getPartnersToken() ?? themeToken()\n return customToken ? nonRandomUUID(customToken) : '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 partnersToken = getPartnersToken()\n if (partnersToken) 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 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 = getCurrentSessionId()\n if (!currentSessionId) {\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, 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)\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)\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 setCurrentSessionId(newSessionId)\n }\n\n const tokens = await tokensFor(applications, completeSession)\n\n // Overwrite partners token if using a custom CLI Token\n const envToken = getPartnersToken()\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 alias - Optional alias to use for the session.\n */\nasync function executeCompleteFlow(applications: OAuthApplications): Promise<Session> {\n const scopes = getFlattenScopes(applications)\n if (firstPartyDev()) {\n outputDebug(outputContent`Authenticating as Shopify Employee...`)\n scopes.push('employee')\n }\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 outputDebug(outputContent`CLI token received (PCAT). Using it directly for API access...`)\n\n // Get the alias for the session (email or userId)\n // Use the PCAT identity token directly to fetch the email\n const alias = (await fetchEmail(identityToken.accessToken)) ?? identityToken.userId\n\n const session: Session = {\n identity: {\n ...identityToken,\n alias,\n },\n applications: {},\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 (PCAT) - no exchange needed\n const identityToken = await refreshAccessToken(session.identity)\n\n return {\n identity: identityToken,\n applications: {},\n }\n}\n\n/**\n * Get the application tokens for a given session.\n * With PCAT, the identity token can be used directly for all APIs.\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 token = session.identity.accessToken\n return {\n userId: session.identity.userId,\n admin: applications.adminApi ? {token, storeFqdn: applications.adminApi.storeFqdn} : undefined,\n partners: applications.partnersApi ? token : undefined,\n storefront: applications.storefrontRendererApi ? token : undefined,\n businessPlatform: applications.businessPlatformApi ? token : undefined,\n appManagement: applications.appManagementApi ? token : undefined,\n }\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\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: identityTokenInformation.userId,\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 {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 {firstPartyDev, 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'\n\n/**\n * Retrieves the user ID from the current session or returns 'unknown' if not found.\n *\n * This function performs the following steps:\n * 1. Checks for a cached user ID in memory (obtained in the current run).\n * 2. Attempts to fetch it from the local storage (from a previous auth session).\n * 3. Checks if a custom token was used (either as a theme password or partners token).\n * 4. If a custom token is present in the environment, generates a UUID and uses it as userId.\n * 5. If after all this we don't have a userId, then reports as 'unknown'.\n *\n * @returns A Promise that resolves to the user ID as a string.\n */\nexport async function getLastSeenUserIdAfterAuth(): Promise<string> {\n if (userId) return userId\n\n const currentSessionId = getCurrentSessionId()\n if (currentSessionId) return currentSessionId\n\n const customToken = getAppAutomationToken() ?? themeToken()\n return customToken ? nonRandomUUID(customToken) : '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 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 = getCurrentSessionId()\n if (!currentSessionId) {\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 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 if (firstPartyDev()) {\n outputDebug(outputContent`Authenticating as Shopify Employee...`)\n scopes.push('employee')\n }\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"]}
|
|
@@ -48,7 +48,7 @@ export const render = (tree, options = {}) => {
|
|
|
48
48
|
rerender: instance.rerender,
|
|
49
49
|
unmount: instance.unmount,
|
|
50
50
|
cleanup: instance.cleanup,
|
|
51
|
-
waitUntilExit: () => trackPromise(instance.waitUntilExit()),
|
|
51
|
+
waitUntilExit: () => trackPromise(instance.waitUntilExit().then(() => { })),
|
|
52
52
|
stdout,
|
|
53
53
|
stderr,
|
|
54
54
|
stdin,
|
|
@@ -60,7 +60,7 @@ export const render = (tree, options = {}) => {
|
|
|
60
60
|
* Wait for the component to be ready to accept input.
|
|
61
61
|
*/
|
|
62
62
|
export function waitForInputsToBeReady() {
|
|
63
|
-
return new Promise((resolve) => setTimeout(resolve,
|
|
63
|
+
return new Promise((resolve) => setTimeout(resolve, 10));
|
|
64
64
|
}
|
|
65
65
|
/**
|
|
66
66
|
* Wait for the last frame to change to anything.
|
|
@@ -97,8 +97,10 @@ export function waitForContent(renderInstance, content, func = () => { }) {
|
|
|
97
97
|
*/
|
|
98
98
|
export async function sendInputAndWaitForChange(renderInstance, ...inputs) {
|
|
99
99
|
await waitForChange(() => inputs.forEach((input) => renderInstance.stdin.write(input)), renderInstance.lastFrame);
|
|
100
|
-
//
|
|
101
|
-
|
|
100
|
+
// Yield via setImmediate → setTimeout(0) so React 19's scheduler can flush
|
|
101
|
+
// effects (e.g. re-register useInput handlers with up-to-date closures)
|
|
102
|
+
// before subsequent input is sent.
|
|
103
|
+
await new Promise((resolve) => setImmediate(() => setTimeout(resolve, 0)));
|
|
102
104
|
}
|
|
103
105
|
/** Send input and wait a number of milliseconds.
|
|
104
106
|
*
|