@shopify/cli-kit
Version:
A set of utilities, interfaces, and models that are common across all the platform features
224 lines • 10.1 kB
JavaScript
import { AbortError, BugError } from './error.js';
import { getPartnersToken } from './environment.js';
import { nonRandomUUID } from './crypto.js';
import { shopifyFetch } from './http.js';
import * as sessionStore from '../../private/node/session/store.js';
import { exchangeCustomPartnerToken, exchangeCliTokenForAppManagementAccessToken, exchangeCliTokenForBusinessPlatformAccessToken, } from '../../private/node/session/exchange.js';
import { outputContent, outputToken, outputDebug } from '../../public/node/output.js';
import { ensureAuthenticated, setLastSeenAuthMethod, setLastSeenUserIdAfterAuth, } from '../../private/node/session.js';
import { isThemeAccessSession } from '../../private/node/api/rest.js';
/**
* Type guard to check if an account is a UserAccount.
*
* @param account - The account to check.
* @returns True if the account is a UserAccount.
*/
export function isUserAccount(account) {
return account.type === 'UserAccount';
}
/**
* Type guard to check if an account is a ServiceAccount.
*
* @param account - The account to check.
* @returns True if the account is a ServiceAccount.
*/
export function isServiceAccount(account) {
return account.type === 'ServiceAccount';
}
/**
* Ensure that we have a valid session with no particular scopes.
*
* @param env - Optional environment variables to use.
* @param options - Optional extra options to use.
* @returns The user ID.
*/
export async function ensureAuthenticatedUser(env = process.env, options = {}) {
outputDebug(outputContent `Ensuring that the user is authenticated with no particular scopes`);
const tokens = await ensureAuthenticated({}, env, options);
return { userId: tokens.userId };
}
/**
* Ensure that we have a valid session to access the Partners API.
* If SHOPIFY_CLI_PARTNERS_TOKEN exists, that token will be used to obtain a valid Partners Token
* If SHOPIFY_CLI_PARTNERS_TOKEN exists, scopes will be ignored.
*
* @param scopes - Optional array of extra scopes to authenticate with.
* @param env - Optional environment variables to use.
* @param options - Optional extra options to use.
* @returns The access token for the Partners API.
*/
export async function ensureAuthenticatedPartners(scopes = [], env = process.env, options = {}) {
outputDebug(outputContent `Ensuring that the user is authenticated with the Partners API with the following scopes:
${outputToken.json(scopes)}
`);
const envToken = getPartnersToken();
if (envToken) {
const result = await exchangeCustomPartnerToken(envToken);
return { token: result.accessToken, userId: result.userId };
}
const tokens = await ensureAuthenticated({ partnersApi: { scopes } }, env, options);
if (!tokens.partners) {
throw new BugError('No partners token found after ensuring authenticated');
}
return { token: tokens.partners, userId: tokens.userId };
}
/**
* Ensure that we have a valid session to access the App Management API.
*
* @param options - Optional extra options to use.
* @param appManagementScopes - Optional array of extra scopes to authenticate with.
* @param businessPlatformScopes - Optional array of extra scopes to authenticate with.
* @param env - Optional environment variables to use.
* @returns The access token for the App Management API.
*/
export async function ensureAuthenticatedAppManagementAndBusinessPlatform(options = {}, appManagementScopes = [], businessPlatformScopes = [], env = process.env) {
outputDebug(outputContent `Ensuring that the user is authenticated with the App Management API with the following scopes:
${outputToken.json(appManagementScopes)}
`);
const envToken = getPartnersToken();
if (envToken) {
const appManagmentToken = await exchangeCliTokenForAppManagementAccessToken(envToken);
const businessPlatformToken = await exchangeCliTokenForBusinessPlatformAccessToken(envToken);
return {
appManagementToken: appManagmentToken.accessToken,
userId: appManagmentToken.userId,
businessPlatformToken: businessPlatformToken.accessToken,
};
}
const tokens = await ensureAuthenticated({ appManagementApi: { scopes: appManagementScopes }, businessPlatformApi: { scopes: businessPlatformScopes } }, env, options);
if (!tokens.appManagement || !tokens.businessPlatform) {
throw new BugError('No App Management or Business Platform token found after ensuring authenticated');
}
return {
appManagementToken: tokens.appManagement,
userId: tokens.userId,
businessPlatformToken: tokens.businessPlatform,
};
}
/**
* Ensure that we have a valid session to access the Storefront API.
*
* @param scopes - Optional array of extra scopes to authenticate with.
* @param password - Optional password to use.
* @param options - Optional extra options to use.
* @returns The access token for the Storefront API.
*/
export async function ensureAuthenticatedStorefront(scopes = [], password = undefined, options = {}) {
if (password) {
const session = { token: password, storeFqdn: '' };
const authMethod = isThemeAccessSession(session) ? 'theme_access_token' : 'custom_app_token';
setLastSeenAuthMethod(authMethod);
setLastSeenUserIdAfterAuth(nonRandomUUID(password));
return password;
}
outputDebug(outputContent `Ensuring that the user is authenticated with the Storefront API with the following scopes:
${outputToken.json(scopes)}
`);
const tokens = await ensureAuthenticated({ storefrontRendererApi: { scopes } }, process.env, options);
if (!tokens.storefront) {
throw new BugError('No storefront token found after ensuring authenticated');
}
return tokens.storefront;
}
/**
* Ensure that we have a valid Admin session for the given store.
*
* @param store - Store fqdn to request auth for.
* @param scopes - Optional array of extra scopes to authenticate with.
* @param options - Optional extra options to use.
* @returns The access token for the Admin API.
*/
export async function ensureAuthenticatedAdmin(store, scopes = [], options = {}) {
outputDebug(outputContent `Ensuring that the user is authenticated with the Admin API with the following scopes for the store ${outputToken.raw(store)}:
${outputToken.json(scopes)}
`);
const tokens = await ensureAuthenticated({ adminApi: { scopes, storeFqdn: store } }, process.env, {
...options,
});
if (!tokens.admin) {
throw new BugError('No admin token found after ensuring authenticated');
}
return tokens.admin;
}
/**
* Ensure that we have a valid session to access the Theme API.
* If a password is provided, that token will be used against Theme Access API.
* Otherwise, it will ensure that the user is authenticated with the Admin API.
*
* @param store - Store fqdn to request auth for.
* @param password - Password generated from Theme Access app.
* @param scopes - Optional array of extra scopes to authenticate with.
* @param options - Optional extra options to use.
* @returns The access token and store.
*/
export async function ensureAuthenticatedThemes(store, password, scopes = [], options = {}) {
outputDebug(outputContent `Ensuring that the user is authenticated with the Theme API with the following scopes:
${outputToken.json(scopes)}
`);
if (password) {
const session = { token: password, storeFqdn: store };
const authMethod = isThemeAccessSession(session) ? 'theme_access_token' : 'custom_app_token';
setLastSeenAuthMethod(authMethod);
setLastSeenUserIdAfterAuth(nonRandomUUID(password));
return session;
}
return ensureAuthenticatedAdmin(store, scopes, options);
}
/**
* Ensure that we have a valid session to access the Business Platform API.
*
* @param scopes - Optional array of extra scopes to authenticate with.
* @returns The access token for the Business Platform API.
*/
export async function ensureAuthenticatedBusinessPlatform(scopes = []) {
outputDebug(outputContent `Ensuring that the user is authenticated with the Business Platform API with the following scopes:
${outputToken.json(scopes)}
`);
const tokens = await ensureAuthenticated({ businessPlatformApi: { scopes } }, process.env);
if (!tokens.businessPlatform) {
throw new BugError('No business-platform token found after ensuring authenticated');
}
return tokens.businessPlatform;
}
/**
* Logout from Shopify.
*
* @returns A promise that resolves when the logout is complete.
*/
export function logout() {
return sessionStore.remove();
}
/**
* Ensure that we have a valid Admin session for the given store, with access on behalf of the app.
*
* See `ensureAuthenticatedAdmin` for access on behalf of a user.
*
* @param storeFqdn - Store fqdn to request auth for.
* @param clientId - Client ID of the app.
* @param clientSecret - Client secret of the app.
* @returns The access token for the Admin API.
*/
export async function ensureAuthenticatedAdminAsApp(storeFqdn, clientId, clientSecret) {
const bodyData = {
client_id: clientId,
client_secret: clientSecret,
grant_type: 'client_credentials',
};
const tokenResponse = await shopifyFetch(`https://${storeFqdn}/admin/oauth/access_token`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(bodyData),
}, 'slow-request');
if (tokenResponse.status === 400) {
const body = await tokenResponse.text();
if (body.includes('app_not_installed')) {
throw new AbortError(outputContent `App is not installed on ${outputToken.green(storeFqdn)}. Try running ${outputToken.genericShellCommand(`shopify app dev`)} to connect your app to the shop.`);
}
throw new AbortError(`Failed to get access token for app ${clientId} on store ${storeFqdn}: ${tokenResponse.statusText}`);
}
const tokenJson = (await tokenResponse.json());
return { token: tokenJson.access_token, storeFqdn };
}
//# sourceMappingURL=session.js.map