UNPKG

@shopify/cli-kit

Version:

A set of utilities, interfaces, and models that are common across all the platform features

224 lines • 10.1 kB
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