Skip to content

Report service_account_token as the auth method for the service account token - #8900

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

egaodd merged 2 commits into
mainfrom
eddie/service-account-token-3-telemetry

Conversation

@egaodd

@egaodd egaodd commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

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

TL;DR: Every CLI analytics event records how the command signed in, in env_auth_method. Today every automation token is recorded as partners_token, so runs that use SHOPIFY_SERVICE_ACCOUNT_TOKEN can't be told apart from runs that use the app or Partner variables. This PR records service_account_token for them. The app and Partner variables keep partners_token. Analytics only; no command behaves differently.

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

WHY are these changes introduced?

  • To measure the move to the new variable. Since 4.9 (2253a5a), env_shopify_variables only carries the agent variables, not the token variable names, so env_auth_method is the only field that can show which variable a command used. That's what tells us when people have moved off the deprecated variables (Warn that SHOPIFY_APP_AUTOMATION_TOKEN and SHOPIFY_CLI_PARTNERS_TOKEN are deprecated #8901).
  • It reports the variable, not the kind of token. The CLI can't tell token kinds apart, and any token works in any of the variables, so service_account_token means "the token came from SHOPIFY_SERVICE_ACCOUNT_TOKEN". The doc comment on automationTokenAuthMethod() says so.
  • Existing metrics don't move. App and Partner tokens keep reporting partners_token. DevTools' CLI active-user queries (World devtools-weekly-metrics, and the "Daily Active CLI Users" query in data-notebooks) only count user IDs that belong to a real Shopify account. A token run's user ID is a hash of the token, which never matches one, whatever env_auth_method says.

WHAT is this pull request doing?

  • Adds automationTokenAuthMethod() to automation-token.ts, next to the variable selection. It returns service_account_token when the token comes from the service account variable, and partners_token otherwise.
  • Uses it in the two places that record an automation token:
    • after a token exchange (exchange.ts), which every successful token command goes through;
    • in getLastSeenAuthMethod's environment fallback, used when no exchange ran, for example when a command refuses the token.
  • Adds 'service_account_token' to AuthMethod.
  • Leaves ensureAuthenticated's partners_token alone: it refuses before that line whenever the service account variable is set.
  • No changeset: analytics only.

How to manually test your changes?

With analytics off, --verbose prints the payload the CLI would send:

SHOPIFY_CLI_NO_ANALYTICS=1 SHOPIFY_SERVICE_ACCOUNT_TOKEN=x shopify store list --verbose 2>&1 | grep env_auth_method
SHOPIFY_CLI_NO_ANALYTICS=1 SHOPIFY_APP_AUTOMATION_TOKEN=x shopify store list --verbose 2>&1 | grep env_auth_method

The first shows "env_auth_method": "service_account_token", the second "env_auth_method": "partners_token".

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

…nt token

CLI analytics report `env_auth_method: partners_token` for every automation
token, so commands run with SHOPIFY_SERVICE_ACCOUNT_TOKEN can't be told
apart from app and Partner tokens. Since 4.9 the token variable names are
no longer sent in `env_shopify_variables`, so this field is the only place
the token type can show up.

While SHOPIFY_SERVICE_ACCOUNT_TOKEN is the selected variable, report
`service_account_token`, both after a token exchange and in the fallback
that reads the environment. SHOPIFY_APP_AUTOMATION_TOKEN and
SHOPIFY_CLI_PARTNERS_TOKEN keep reporting `partners_token`, so existing
dashboards don't change.

The value comes from `automationTokenAuthMethod()`, next to the variable
selection in automation-token.ts.

Co-authored-by: Eddie Gao <eddie.gao@shopify.com>
Comment thread packages/cli-kit/src/private/node/session/automation-token.ts
The CLI can't tell token kinds apart, and any token works in any of the
automation variables, so `service_account_token` means the token came from
SHOPIFY_SERVICE_ACCOUNT_TOKEN. Say so in automationTokenAuthMethod's doc
comment.

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/private/node/session.d.ts
@@ -61,7 +61,7 @@ export interface OAuthSession {
     appManagement?: string;
     userId: string;
 }
-type AuthMethod = 'partners_token' | 'device_auth' | 'theme_access_token' | 'custom_app_token' | 'none';
+type AuthMethod = 'partners_token' | 'service_account_token' | 'device_auth' | 'theme_access_token' | 'custom_app_token' | 'none';
 /**
  * Retrieves a stable user identifier for analytics, or `'unknown'` if none applies.
  *
@@ -82,7 +82,8 @@ export declare function setLastSeenUserIdAfterAuth(id: string): void;
  * This function checks for the authentication method in the following order:
  * 1. Returns the cached auth method if it's not 'none'.
  * 2. Checks for a cached session, which implies 'device_auth' was used.
- * 3. Checks for a partners token in the environment.
+ * 3. Checks for an automation token in the environment: `service_account_token` for SHOPIFY_SERVICE_ACCOUNT_TOKEN,
+ *    `partners_token` for the app and Partner variables.
  * 4. Checks for a theme password in the environment.
  * 5. If none of the above are true, returns 'none'.
  *
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/notifications-system.d.ts
@@ -26,7 +26,7 @@ declare const NotificationSchema: zod.ZodObject<{
     id: string;
     type: "info" | "error" | "warning";
     message: string;
-    frequency: "once" | "always" | "once_a_day" | "once_a_week";
+    frequency: "always" | "once" | "once_a_day" | "once_a_week";
     ownerChannel: string;
     title?: string | undefined;
     cta?: {
@@ -43,7 +43,7 @@ declare const NotificationSchema: zod.ZodObject<{
     id: string;
     type: "info" | "error" | "warning";
     message: string;
-    frequency: "once" | "always" | "once_a_day" | "once_a_week";
+    frequency: "always" | "once" | "once_a_day" | "once_a_week";
     ownerChannel: string;
     title?: string | undefined;
     cta?: {
@@ -86,7 +86,7 @@ declare const NotificationsSchema: zod.ZodObject<{
         id: string;
         type: "info" | "error" | "warning";
         message: string;
-        frequency: "once" | "always" | "once_a_day" | "once_a_week";
+        frequency: "always" | "once" | "once_a_day" | "once_a_week";
         ownerChannel: string;
         title?: string | undefined;
         cta?: {
@@ -103,7 +103,7 @@ declare const NotificationsSchema: zod.ZodObject<{
         id: string;
         type: "info" | "error" | "warning";
         message: string;
-        frequency: "once" | "always" | "once_a_day" | "once_a_week";
+        frequency: "always" | "once" | "once_a_day" | "once_a_week";
         ownerChannel: string;
         title?: string | undefined;
         cta?: {
@@ -122,7 +122,7 @@ declare const NotificationsSchema: zod.ZodObject<{
         id: string;
         type: "info" | "error" | "warning";
         message: string;
-        frequency: "once" | "always" | "once_a_day" | "once_a_week";
+        frequency: "always" | "once" | "once_a_day" | "once_a_week";
         ownerChannel: string;
         title?: string | undefined;
         cta?: {
@@ -141,7 +141,7 @@ declare const NotificationsSchema: zod.ZodObject<{
         id: string;
         type: "info" | "error" | "warning";
         message: string;
-        frequency: "once" | "always" | "once_a_day" | "once_a_week";
+        frequency: "always" | "once" | "once_a_day" | "once_a_week";
         ownerChannel: string;
         title?: string | undefined;
         cta?: {
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.
  *
@@ -12,11 +12,23 @@ interface AutomationTokenVariablesProblem {
  * @returns The variable name, or undefined when none of them is set.
  */
 export declare function automationTokenVariable(env?: NodeJS.ProcessEnv): string | undefined;
+/**
+ * Returns the `env_auth_method` analytics value for the automation token variable the CLI authenticates with:
+ * `service_account_token` for SHOPIFY_SERVICE_ACCOUNT_TOKEN, and `partners_token` for SHOPIFY_APP_AUTOMATION_TOKEN and
+ * SHOPIFY_CLI_PARTNERS_TOKEN, which existing dashboards already expect.
+ *
+ * The value tells which variable held the token, not which kind of token it is: the CLI can't tell tokens apart, and
+ * any of them works in any of the variables. It shows how much usage has moved to SHOPIFY_SERVICE_ACCOUNT_TOKEN.
+ *
+ * @param env - Environment variables to read.
+ * @returns The auth method to report.
+ */
+export declare function automationTokenAuthMethod(env?: NodeJS.ProcessEnv): 'service_account_token' | 'partners_token';
 /**
  * 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.
  *

Base automatically changed from eddie/service-account-token-2-store-info to main October 15, 2026 16:07
@egaodd
egaodd added this pull request to the merge queue Oct 15, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Oct 15, 2026
@egaodd
egaodd added this pull request to the merge queue Oct 15, 2026
Merged via the queue into main with commit c7d5c37 Oct 15, 2026
26 of 28 checks passed
@egaodd
egaodd deleted the eddie/service-account-token-3-telemetry branch October 15, 2026 16:59
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.

5 participants