Skip to content

Never use saved logins in store info while the service account token is set - #8899

Merged
egaodd merged 2 commits into
mainfrom
eddie/service-account-token-2-store-info
Oct 15, 2026
Merged

egaodd merged 2 commits into
mainfrom
eddie/service-account-token-2-store-info

Conversation

@egaodd

@egaodd egaodd commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Part of shop/issues-develop#24007 · Builds on #8898

TL;DR: While SHOPIFY_SERVICE_ACCOUNT_TOKEN is set, shopify store info (and shopify store open, which builds on it) never reads a login saved on this machine. It returns what the Business Platform finds for the service account, or the Business Platform's error. Nothing changes when the token isn't set.

  1. Rename SHOPIFY_ORGANIZATION_AUTOMATION_TOKEN to SHOPIFY_SERVICE_ACCOUNT_TOKEN #8898: rename to SHOPIFY_SERVICE_ACCOUNT_TOKEN
  2. store info / store open never use a saved login while the token is set ← this one
  3. Report service_account_token as the auth method for the service account token #8900: telemetry reports service_account_token as the auth method
  4. Deprecation warning for SHOPIFY_APP_AUTOMATION_TOKEN and SHOPIFY_CLI_PARTNERS_TOKEN

WHY are these changes introduced?

store info checks for a login saved on this machine for the store before asking the Business Platform:

  • A saved preview store session answers on its own. The Business Platform is never asked.
  • A saved shopify store auth login is the fallback when the Business Platform can't find the store, or would need to prompt.

With the service account token set, either path makes the command answer as whoever saved that login rather than as the service account. An agent or CI job could then see a store the token can't reach, and never get the error that says so. The tech doc's noninteractive command contract lists this as "no cached Admin/preview fallback" for store info and store open.

The other store commands that read saved logins (store execute, store bulk, store graphiql) already refuse while the token is set (#8731). store auth deliberately ignores the token. store info was the last one that could still use a saved login.

WHAT is this pull request doing?

  • store info: when the token is set, getStoreInfo treats the store as having no saved login. That one line skips the preview path, sends the Business Platform lookup with noPrompt: false (the token never prompts), and turns off the fallback. So a Business Platform miss surfaces as the Business Platform's error.
  • store open: no code change. It gets the store from getStoreInfo.
  • cli-kit: adds isServiceAccountTokenSet() next to ensureNoServiceAccountToken(), which now uses it. It's named like cli-kit's other boolean helpers (isCI, hasGit), because a public export can't be renamed after release without a breaking change. It's true whenever the variable is set, even when it's empty or conflicts with another variable. In that case the command still skips saved logins, and authentication reports the problem.
  • No changeset: the variable is new in this release and Rename SHOPIFY_ORGANIZATION_AUTOMATION_TOKEN to SHOPIFY_SERVICE_ACCOUNT_TOKEN #8898's changeset already announces it. Nothing changes for anyone who doesn't set it.

How to manually test your changes?

With a saved login for a store (shopify store auth --store <store> --scopes read_products), and any value in the token:

SHOPIFY_SERVICE_ACCOUNT_TOKEN=x shopify store info --store <store>
SHOPIFY_SERVICE_ACCOUNT_TOKEN=x shopify store open --store <store>

Both fail at the token exchange ("The custom token provided can't be used for the Business Platform API"). They don't print the store's details or open it from the saved login. Without the variable, both behave as on main.

Checklist

  • I've considered possible cross-platform impacts (Mac, Linux, Windows)
  • I've considered possible documentation changes
  • I've considered analytics changes to measure impact
  • The change is user-facing — I've identified the correct bump type (patch for bug fixes · minor for new features · major for breaking changes) and added a changeset with pnpm changeset add

…is set

`shopify store info` reads the login saved on this machine for the store
before asking the Business Platform. A saved preview store session answers
on its own, and a saved `shopify store auth` login is the fallback when the
Business Platform can't find the store. With SHOPIFY_SERVICE_ACCOUNT_TOKEN
set, either one makes the command answer as whoever saved that login
instead of as the service account.

While the token is set, store info now skips the saved login entirely: it
returns what the Business Platform finds, or the Business Platform's
error. `shopify store open` gets its store from store info, so it follows.

Adds `usingServiceAccountToken()` to cli-kit's session module, next to
`ensureNoServiceAccountToken()`, which now uses it.

Co-authored-by: Eddie Gao <eddie.gao@shopify.com>
@egaodd
egaodd requested a review from a team as a code owner October 14, 2026 19:38
@github-actions github-actions Bot added the no-changelog This PR doesn't include a changeset entry. Is an internal only change not relevant to end users. label Oct 14, 2026
@egaodd egaodd self-assigned this Oct 14, 2026
cli-kit's boolean helpers are named is* or has* (isCI, isTerminalInteractive,
hasGit). The new helper is public API, and renaming a public export after a
release counts as a breaking change, so name it to match before it ships.

Co-authored-by: Eddie Gao <eddie.gao@shopify.com>
@github-actions

Copy link
Copy Markdown
Contributor

Differences in type declarations

We detected differences in the type declarations generated by Typescript for this branch compared to the baseline ('main' branch). Please, review them to ensure they are backward-compatible. Here are some important things to keep in mind:

  • Some seemingly private modules might be re-exported through public modules.
  • If the branch is behind main you might see odd diffs, rebase main into this branch.

New type declarations

We found no new type declarations in this PR

Existing type declarations

packages/cli-kit/dist/private/node/constants.d.ts
@@ -8,7 +8,7 @@ export declare const environmentVariables: {
     env: string;
     noAnalytics: string;
     optOutInstrumentation: string;
-    organizationAutomationToken: string;
+    serviceAccountToken: string;
     appAutomationToken: string;
     partnersToken: string;
     runAsUser: string;
packages/cli-kit/dist/public/node/environment.d.ts
@@ -11,9 +11,9 @@
 export declare function getEnvironmentVariables(): NodeJS.ProcessEnv;
 /**
  * Returns the automation token the CLI authenticates with, from the first of these variables that is set:
- * SHOPIFY_ORGANIZATION_AUTOMATION_TOKEN, SHOPIFY_APP_AUTOMATION_TOKEN, or the deprecated SHOPIFY_CLI_PARTNERS_TOKEN.
+ * SHOPIFY_SERVICE_ACCOUNT_TOKEN, SHOPIFY_APP_AUTOMATION_TOKEN, or the deprecated SHOPIFY_CLI_PARTNERS_TOKEN.
  *
- * Returns undefined when the variables can't be used (an empty value, or the organization variable set alongside
+ * Returns undefined when the variables can't be used (an empty value, or the service account variable set alongside
  * another one). Callers then fall back to the login flow, which reports the problem instead of logging in.
  *
  * @returns The automation token, or undefined if there is no usable one.
packages/cli-kit/dist/public/node/session.d.ts
@@ -148,15 +148,24 @@ export interface EnsureAuthenticatedBusinessPlatformOptions extends EnsureAuthen
  */
 export declare function ensureAuthenticatedBusinessPlatform(scopes?: BusinessPlatformScope[], options?: EnsureAuthenticatedBusinessPlatformOptions): Promise<string>;
 /**
- * Fails when SHOPIFY_ORGANIZATION_AUTOMATION_TOKEN is set, for commands that can't run with that token.
+ * Whether SHOPIFY_SERVICE_ACCOUNT_TOKEN is set, which makes it the automation token the CLI authenticates with.
  *
- * Organization automation tokens can't log in as a user or call a store's Admin API, so commands that need either
+ * True even when it's empty or set together with another automation token variable. Commands use this to skip logins
+ * saved on this machine, and authentication then reports any problem with the variables.
+ *
+ * @returns True when SHOPIFY_SERVICE_ACCOUNT_TOKEN is set.
+ */
+export declare function isServiceAccountTokenSet(): boolean;
+/**
+ * Fails when SHOPIFY_SERVICE_ACCOUNT_TOKEN is set, for commands that can't run with that token.
+ *
+ * Service account tokens can't log in as a user or call a store's Admin API, so commands that need either
  * refuse to run instead of quietly using the person's own login. `ensureAuthenticated` runs this check before any
  * login. Commands that run on a login saved by `shopify store auth` call it before loading that login.
  *
- * @throws AbortError when SHOPIFY_ORGANIZATION_AUTOMATION_TOKEN is set.
+ * @throws AbortError when SHOPIFY_SERVICE_ACCOUNT_TOKEN is set.
  */
-export declare function ensureNoOrganizationAutomationToken(): void;
+export declare function ensureNoServiceAccountToken(): void;
 /**
  * Logout from Shopify.
  *
packages/cli-kit/dist/private/node/session/automation-token.d.ts
@@ -4,7 +4,7 @@ interface AutomationTokenVariablesProblem {
 }
 /**
  * Returns the name of the automation token variable the CLI authenticates with: the first one that is set, in
- * the order SHOPIFY_ORGANIZATION_AUTOMATION_TOKEN, SHOPIFY_APP_AUTOMATION_TOKEN, SHOPIFY_CLI_PARTNERS_TOKEN.
+ * the order SHOPIFY_SERVICE_ACCOUNT_TOKEN, SHOPIFY_APP_AUTOMATION_TOKEN, SHOPIFY_CLI_PARTNERS_TOKEN.
  *
  * A variable set to an empty string still counts as set, so `automationTokenVariablesProblem` can report it.
  *
@@ -16,7 +16,7 @@ export declare function automationTokenVariable(env?: NodeJS.ProcessEnv): string
  * Explains why the automation token variables can't be used, so a misconfigured environment fails instead of
  * falling back to the logged-in user.
  *
- * - SHOPIFY_ORGANIZATION_AUTOMATION_TOKEN can't be set together with SHOPIFY_APP_AUTOMATION_TOKEN or
+ * - SHOPIFY_SERVICE_ACCOUNT_TOKEN can't be set together with SHOPIFY_APP_AUTOMATION_TOKEN or
  *   SHOPIFY_CLI_PARTNERS_TOKEN.
  * - The selected variable can't be empty.
  *

const storedSession = getCurrentStoredStoreAppSession(store)
// A service account token never uses logins saved on this machine: no preview store session, and no fallback to a
// `shopify store auth` login when the Business Platform can't find the store.
const storedSession = isServiceAccountTokenSet() ? undefined : getCurrentStoredStoreAppSession(store)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this apply to other store commands/flows? or only here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only here; store info was the last command that both accepts the token and can read a saved login

Base automatically changed from eddie/service-account-token-1-rename to main October 15, 2026 14:43
@egaodd
egaodd added this pull request to the merge queue Oct 15, 2026
Merged via the queue into main with commit 019c059 Oct 15, 2026
26 of 28 checks passed
@egaodd
egaodd deleted the eddie/service-account-token-2-store-info branch October 15, 2026 16:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-changelog This PR doesn't include a changeset entry. Is an internal only change not relevant to end users.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants