@shopify/cli-kit 3.0.25 → 3.1.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/CHANGELOG.md +34 -0
- package/assets/auth-error.html +2 -4
- package/assets/empty-url.html +2 -4
- package/assets/missing-code.html +2 -4
- package/assets/missing-state.html +2 -4
- package/assets/style.css +5 -8
- package/assets/success.html +1 -1
- package/dist/abort.d.ts +1 -0
- package/dist/abort.js +2 -0
- package/dist/abort.js.map +1 -0
- package/dist/analytics.d.ts +13 -0
- package/dist/analytics.js +111 -0
- package/dist/analytics.js.map +1 -0
- package/dist/api/admin.d.ts +3 -0
- package/dist/api/admin.js +80 -0
- package/dist/api/admin.js.map +1 -0
- package/dist/api/common.d.ts +11 -0
- package/dist/api/common.js +40 -0
- package/dist/api/common.js.map +1 -0
- package/dist/api/graphql/all_app_extension_registrations.d.ts +14 -0
- package/dist/api/graphql/all_app_extension_registrations.js +14 -0
- package/dist/api/graphql/all_app_extension_registrations.js.map +1 -0
- package/dist/api/graphql/all_orgs.d.ts +12 -0
- package/dist/api/graphql/all_orgs.js +14 -0
- package/dist/api/graphql/all_orgs.js.map +1 -0
- package/dist/api/graphql/all_stores_by_org.d.ts +18 -0
- package/dist/api/graphql/all_stores_by_org.js +21 -0
- package/dist/api/graphql/all_stores_by_org.js.map +1 -0
- package/dist/api/graphql/convert_dev_to_test_store.d.ts +16 -0
- package/dist/api/graphql/convert_dev_to_test_store.js +13 -0
- package/dist/api/graphql/convert_dev_to_test_store.js.map +1 -0
- package/dist/api/graphql/create_app.d.ts +28 -0
- package/dist/api/graphql/create_app.js +32 -0
- package/dist/api/graphql/create_app.js.map +1 -0
- package/dist/api/graphql/create_deployment.d.ts +33 -0
- package/dist/api/graphql/create_deployment.js +25 -0
- package/dist/api/graphql/create_deployment.js.map +1 -0
- package/dist/api/graphql/extension_create.d.ts +30 -0
- package/dist/api/graphql/extension_create.js +26 -0
- package/dist/api/graphql/extension_create.js.map +1 -0
- package/dist/api/graphql/extension_specifications.d.ts +18 -0
- package/dist/api/graphql/extension_specifications.js +18 -0
- package/dist/api/graphql/extension_specifications.js.map +1 -0
- package/dist/api/graphql/find_app.d.ts +13 -0
- package/dist/api/graphql/find_app.js +16 -0
- package/dist/api/graphql/find_app.js.map +1 -0
- package/dist/api/graphql/find_org.d.ts +23 -0
- package/dist/api/graphql/find_org.js +26 -0
- package/dist/api/graphql/find_org.js.map +1 -0
- package/dist/api/graphql/find_org_basic.d.ts +11 -0
- package/dist/api/graphql/find_org_basic.js +14 -0
- package/dist/api/graphql/find_org_basic.js.map +1 -0
- package/dist/api/graphql/find_store_by_domain.d.ts +21 -0
- package/dist/api/graphql/find_store_by_domain.js +24 -0
- package/dist/api/graphql/find_store_by_domain.js.map +1 -0
- package/dist/api/graphql/functions/app_function_set.d.ts +43 -0
- package/dist/api/graphql/functions/app_function_set.js +52 -0
- package/dist/api/graphql/functions/app_function_set.js.map +1 -0
- package/dist/api/graphql/functions/compile_module.d.ts +15 -0
- package/dist/api/graphql/functions/compile_module.js +13 -0
- package/dist/api/graphql/functions/compile_module.js.map +1 -0
- package/dist/api/graphql/functions/function_service_proxy.d.ts +4 -0
- package/dist/api/graphql/functions/function_service_proxy.js +7 -0
- package/dist/api/graphql/functions/function_service_proxy.js.map +1 -0
- package/dist/api/graphql/functions/get_app_functions.d.ts +1 -0
- package/dist/api/graphql/functions/get_app_functions.js +10 -0
- package/dist/api/graphql/functions/get_app_functions.js.map +1 -0
- package/dist/api/graphql/functions/module_compilation_status.d.ts +15 -0
- package/dist/api/graphql/functions/module_compilation_status.js +13 -0
- package/dist/api/graphql/functions/module_compilation_status.js.map +1 -0
- package/dist/api/graphql/functions/module_upload_url_generate.d.ts +18 -0
- package/dist/api/graphql/functions/module_upload_url_generate.js +17 -0
- package/dist/api/graphql/functions/module_upload_url_generate.js.map +1 -0
- package/dist/api/graphql/generate_signed_upload_url.d.ts +15 -0
- package/dist/api/graphql/generate_signed_upload_url.js +15 -0
- package/dist/api/graphql/generate_signed_upload_url.js.map +1 -0
- package/dist/api/graphql/get_variant_id.d.ts +17 -0
- package/dist/api/graphql/get_variant_id.js +20 -0
- package/dist/api/graphql/get_variant_id.js.map +1 -0
- package/dist/api/graphql/index.d.ts +22 -0
- package/dist/api/graphql/index.js +23 -0
- package/dist/api/graphql/index.js.map +1 -0
- package/dist/api/graphql/update_draft.d.ts +33 -0
- package/dist/api/graphql/update_draft.js +24 -0
- package/dist/api/graphql/update_draft.js.map +1 -0
- package/dist/api/graphql/update_urls.d.ts +14 -0
- package/dist/api/graphql/update_urls.js +12 -0
- package/dist/api/graphql/update_urls.js.map +1 -0
- package/dist/api/identity.d.ts +1 -0
- package/dist/api/identity.js +32 -0
- package/dist/api/identity.js.map +1 -0
- package/dist/api/partners.d.ts +25 -0
- package/dist/api/partners.js +100 -0
- package/dist/api/partners.js.map +1 -0
- package/dist/api.d.ts +5 -0
- package/dist/api.js +6 -0
- package/dist/api.js.map +1 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.js +13 -0
- package/dist/cli.js.map +1 -0
- package/dist/constants.d.ts +43 -0
- package/dist/constants.js +63 -0
- package/dist/constants.js.map +1 -0
- package/dist/environment/fqdn.d.ts +23 -0
- package/dist/environment/fqdn.js +61 -0
- package/dist/environment/fqdn.js.map +1 -0
- package/dist/environment/local.d.ts +48 -0
- package/dist/environment/local.js +82 -0
- package/dist/environment/local.js.map +1 -0
- package/dist/environment/service.d.ts +17 -0
- package/dist/environment/service.js +41 -0
- package/dist/environment/service.js.map +1 -0
- package/dist/environment/spin.d.ts +47 -0
- package/dist/environment/spin.js +83 -0
- package/dist/environment/spin.js.map +1 -0
- package/dist/environment/utilities.d.ts +6 -0
- package/dist/environment/utilities.js +12 -0
- package/dist/environment/utilities.js.map +1 -0
- package/dist/environment.d.ts +5 -0
- package/dist/environment.js +6 -0
- package/dist/environment.js.map +1 -0
- package/dist/error.d.ts +44 -0
- package/dist/error.js +91 -0
- package/dist/error.js.map +1 -0
- package/dist/file.d.ts +67 -0
- package/dist/file.js +165 -0
- package/dist/file.js.map +1 -0
- package/dist/git.d.ts +15 -0
- package/dist/git.js +48 -0
- package/dist/git.js.map +1 -0
- package/dist/github.d.ts +33 -0
- package/dist/github.js +56 -0
- package/dist/github.js.map +1 -0
- package/dist/haiku.d.ts +1 -0
- package/dist/haiku.js +8 -0
- package/dist/haiku.js.map +1 -0
- package/dist/http/fetch.d.ts +16 -0
- package/dist/http/fetch.js +18 -0
- package/dist/http/fetch.js.map +1 -0
- package/dist/http/formdata.d.ts +3 -0
- package/dist/http/formdata.js +6 -0
- package/dist/http/formdata.js.map +1 -0
- package/dist/http.d.ts +2 -0
- package/dist/http.js +3 -0
- package/dist/http.js.map +1 -0
- package/dist/id.d.ts +6 -0
- package/dist/id.js +18 -0
- package/dist/id.js.map +1 -0
- package/dist/index.d.ts +33 -2179
- package/dist/index.js +34 -3264
- package/dist/index.js.map +1 -1
- package/dist/network/api.d.ts +2 -0
- package/dist/network/api.js +2 -0
- package/dist/network/api.js.map +1 -0
- package/dist/network/service.d.ts +16 -0
- package/dist/network/service.js +12 -0
- package/dist/network/service.js.map +1 -0
- package/dist/{archiver.d.ts → node/archiver.d.ts} +1 -4
- package/dist/node/archiver.js +22 -42
- package/dist/node/archiver.js.map +1 -1
- package/dist/node/checksum.d.ts +20 -0
- package/dist/node/checksum.js +32 -0
- package/dist/node/checksum.js.map +1 -0
- package/dist/node/cli.d.ts +18 -0
- package/dist/node/cli.js +96 -0
- package/dist/node/cli.js.map +1 -0
- package/dist/node/colors.d.ts +1 -0
- package/dist/node/colors.js +8 -0
- package/dist/node/colors.js.map +1 -0
- package/dist/node/dot-env.d.ts +33 -0
- package/dist/node/dot-env.js +36 -0
- package/dist/node/dot-env.js.map +1 -0
- package/dist/node/node-package-manager.d.ts +197 -0
- package/dist/node/node-package-manager.js +309 -0
- package/dist/node/node-package-manager.js.map +1 -0
- package/dist/node/ruby.d.ts +30 -0
- package/dist/node/ruby.js +197 -0
- package/dist/node/ruby.js.map +1 -0
- package/dist/npm.d.ts +27 -0
- package/dist/npm.js +20 -0
- package/dist/npm.js.map +1 -0
- package/dist/os.d.ts +10 -0
- package/dist/os.js +70 -0
- package/dist/os.js.map +1 -0
- package/dist/output.d.ts +149 -0
- package/dist/output.js +515 -0
- package/dist/output.js.map +1 -0
- package/dist/path.d.ts +22 -0
- package/dist/path.js +43 -0
- package/dist/path.js.map +1 -0
- package/dist/plugins.d.ts +9 -0
- package/dist/plugins.js +12 -0
- package/dist/plugins.js.map +1 -0
- package/dist/port.d.ts +5 -0
- package/dist/port.js +35 -0
- package/dist/port.js.map +1 -0
- package/dist/schema.d.ts +1 -0
- package/dist/schema.js +2 -0
- package/dist/schema.js.map +1 -0
- package/dist/secure-store.d.ts +19 -0
- package/dist/secure-store.js +63 -0
- package/dist/secure-store.js.map +1 -0
- package/dist/semver.d.ts +3 -0
- package/dist/semver.js +6 -0
- package/dist/semver.js.map +1 -0
- package/dist/session/authorize.d.ts +7 -0
- package/dist/session/authorize.js +40 -0
- package/dist/session/authorize.js.map +1 -0
- package/dist/session/exchange.d.ts +42 -0
- package/dist/session/exchange.js +144 -0
- package/dist/session/exchange.js.map +1 -0
- package/dist/session/identity.d.ts +3 -0
- package/dist/session/identity.js +58 -0
- package/dist/session/identity.js.map +1 -0
- package/dist/session/post-auth.d.ts +13 -0
- package/dist/session/post-auth.js +56 -0
- package/dist/session/post-auth.js.map +1 -0
- package/dist/session/redirect-listener.d.ts +34 -0
- package/dist/session/redirect-listener.js +97 -0
- package/dist/session/redirect-listener.js.map +1 -0
- package/dist/session/schema.d.ts +174 -0
- package/dist/session/schema.js +59 -0
- package/dist/session/schema.js.map +1 -0
- package/dist/session/scopes.d.ts +16 -0
- package/dist/session/scopes.js +53 -0
- package/dist/session/scopes.js.map +1 -0
- package/dist/session/store.d.ts +24 -0
- package/dist/session/store.js +88 -0
- package/dist/session/store.js.map +1 -0
- package/dist/session/token.d.ts +40 -0
- package/dist/session/token.js +22 -0
- package/dist/session/token.js.map +1 -0
- package/dist/session/validate.d.ts +17 -0
- package/dist/session/validate.js +75 -0
- package/dist/session/validate.js.map +1 -0
- package/dist/session.d.ts +88 -0
- package/dist/session.js +251 -0
- package/dist/session.js.map +1 -0
- package/dist/store/schema.d.ts +3 -0
- package/dist/store/schema.js +27 -0
- package/dist/store/schema.js.map +1 -0
- package/dist/store.d.ts +32 -0
- package/dist/store.js +102 -0
- package/dist/store.js.map +1 -0
- package/dist/string.d.ts +22 -0
- package/dist/string.js +38 -0
- package/dist/string.js.map +1 -0
- package/dist/system.d.ts +53 -0
- package/dist/system.js +109 -0
- package/dist/system.js.map +1 -0
- package/dist/template.d.ts +11 -0
- package/dist/template.js +50 -0
- package/dist/template.js.map +1 -0
- package/dist/testing/output.d.ts +9 -0
- package/dist/testing/output.js +15 -0
- package/dist/testing/output.js.map +1 -0
- package/dist/testing/store.d.ts +7 -0
- package/dist/testing/store.js +26 -0
- package/dist/testing/store.js.map +1 -0
- package/dist/toml.d.ts +3 -0
- package/dist/toml.js +8 -0
- package/dist/toml.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/ui/autocomplete.d.ts +7 -0
- package/dist/ui/autocomplete.js +43 -0
- package/dist/ui/autocomplete.js.map +1 -0
- package/dist/ui/input.d.ts +7 -0
- package/dist/ui/input.js +48 -0
- package/dist/ui/input.js.map +1 -0
- package/dist/ui/select.d.ts +6 -0
- package/dist/ui/select.js +30 -0
- package/dist/ui/select.js.map +1 -0
- package/dist/ui.d.ts +36 -0
- package/dist/ui.js +124 -0
- package/dist/ui.js.map +1 -0
- package/dist/version.d.ts +19 -0
- package/dist/version.js +34 -0
- package/dist/version.js.map +1 -0
- package/dist/vscode.d.ts +8 -0
- package/dist/vscode.js +36 -0
- package/dist/vscode.js.map +1 -0
- package/dist/yaml.d.ts +2 -0
- package/dist/yaml.js +8 -0
- package/dist/yaml.js.map +1 -0
- package/package.json +19 -8
- package/dist/archiver.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/local-d0094ffe.js +0 -1344
- package/dist/local-d0094ffe.js.map +0 -1
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ApplicationToken, IdentityToken } from './schema.js';
|
|
2
|
+
import { OAuthApplications } from '../session.js';
|
|
3
|
+
declare type ValidationResult = 'needs_refresh' | 'needs_full_auth' | 'ok';
|
|
4
|
+
/**
|
|
5
|
+
* Validate if the current session is valid or we need to refresh/re-authenticate
|
|
6
|
+
* @param scopes {string[]} requested scopes to validate
|
|
7
|
+
* @param applications {OAuthApplications} requested applications
|
|
8
|
+
* @param session current session with identity and application tokens
|
|
9
|
+
* @returns {ValidationResult} 'ok' if the session is valid, 'needs_full_auth' if we need to re-authenticate, 'needs_refresh' if we need to refresh the session
|
|
10
|
+
*/
|
|
11
|
+
export declare function validateSession(scopes: string[], applications: OAuthApplications, session: {
|
|
12
|
+
identity: IdentityToken;
|
|
13
|
+
applications: {
|
|
14
|
+
[x: string]: ApplicationToken;
|
|
15
|
+
};
|
|
16
|
+
}): Promise<ValidationResult>;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { applicationId } from './identity.js';
|
|
2
|
+
import constants from '../constants.js';
|
|
3
|
+
import { identity, partners } from '../api.js';
|
|
4
|
+
import { debug } from '../output.js';
|
|
5
|
+
/**
|
|
6
|
+
* Validate if an identity token is valid for the requested scopes
|
|
7
|
+
* @param requestedScopes scopes
|
|
8
|
+
* @param identity
|
|
9
|
+
* @returns
|
|
10
|
+
*/
|
|
11
|
+
function validateScopes(requestedScopes, identity) {
|
|
12
|
+
const currentScopes = identity.scopes;
|
|
13
|
+
return requestedScopes.every((scope) => currentScopes.includes(scope));
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Validate if the current session is valid or we need to refresh/re-authenticate
|
|
17
|
+
* @param scopes {string[]} requested scopes to validate
|
|
18
|
+
* @param applications {OAuthApplications} requested applications
|
|
19
|
+
* @param session current session with identity and application tokens
|
|
20
|
+
* @returns {ValidationResult} 'ok' if the session is valid, 'needs_full_auth' if we need to re-authenticate, 'needs_refresh' if we need to refresh the session
|
|
21
|
+
*/
|
|
22
|
+
export async function validateSession(scopes, applications, session) {
|
|
23
|
+
if (!session)
|
|
24
|
+
return 'needs_full_auth';
|
|
25
|
+
const scopesAreValid = validateScopes(scopes, session.identity);
|
|
26
|
+
const identityIsValid = await identity.validateIdentityToken(session.identity.accessToken);
|
|
27
|
+
if (!scopesAreValid)
|
|
28
|
+
return 'needs_full_auth';
|
|
29
|
+
let tokensAreExpired = isTokenExpired(session.identity);
|
|
30
|
+
let tokensAreRevoked = false;
|
|
31
|
+
if (applications.partnersApi) {
|
|
32
|
+
const appId = applicationId('partners');
|
|
33
|
+
const token = session.applications[appId];
|
|
34
|
+
tokensAreRevoked = tokensAreRevoked || (await isPartnersTokenRevoked(token));
|
|
35
|
+
tokensAreExpired = tokensAreExpired || isTokenExpired(token);
|
|
36
|
+
}
|
|
37
|
+
if (applications.storefrontRendererApi) {
|
|
38
|
+
const appId = applicationId('storefront-renderer');
|
|
39
|
+
const token = session.applications[appId];
|
|
40
|
+
tokensAreExpired = tokensAreExpired || isTokenExpired(token);
|
|
41
|
+
}
|
|
42
|
+
if (applications.adminApi) {
|
|
43
|
+
const appId = applicationId('admin');
|
|
44
|
+
const realAppId = `${applications.adminApi.storeFqdn}-${appId}`;
|
|
45
|
+
const token = session.applications[realAppId];
|
|
46
|
+
tokensAreExpired = tokensAreExpired || isTokenExpired(token);
|
|
47
|
+
}
|
|
48
|
+
debug(`
|
|
49
|
+
The validation of the token for application/identity completed with the following results:
|
|
50
|
+
- It's expired: ${tokensAreExpired}
|
|
51
|
+
- It's been revoked: ${tokensAreRevoked}
|
|
52
|
+
- It's invalid in identity: ${!identityIsValid}
|
|
53
|
+
`);
|
|
54
|
+
if (tokensAreExpired)
|
|
55
|
+
return 'needs_refresh';
|
|
56
|
+
if (tokensAreRevoked)
|
|
57
|
+
return 'needs_full_auth';
|
|
58
|
+
if (!identityIsValid)
|
|
59
|
+
return 'needs_full_auth';
|
|
60
|
+
return 'ok';
|
|
61
|
+
}
|
|
62
|
+
function isTokenExpired(token) {
|
|
63
|
+
if (!token)
|
|
64
|
+
return true;
|
|
65
|
+
return token.expiresAt < expireThreshold();
|
|
66
|
+
}
|
|
67
|
+
async function isPartnersTokenRevoked(token) {
|
|
68
|
+
if (!token)
|
|
69
|
+
return false;
|
|
70
|
+
return partners.checkIfTokenIsRevoked(token.accessToken);
|
|
71
|
+
}
|
|
72
|
+
function expireThreshold() {
|
|
73
|
+
return new Date(Date.now() + constants.session.expirationTimeMarginInMinutes * 60 * 1000);
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=validate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/session/validate.ts"],"names":[],"mappings":"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","sourcesContent":["import {applicationId} from './identity.js'\nimport {ApplicationToken, IdentityToken} from './schema.js'\nimport constants from '../constants.js'\nimport {OAuthApplications} from '../session.js'\nimport {identity, partners} from '../api.js'\nimport {debug} from '../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 * @param requestedScopes scopes\n * @param identity\n * @returns\n */\nfunction validateScopes(requestedScopes: string[], identity: IdentityToken) {\n const currentScopes = identity.scopes\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 {string[]} requested scopes to validate\n * @param applications {OAuthApplications} requested applications\n * @param session current session with identity and application tokens\n * @returns {ValidationResult} '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: {\n identity: IdentityToken\n applications: {[x: string]: ApplicationToken}\n },\n): Promise<ValidationResult> {\n if (!session) return 'needs_full_auth'\n const scopesAreValid = validateScopes(scopes, session.identity)\n const identityIsValid = await identity.validateIdentityToken(session.identity.accessToken)\n if (!scopesAreValid) return 'needs_full_auth'\n let tokensAreExpired = isTokenExpired(session.identity)\n let tokensAreRevoked = false\n\n if (applications.partnersApi) {\n const appId = applicationId('partners')\n const token = session.applications[appId]\n tokensAreRevoked = tokensAreRevoked || (await isPartnersTokenRevoked(token))\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 debug(`\nThe validation of the token for application/identity completed with the following results:\n- It's expired: ${tokensAreExpired}\n- It's been revoked: ${tokensAreRevoked}\n- It's invalid in identity: ${!identityIsValid}\n `)\n\n if (tokensAreExpired) return 'needs_refresh'\n if (tokensAreRevoked) return 'needs_full_auth'\n if (!identityIsValid) return 'needs_full_auth'\n return 'ok'\n}\n\nfunction isTokenExpired(token: ApplicationToken): boolean {\n if (!token) return true\n return token.expiresAt < expireThreshold()\n}\n\nasync function isPartnersTokenRevoked(token: ApplicationToken) {\n if (!token) return false\n return partners.checkIfTokenIsRevoked(token.accessToken)\n}\n\nfunction expireThreshold(): Date {\n return new Date(Date.now() + constants.session.expirationTimeMarginInMinutes * 60 * 1000)\n}\n"]}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { Abort } from './error.js';
|
|
3
|
+
/**
|
|
4
|
+
* A scope supported by the Shopify Admin API.
|
|
5
|
+
*/
|
|
6
|
+
declare type AdminAPIScope = 'graphql' | 'themes' | 'collaborator' | string;
|
|
7
|
+
/**
|
|
8
|
+
* It represents the options to authenticate against the Shopify Admin API.
|
|
9
|
+
*/
|
|
10
|
+
interface AdminAPIOAuthOptions {
|
|
11
|
+
/** Store to request permissions for */
|
|
12
|
+
storeFqdn: string;
|
|
13
|
+
/** List of scopes to request permissions for */
|
|
14
|
+
scopes: AdminAPIScope[];
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* A scope supported by the Partners API.
|
|
18
|
+
*/
|
|
19
|
+
declare type PartnersAPIScope = 'cli' | string;
|
|
20
|
+
interface PartnersAPIOAuthOptions {
|
|
21
|
+
/** List of scopes to request permissions for */
|
|
22
|
+
scopes: PartnersAPIScope[];
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* A scope supported by the Storefront Renderer API.
|
|
26
|
+
*/
|
|
27
|
+
declare type StorefrontRendererScope = 'devtools' | string;
|
|
28
|
+
interface StorefrontRendererAPIOAuthOptions {
|
|
29
|
+
/** List of scopes to request permissions for */
|
|
30
|
+
scopes: StorefrontRendererScope[];
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* It represents the authentication requirements and
|
|
34
|
+
* is the input necessary to trigger the authentication
|
|
35
|
+
* flow.
|
|
36
|
+
*/
|
|
37
|
+
export interface OAuthApplications {
|
|
38
|
+
adminApi?: AdminAPIOAuthOptions;
|
|
39
|
+
storefrontRendererApi?: StorefrontRendererAPIOAuthOptions;
|
|
40
|
+
partnersApi?: PartnersAPIOAuthOptions;
|
|
41
|
+
}
|
|
42
|
+
export interface AdminSession {
|
|
43
|
+
token: string;
|
|
44
|
+
storeFqdn: string;
|
|
45
|
+
}
|
|
46
|
+
export interface OAuthSession {
|
|
47
|
+
admin?: AdminSession;
|
|
48
|
+
partners?: string;
|
|
49
|
+
storefront?: string;
|
|
50
|
+
}
|
|
51
|
+
export declare const PartnerOrganizationNotFoundError: () => Abort;
|
|
52
|
+
/**
|
|
53
|
+
* Ensure that we have a valid session to access the Partners API.
|
|
54
|
+
* If SHOPIFY_CLI_PARTNERS_TOKEN exists, that token will be used to obtain a valid Partners Token
|
|
55
|
+
* If SHOPIFY_CLI_PARTNERS_TOKEN exists, scopes will be ignored
|
|
56
|
+
* @param scopes {string[]} Optional array of extra scopes to authenticate with.
|
|
57
|
+
* @returns {Promise<string>} The access token for the Partners API.
|
|
58
|
+
*/
|
|
59
|
+
export declare function ensureAuthenticatedPartners(scopes?: string[], env?: NodeJS.ProcessEnv): Promise<string>;
|
|
60
|
+
/**
|
|
61
|
+
* Ensure that we have a valid session to access the Storefront API.
|
|
62
|
+
* @param scopes {string[]} Optional array of extra scopes to authenticate with.
|
|
63
|
+
* @returns {Promise<string>} The access token for the Storefront API.
|
|
64
|
+
*/
|
|
65
|
+
export declare function ensureAuthenticatedStorefront(scopes?: string[]): Promise<string>;
|
|
66
|
+
/**
|
|
67
|
+
* Ensure that we have a valid Admin session for the given store.
|
|
68
|
+
* @param store {string} Store fqdn to request auth for
|
|
69
|
+
* @param scopes {string[]} Optional array of extra scopes to authenticate with.
|
|
70
|
+
* @returns {Promise<string>} The access token for the Admin API
|
|
71
|
+
*/
|
|
72
|
+
export declare function ensureAuthenticatedAdmin(store: string, scopes?: string[]): Promise<AdminSession>;
|
|
73
|
+
/**
|
|
74
|
+
* This method ensures that we have a valid session to authenticate against the given applications using the provided scopes.
|
|
75
|
+
* @param applications {OAuthApplications} An object containing the applications we need to be authenticated with.
|
|
76
|
+
* @returns {OAuthSession} An instance with the access tokens organized by application.
|
|
77
|
+
*/
|
|
78
|
+
export declare function ensureAuthenticated(applications: OAuthApplications, env?: NodeJS.ProcessEnv): Promise<OAuthSession>;
|
|
79
|
+
export declare function hasPartnerAccount(partnersToken: string): Promise<boolean>;
|
|
80
|
+
/**
|
|
81
|
+
* If the user creates an account from the Identity website, the created
|
|
82
|
+
* account won't get a Partner organization created. We need to detect that
|
|
83
|
+
* and take the user to create a partner organization.
|
|
84
|
+
* @param partnersToken {string} Partners token
|
|
85
|
+
*/
|
|
86
|
+
export declare function ensureUserHasPartnerAccount(partnersToken: string): Promise<void>;
|
|
87
|
+
export declare function logout(): Promise<void>;
|
|
88
|
+
export {};
|
package/dist/session.js
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { applicationId } from './session/identity.js';
|
|
2
|
+
import { Abort, Bug } from './error.js';
|
|
3
|
+
import { validateSession } from './session/validate.js';
|
|
4
|
+
import { allDefaultScopes, apiScopes } from './session/scopes.js';
|
|
5
|
+
import { identity as identityFqdn } from './environment/fqdn.js';
|
|
6
|
+
import { open } from './system.js';
|
|
7
|
+
import { exchangeAccessForApplicationTokens, exchangeCodeForAccessToken, exchangeCustomPartnerToken, refreshAccessToken, InvalidGrantError, } from './session/exchange.js';
|
|
8
|
+
import { content, token, debug } from './output.js';
|
|
9
|
+
import { keypress } from './ui.js';
|
|
10
|
+
import { authorize } from './session/authorize.js';
|
|
11
|
+
import * as secureStore from './session/store.js';
|
|
12
|
+
import constants from './constants.js';
|
|
13
|
+
import { normalizeStoreName } from './string.js';
|
|
14
|
+
import * as output from './output.js';
|
|
15
|
+
import { partners } from './api.js';
|
|
16
|
+
import { gql } from 'graphql-request';
|
|
17
|
+
const NoSessionError = new Bug('No session found after ensuring authenticated');
|
|
18
|
+
const MissingPartnerTokenError = new Bug('No partners token found after ensuring authenticated');
|
|
19
|
+
const MissingAdminTokenError = new Bug('No admin token found after ensuring authenticated');
|
|
20
|
+
const MissingStorefrontTokenError = new Bug('No storefront token found after ensuring authenticated');
|
|
21
|
+
export const PartnerOrganizationNotFoundError = () => {
|
|
22
|
+
return new Abort(`Couldn't find your Shopify Partners organization`, `Have you confirmed your accounts from the emails you received?`);
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Ensure that we have a valid session to access the Partners API.
|
|
26
|
+
* If SHOPIFY_CLI_PARTNERS_TOKEN exists, that token will be used to obtain a valid Partners Token
|
|
27
|
+
* If SHOPIFY_CLI_PARTNERS_TOKEN exists, scopes will be ignored
|
|
28
|
+
* @param scopes {string[]} Optional array of extra scopes to authenticate with.
|
|
29
|
+
* @returns {Promise<string>} The access token for the Partners API.
|
|
30
|
+
*/
|
|
31
|
+
export async function ensureAuthenticatedPartners(scopes = [], env = process.env) {
|
|
32
|
+
debug(content `Ensuring that the user is authenticated with the Partners API with the following scopes:
|
|
33
|
+
${token.json(scopes)}
|
|
34
|
+
`);
|
|
35
|
+
const envToken = env[constants.environmentVariables.partnersToken];
|
|
36
|
+
if (envToken) {
|
|
37
|
+
return (await exchangeCustomPartnerToken(envToken)).accessToken;
|
|
38
|
+
}
|
|
39
|
+
const tokens = await ensureAuthenticated({ partnersApi: { scopes } });
|
|
40
|
+
if (!tokens.partners) {
|
|
41
|
+
throw MissingPartnerTokenError;
|
|
42
|
+
}
|
|
43
|
+
return tokens.partners;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Ensure that we have a valid session to access the Storefront API.
|
|
47
|
+
* @param scopes {string[]} Optional array of extra scopes to authenticate with.
|
|
48
|
+
* @returns {Promise<string>} The access token for the Storefront API.
|
|
49
|
+
*/
|
|
50
|
+
export async function ensureAuthenticatedStorefront(scopes = []) {
|
|
51
|
+
debug(content `Ensuring that the user is authenticated with the Storefront API with the following scopes:
|
|
52
|
+
${token.json(scopes)}
|
|
53
|
+
`);
|
|
54
|
+
const tokens = await ensureAuthenticated({ storefrontRendererApi: { scopes } });
|
|
55
|
+
if (!tokens.storefront) {
|
|
56
|
+
throw MissingStorefrontTokenError;
|
|
57
|
+
}
|
|
58
|
+
return tokens.storefront;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Ensure that we have a valid Admin session for the given store.
|
|
62
|
+
* @param store {string} Store fqdn to request auth for
|
|
63
|
+
* @param scopes {string[]} Optional array of extra scopes to authenticate with.
|
|
64
|
+
* @returns {Promise<string>} The access token for the Admin API
|
|
65
|
+
*/
|
|
66
|
+
export async function ensureAuthenticatedAdmin(store, scopes = []) {
|
|
67
|
+
debug(content `Ensuring that the user is authenticated with the Admin API with the following scopes for the store ${token.raw(store)}:
|
|
68
|
+
${token.json(scopes)}
|
|
69
|
+
`);
|
|
70
|
+
const tokens = await ensureAuthenticated({ adminApi: { scopes, storeFqdn: store } });
|
|
71
|
+
if (!tokens.admin) {
|
|
72
|
+
throw MissingAdminTokenError;
|
|
73
|
+
}
|
|
74
|
+
return tokens.admin;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* This method ensures that we have a valid session to authenticate against the given applications using the provided scopes.
|
|
78
|
+
* @param applications {OAuthApplications} An object containing the applications we need to be authenticated with.
|
|
79
|
+
* @returns {OAuthSession} An instance with the access tokens organized by application.
|
|
80
|
+
*/
|
|
81
|
+
export async function ensureAuthenticated(applications, env = process.env) {
|
|
82
|
+
const fqdn = await identityFqdn();
|
|
83
|
+
if (applications.adminApi?.storeFqdn) {
|
|
84
|
+
applications.adminApi.storeFqdn = normalizeStoreName(applications.adminApi.storeFqdn);
|
|
85
|
+
}
|
|
86
|
+
const currentSession = (await secureStore.fetch()) || {};
|
|
87
|
+
const fqdnSession = currentSession[fqdn];
|
|
88
|
+
const scopes = getFlattenScopes(applications);
|
|
89
|
+
debug(content `Validating existing session against the scopes:
|
|
90
|
+
${token.json(scopes)}
|
|
91
|
+
For applications:
|
|
92
|
+
${token.json(applications)}
|
|
93
|
+
`);
|
|
94
|
+
const validationResult = await validateSession(scopes, applications, fqdnSession);
|
|
95
|
+
let newSession = {};
|
|
96
|
+
if (validationResult === 'needs_full_auth') {
|
|
97
|
+
debug(content `Initiating the full authentication flow...`);
|
|
98
|
+
newSession = await executeCompleteFlow(applications, fqdn);
|
|
99
|
+
}
|
|
100
|
+
else if (validationResult === 'needs_refresh') {
|
|
101
|
+
debug(content `The current session is valid but needs refresh. Refreshing...`);
|
|
102
|
+
try {
|
|
103
|
+
newSession = await refreshTokens(fqdnSession.identity, applications, fqdn);
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
if (error instanceof InvalidGrantError) {
|
|
107
|
+
newSession = await executeCompleteFlow(applications, fqdn);
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
throw error;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const completeSession = { ...currentSession, ...newSession };
|
|
115
|
+
await secureStore.store(completeSession);
|
|
116
|
+
const tokens = await tokensFor(applications, completeSession, fqdn);
|
|
117
|
+
// Overwrite partners token if using a custom CLI Token
|
|
118
|
+
const envToken = env[constants.environmentVariables.partnersToken];
|
|
119
|
+
if (envToken && applications.partnersApi) {
|
|
120
|
+
tokens.partners = (await exchangeCustomPartnerToken(envToken)).accessToken;
|
|
121
|
+
}
|
|
122
|
+
if (!envToken && tokens.partners) {
|
|
123
|
+
await ensureUserHasPartnerAccount(tokens.partners);
|
|
124
|
+
}
|
|
125
|
+
return tokens;
|
|
126
|
+
}
|
|
127
|
+
export async function hasPartnerAccount(partnersToken) {
|
|
128
|
+
try {
|
|
129
|
+
await partners.request(gql `
|
|
130
|
+
{
|
|
131
|
+
organizations(first: 1) {
|
|
132
|
+
nodes {
|
|
133
|
+
id
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
`, partnersToken);
|
|
138
|
+
return true;
|
|
139
|
+
// eslint-disable-next-line no-catch-all/no-catch-all
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
if (error instanceof partners.RequestClientError && error.statusCode === 404) {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* If the user creates an account from the Identity website, the created
|
|
152
|
+
* account won't get a Partner organization created. We need to detect that
|
|
153
|
+
* and take the user to create a partner organization.
|
|
154
|
+
* @param partnersToken {string} Partners token
|
|
155
|
+
*/
|
|
156
|
+
export async function ensureUserHasPartnerAccount(partnersToken) {
|
|
157
|
+
debug(content `Verifying that the user has a Partner organization`);
|
|
158
|
+
if (!(await hasPartnerAccount(partnersToken))) {
|
|
159
|
+
output.info(`\nA Shopify Partners organization is needed to proceed.`);
|
|
160
|
+
output.info(`👉 Press any key to create one`);
|
|
161
|
+
await keypress();
|
|
162
|
+
open(`https://partners.shopify.com/signup`);
|
|
163
|
+
output.info(output.content `👉 Press any key when you have ${output.token.cyan('created the organization')}`);
|
|
164
|
+
output.warn(output.content `Make sure you've confirmed your Shopify and the Partner organization from the email`);
|
|
165
|
+
await keypress();
|
|
166
|
+
if (!(await hasPartnerAccount(partnersToken))) {
|
|
167
|
+
throw PartnerOrganizationNotFoundError();
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
async function executeCompleteFlow(applications, identityFqdn) {
|
|
172
|
+
const scopes = getFlattenScopes(applications);
|
|
173
|
+
const exchangeScopes = getExchangeScopes(applications);
|
|
174
|
+
const store = applications.adminApi?.storeFqdn;
|
|
175
|
+
// Authorize user via browser
|
|
176
|
+
debug(content `Authorizing through Identity's website...`);
|
|
177
|
+
const code = await authorize(scopes);
|
|
178
|
+
// Exchange code for identity token
|
|
179
|
+
debug(content `Authorization code received. Exchanging it for a CLI token...`);
|
|
180
|
+
const identityToken = await exchangeCodeForAccessToken(code);
|
|
181
|
+
// Exchange identity token for application tokens
|
|
182
|
+
debug(content `CLI token received. Exchanging it for application tokens...`);
|
|
183
|
+
const result = await exchangeAccessForApplicationTokens(identityToken, exchangeScopes, store);
|
|
184
|
+
const session = {
|
|
185
|
+
[identityFqdn]: {
|
|
186
|
+
identity: identityToken,
|
|
187
|
+
applications: result,
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
output.completed('Logged in.');
|
|
191
|
+
return session;
|
|
192
|
+
}
|
|
193
|
+
async function refreshTokens(token, applications, fqdn) {
|
|
194
|
+
// Refresh Identity Token
|
|
195
|
+
const identityToken = await refreshAccessToken(token);
|
|
196
|
+
// Exchange new identity token for application tokens
|
|
197
|
+
const exchangeScopes = getExchangeScopes(applications);
|
|
198
|
+
const applicationTokens = await exchangeAccessForApplicationTokens(identityToken, exchangeScopes, applications.adminApi?.storeFqdn);
|
|
199
|
+
return {
|
|
200
|
+
[fqdn]: {
|
|
201
|
+
identity: identityToken,
|
|
202
|
+
applications: applicationTokens,
|
|
203
|
+
},
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
async function tokensFor(applications, session, fqdn) {
|
|
207
|
+
const fqdnSession = session[fqdn];
|
|
208
|
+
if (!fqdnSession) {
|
|
209
|
+
throw NoSessionError;
|
|
210
|
+
}
|
|
211
|
+
const tokens = {};
|
|
212
|
+
if (applications.adminApi) {
|
|
213
|
+
const appId = applicationId('admin');
|
|
214
|
+
const realAppId = `${applications.adminApi.storeFqdn}-${appId}`;
|
|
215
|
+
const token = fqdnSession.applications[realAppId]?.accessToken;
|
|
216
|
+
if (token) {
|
|
217
|
+
tokens.admin = { token, storeFqdn: applications.adminApi.storeFqdn };
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
if (applications.partnersApi) {
|
|
221
|
+
const appId = applicationId('partners');
|
|
222
|
+
tokens.partners = fqdnSession.applications[appId]?.accessToken;
|
|
223
|
+
}
|
|
224
|
+
if (applications.storefrontRendererApi) {
|
|
225
|
+
const appId = applicationId('storefront-renderer');
|
|
226
|
+
tokens.storefront = fqdnSession.applications[appId]?.accessToken;
|
|
227
|
+
}
|
|
228
|
+
return tokens;
|
|
229
|
+
}
|
|
230
|
+
// Scope Helpers
|
|
231
|
+
function getFlattenScopes(apps) {
|
|
232
|
+
const admin = apps.adminApi?.scopes || [];
|
|
233
|
+
const partner = apps.partnersApi?.scopes || [];
|
|
234
|
+
const storefront = apps.storefrontRendererApi?.scopes || [];
|
|
235
|
+
const requestedScopes = [...admin, ...partner, ...storefront];
|
|
236
|
+
return allDefaultScopes(requestedScopes);
|
|
237
|
+
}
|
|
238
|
+
function getExchangeScopes(apps) {
|
|
239
|
+
const adminScope = apps.adminApi?.scopes || [];
|
|
240
|
+
const partnerScope = apps.partnersApi?.scopes || [];
|
|
241
|
+
const storefrontScopes = apps.storefrontRendererApi?.scopes || [];
|
|
242
|
+
return {
|
|
243
|
+
admin: apiScopes('admin', adminScope),
|
|
244
|
+
partners: apiScopes('partners', partnerScope),
|
|
245
|
+
storefront: apiScopes('storefront-renderer', storefrontScopes),
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
export function logout() {
|
|
249
|
+
return secureStore.remove();
|
|
250
|
+
}
|
|
251
|
+
//# sourceMappingURL=session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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","sourcesContent":["import {applicationId} from './session/identity.js'\nimport {Abort, Bug} from './error.js'\nimport {validateSession} from './session/validate.js'\nimport {allDefaultScopes, apiScopes} from './session/scopes.js'\nimport {identity as identityFqdn} from './environment/fqdn.js'\nimport {open} from './system.js'\nimport {\n exchangeAccessForApplicationTokens,\n exchangeCodeForAccessToken,\n exchangeCustomPartnerToken,\n ExchangeScopes,\n refreshAccessToken,\n InvalidGrantError,\n} from './session/exchange.js'\n\nimport {content, token, debug} from './output.js'\nimport {keypress} from './ui.js'\n\nimport {authorize} from './session/authorize.js'\nimport {IdentityToken, Session} from './session/schema.js'\nimport * as secureStore from './session/store.js'\nimport constants from './constants.js'\nimport {normalizeStoreName} from './string.js'\nimport * as output from './output.js'\nimport {partners} from './api.js'\nimport {gql} from 'graphql-request'\n\nconst NoSessionError = new Bug('No session found after ensuring authenticated')\nconst MissingPartnerTokenError = new Bug('No partners token found after ensuring authenticated')\nconst MissingAdminTokenError = new Bug('No admin token found after ensuring authenticated')\nconst MissingStorefrontTokenError = new Bug('No storefront token found after ensuring authenticated')\n\n/**\n * A scope supported by the Shopify Admin API.\n */\ntype AdminAPIScope = 'graphql' | 'themes' | 'collaborator' | string\n\n/**\n * It represents the options to authenticate against the Shopify Admin API.\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 */\ntype PartnersAPIScope = 'cli' | string\ninterface PartnersAPIOAuthOptions {\n /** List of scopes to request permissions for */\n scopes: PartnersAPIScope[]\n}\n\n/**\n * A scope supported by the Storefront Renderer API.\n */\ntype StorefrontRendererScope = 'devtools' | string\ninterface StorefrontRendererAPIOAuthOptions {\n /** List of scopes to request permissions for */\n scopes: StorefrontRendererScope[]\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}\n\nexport interface AdminSession {\n token: string\n storeFqdn: string\n}\n\nexport interface OAuthSession {\n admin?: AdminSession\n partners?: string\n storefront?: string\n}\nexport const PartnerOrganizationNotFoundError = () => {\n return new Abort(\n `Couldn't find your Shopify Partners organization`,\n `Have you confirmed your accounts from the emails you received?`,\n )\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 * @param scopes {string[]} Optional array of extra scopes to authenticate with.\n * @returns {Promise<string>} The access token for the Partners API.\n */\nexport async function ensureAuthenticatedPartners(scopes: string[] = [], env = process.env): Promise<string> {\n debug(content`Ensuring that the user is authenticated with the Partners API with the following scopes:\n${token.json(scopes)}\n`)\n const envToken = env[constants.environmentVariables.partnersToken]\n if (envToken) {\n return (await exchangeCustomPartnerToken(envToken)).accessToken\n }\n const tokens = await ensureAuthenticated({partnersApi: {scopes}})\n if (!tokens.partners) {\n throw MissingPartnerTokenError\n }\n return tokens.partners\n}\n\n/**\n * Ensure that we have a valid session to access the Storefront API.\n * @param scopes {string[]} Optional array of extra scopes to authenticate with.\n * @returns {Promise<string>} The access token for the Storefront API.\n */\nexport async function ensureAuthenticatedStorefront(scopes: string[] = []): Promise<string> {\n debug(content`Ensuring that the user is authenticated with the Storefront API with the following scopes:\n${token.json(scopes)}\n`)\n const tokens = await ensureAuthenticated({storefrontRendererApi: {scopes}})\n if (!tokens.storefront) {\n throw MissingStorefrontTokenError\n }\n return tokens.storefront\n}\n\n/**\n * Ensure that we have a valid Admin session for the given store.\n * @param store {string} Store fqdn to request auth for\n * @param scopes {string[]} Optional array of extra scopes to authenticate with.\n * @returns {Promise<string>} The access token for the Admin API\n */\nexport async function ensureAuthenticatedAdmin(store: string, scopes: string[] = []): Promise<AdminSession> {\n debug(content`Ensuring that the user is authenticated with the Admin API with the following scopes for the store ${token.raw(\n store,\n )}:\n${token.json(scopes)}\n`)\n const tokens = await ensureAuthenticated({adminApi: {scopes, storeFqdn: store}})\n if (!tokens.admin) {\n throw MissingAdminTokenError\n }\n return tokens.admin\n}\n\n/**\n * This method ensures that we have a valid session to authenticate against the given applications using the provided scopes.\n * @param applications {OAuthApplications} An object containing the applications we need to be authenticated with.\n * @returns {OAuthSession} An instance with the access tokens organized by application.\n */\nexport async function ensureAuthenticated(applications: OAuthApplications, env = process.env): Promise<OAuthSession> {\n const fqdn = await identityFqdn()\n\n if (applications.adminApi?.storeFqdn) {\n applications.adminApi.storeFqdn = normalizeStoreName(applications.adminApi.storeFqdn)\n }\n\n const currentSession = (await secureStore.fetch()) || {}\n const fqdnSession = currentSession[fqdn]\n const scopes = getFlattenScopes(applications)\n\n debug(content`Validating existing session against the scopes:\n${token.json(scopes)}\nFor applications:\n${token.json(applications)}\n`)\n const validationResult = await validateSession(scopes, applications, fqdnSession)\n\n let newSession = {}\n\n if (validationResult === 'needs_full_auth') {\n debug(content`Initiating the full authentication flow...`)\n newSession = await executeCompleteFlow(applications, fqdn)\n } else if (validationResult === 'needs_refresh') {\n debug(content`The current session is valid but needs refresh. Refreshing...`)\n try {\n newSession = await refreshTokens(fqdnSession.identity, applications, fqdn)\n } catch (error) {\n if (error instanceof InvalidGrantError) {\n newSession = await executeCompleteFlow(applications, fqdn)\n } else {\n throw error\n }\n }\n }\n\n const completeSession: Session = {...currentSession, ...newSession}\n await secureStore.store(completeSession)\n const tokens = await tokensFor(applications, completeSession, fqdn)\n\n // Overwrite partners token if using a custom CLI Token\n const envToken = env[constants.environmentVariables.partnersToken]\n if (envToken && applications.partnersApi) {\n tokens.partners = (await exchangeCustomPartnerToken(envToken)).accessToken\n }\n if (!envToken && tokens.partners) {\n await ensureUserHasPartnerAccount(tokens.partners)\n }\n\n return tokens\n}\n\nexport async function hasPartnerAccount(partnersToken: string): Promise<boolean> {\n try {\n await partners.request(\n gql`\n {\n organizations(first: 1) {\n nodes {\n id\n }\n }\n }\n `,\n partnersToken,\n )\n return true\n // eslint-disable-next-line no-catch-all/no-catch-all\n } catch (error) {\n if (error instanceof partners.RequestClientError && error.statusCode === 404) {\n return false\n } else {\n return true\n }\n }\n}\n\n/**\n * If the user creates an account from the Identity website, the created\n * account won't get a Partner organization created. We need to detect that\n * and take the user to create a partner organization.\n * @param partnersToken {string} Partners token\n */\nexport async function ensureUserHasPartnerAccount(partnersToken: string) {\n debug(content`Verifying that the user has a Partner organization`)\n if (!(await hasPartnerAccount(partnersToken))) {\n output.info(`\\nA Shopify Partners organization is needed to proceed.`)\n output.info(`👉 Press any key to create one`)\n await keypress()\n open(`https://partners.shopify.com/signup`)\n output.info(output.content`👉 Press any key when you have ${output.token.cyan('created the organization')}`)\n output.warn(output.content`Make sure you've confirmed your Shopify and the Partner organization from the email`)\n await keypress()\n if (!(await hasPartnerAccount(partnersToken))) {\n throw PartnerOrganizationNotFoundError()\n }\n }\n}\n\nasync function executeCompleteFlow(applications: OAuthApplications, identityFqdn: string): Promise<Session> {\n const scopes = getFlattenScopes(applications)\n const exchangeScopes = getExchangeScopes(applications)\n const store = applications.adminApi?.storeFqdn\n\n // Authorize user via browser\n debug(content`Authorizing through Identity's website...`)\n const code = await authorize(scopes)\n\n // Exchange code for identity token\n debug(content`Authorization code received. Exchanging it for a CLI token...`)\n const identityToken = await exchangeCodeForAccessToken(code)\n\n // Exchange identity token for application tokens\n debug(content`CLI token received. Exchanging it for application tokens...`)\n const result = await exchangeAccessForApplicationTokens(identityToken, exchangeScopes, store)\n\n const session: Session = {\n [identityFqdn]: {\n identity: identityToken,\n applications: result,\n },\n }\n\n output.completed('Logged in.')\n\n return session\n}\n\nasync function refreshTokens(token: IdentityToken, applications: OAuthApplications, fqdn: string): Promise<Session> {\n // Refresh Identity Token\n const identityToken = await refreshAccessToken(token)\n\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 [fqdn]: {\n identity: identityToken,\n applications: applicationTokens,\n },\n }\n}\n\nasync function tokensFor(applications: OAuthApplications, session: Session, fqdn: string): Promise<OAuthSession> {\n const fqdnSession = session[fqdn]\n if (!fqdnSession) {\n throw NoSessionError\n }\n const tokens: OAuthSession = {}\n if (applications.adminApi) {\n const appId = applicationId('admin')\n const realAppId = `${applications.adminApi.storeFqdn}-${appId}`\n const token = fqdnSession.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 = fqdnSession.applications[appId]?.accessToken\n }\n\n if (applications.storefrontRendererApi) {\n const appId = applicationId('storefront-renderer')\n tokens.storefront = fqdnSession.applications[appId]?.accessToken\n }\n return tokens\n}\n\n// Scope Helpers\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 requestedScopes = [...admin, ...partner, ...storefront]\n return allDefaultScopes(requestedScopes)\n}\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 return {\n admin: apiScopes('admin', adminScope),\n partners: apiScopes('partners', partnerScope),\n storefront: apiScopes('storefront-renderer', storefrontScopes),\n }\n}\n\nexport function logout() {\n return secureStore.remove()\n}\n"]}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const schema = {
|
|
2
|
+
appInfo: {
|
|
3
|
+
type: 'array',
|
|
4
|
+
items: {
|
|
5
|
+
type: 'object',
|
|
6
|
+
properties: {
|
|
7
|
+
appId: {
|
|
8
|
+
type: 'string',
|
|
9
|
+
},
|
|
10
|
+
title: {
|
|
11
|
+
type: 'string',
|
|
12
|
+
},
|
|
13
|
+
orgId: {
|
|
14
|
+
type: 'string',
|
|
15
|
+
},
|
|
16
|
+
storeFqdn: {
|
|
17
|
+
type: 'string',
|
|
18
|
+
},
|
|
19
|
+
directory: {
|
|
20
|
+
type: 'string',
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
export default schema;
|
|
27
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/store/schema.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,GAAoB;IAC9B,OAAO,EAAE;QACP,IAAI,EAAE,OAAO;QACb,KAAK,EAAE;YACL,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE;gBACV,KAAK,EAAE;oBACL,IAAI,EAAE,QAAQ;iBACf;gBACD,KAAK,EAAE;oBACL,IAAI,EAAE,QAAQ;iBACf;gBACD,KAAK,EAAE;oBACL,IAAI,EAAE,QAAQ;iBACf;gBACD,SAAS,EAAE;oBACT,IAAI,EAAE,QAAQ;iBACf;gBACD,SAAS,EAAE;oBACT,IAAI,EAAE,QAAQ;iBACf;aACF;SACF;KACF;CACF,CAAA;AAED,eAAe,MAAM,CAAA","sourcesContent":["import type {Schema} from 'conf'\n\nconst schema: Schema<unknown> = {\n appInfo: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n appId: {\n type: 'string',\n },\n title: {\n type: 'string',\n },\n orgId: {\n type: 'string',\n },\n storeFqdn: {\n type: 'string',\n },\n directory: {\n type: 'string',\n },\n },\n },\n },\n}\n\nexport default schema\n"]}
|
package/dist/store.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import Conf from 'conf';
|
|
2
|
+
export interface CachedAppInfo {
|
|
3
|
+
directory: string;
|
|
4
|
+
appId: string;
|
|
5
|
+
title?: string;
|
|
6
|
+
orgId?: string;
|
|
7
|
+
storeFqdn?: string;
|
|
8
|
+
}
|
|
9
|
+
interface ConfSchema {
|
|
10
|
+
appInfo: CachedAppInfo[];
|
|
11
|
+
themeStore: string;
|
|
12
|
+
session: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function cliKitStore(): CLIKitStore;
|
|
15
|
+
export declare function initializeCliKitStore(): Promise<void>;
|
|
16
|
+
export declare class CLIKitStore extends Conf<ConfSchema> {
|
|
17
|
+
getAppInfo(directory: string): CachedAppInfo | undefined;
|
|
18
|
+
setAppInfo(options: {
|
|
19
|
+
directory: string;
|
|
20
|
+
appId: string;
|
|
21
|
+
title?: string;
|
|
22
|
+
storeFqdn?: string;
|
|
23
|
+
orgId?: string;
|
|
24
|
+
}): void;
|
|
25
|
+
clearAppInfo(directory: string): void;
|
|
26
|
+
getTheme(): string | undefined;
|
|
27
|
+
setTheme(store: string): void;
|
|
28
|
+
getSession(): string | undefined;
|
|
29
|
+
setSession(store: string): void;
|
|
30
|
+
removeSession(): void;
|
|
31
|
+
}
|
|
32
|
+
export {};
|
package/dist/store.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { content, token, debug } from './output.js';
|
|
2
|
+
import constants from './constants.js';
|
|
3
|
+
import { Abort } from './error.js';
|
|
4
|
+
import Conf from 'conf';
|
|
5
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
6
|
+
// @ts-ignore
|
|
7
|
+
const migrations = {};
|
|
8
|
+
const schema = {
|
|
9
|
+
appInfo: {
|
|
10
|
+
type: 'array',
|
|
11
|
+
items: {
|
|
12
|
+
type: 'object',
|
|
13
|
+
properties: {
|
|
14
|
+
appId: {
|
|
15
|
+
type: 'string',
|
|
16
|
+
},
|
|
17
|
+
orgId: {
|
|
18
|
+
type: 'string',
|
|
19
|
+
},
|
|
20
|
+
storeFqdn: {
|
|
21
|
+
type: 'string',
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
let _instance;
|
|
28
|
+
export function cliKitStore() {
|
|
29
|
+
if (!_instance) {
|
|
30
|
+
throw new Abort("The CLIKitStore instance hasn't been initialized");
|
|
31
|
+
}
|
|
32
|
+
return _instance;
|
|
33
|
+
}
|
|
34
|
+
export async function initializeCliKitStore() {
|
|
35
|
+
if (!_instance) {
|
|
36
|
+
// eslint-disable-next-line require-atomic-updates
|
|
37
|
+
_instance = new CLIKitStore({
|
|
38
|
+
schema,
|
|
39
|
+
migrations,
|
|
40
|
+
projectName: 'shopify-cli-kit',
|
|
41
|
+
projectVersion: await constants.versions.cliKit(),
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
export class CLIKitStore extends Conf {
|
|
46
|
+
getAppInfo(directory) {
|
|
47
|
+
debug(content `Reading cached app information for directory ${token.path(directory)}...`);
|
|
48
|
+
const apps = this.get('appInfo') ?? [];
|
|
49
|
+
return apps.find((app) => app.directory === directory);
|
|
50
|
+
}
|
|
51
|
+
setAppInfo(options) {
|
|
52
|
+
debug(content `Storing app information for directory ${token.path(options.directory)}:
|
|
53
|
+
${token.json(options)}
|
|
54
|
+
`);
|
|
55
|
+
const apps = this.get('appInfo') ?? [];
|
|
56
|
+
const index = apps.findIndex((saved) => saved.directory === options.directory);
|
|
57
|
+
if (index === -1) {
|
|
58
|
+
apps.push(options);
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
const app = apps[index];
|
|
62
|
+
apps[index] = {
|
|
63
|
+
appId: options.appId,
|
|
64
|
+
directory: options.directory,
|
|
65
|
+
title: options.title ?? app.title,
|
|
66
|
+
storeFqdn: options.storeFqdn ?? app.storeFqdn,
|
|
67
|
+
orgId: options.orgId ?? app.orgId,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
this.set('appInfo', apps);
|
|
71
|
+
}
|
|
72
|
+
clearAppInfo(directory) {
|
|
73
|
+
debug(content `Clearning app information for directory ${token.path(directory)}...`);
|
|
74
|
+
const apps = this.get('appInfo') ?? [];
|
|
75
|
+
const index = apps.findIndex((saved) => saved.directory === directory);
|
|
76
|
+
if (index !== -1) {
|
|
77
|
+
apps.splice(index, 1);
|
|
78
|
+
}
|
|
79
|
+
this.set('appInfo', apps);
|
|
80
|
+
}
|
|
81
|
+
getTheme() {
|
|
82
|
+
debug(content `Getting theme store...`);
|
|
83
|
+
return this.get('themeStore');
|
|
84
|
+
}
|
|
85
|
+
setTheme(store) {
|
|
86
|
+
debug(content `Setting theme store...`);
|
|
87
|
+
this.set('themeStore', store);
|
|
88
|
+
}
|
|
89
|
+
getSession() {
|
|
90
|
+
debug(content `Getting session store...`);
|
|
91
|
+
return this.get('sessionStore');
|
|
92
|
+
}
|
|
93
|
+
setSession(store) {
|
|
94
|
+
debug(content `Setting session store...`);
|
|
95
|
+
this.set('sessionStore', store);
|
|
96
|
+
}
|
|
97
|
+
removeSession() {
|
|
98
|
+
debug(content `Removing session store...`);
|
|
99
|
+
this.set('sessionStore', '');
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=store.js.map
|