Skip to content

Read automation tokens through one selector - #8728

Closed
egaodd wants to merge 1 commit into
mainfrom
eddie/org-token-1-selector
Closed

egaodd wants to merge 1 commit into
mainfrom
eddie/org-token-1-selector

Conversation

@egaodd

@egaodd egaodd commented Oct 1, 2026 •

Copy link
Copy Markdown

Part of shop/issues-develop#24004

TL;DR: Moves every read of the automation token variables behind one selector, so the next two PRs change token selection in one place. No behavior change.

  1. Read automation tokens through one selector #8728: one token selector ← this one
  2. Authenticate store and organization commands with an automation token #8729: store and organization commands authenticate with an exported automation token
  3. Add SHOPIFY_ORGANIZATION_AUTOMATION_TOKEN #8731: add SHOPIFY_ORGANIZATION_AUTOMATION_TOKEN and the selection rules (draft until the name is confirmed)

WHY are these changes introduced?

SHOPIFY_APP_AUTOMATION_TOKEN and SHOPIFY_CLI_PARTNERS_TOKEN are read in five places across public/node/session.ts and private/node/session.ts. #8731 adds a third variable plus conflict and empty-value rules. Without a single selector, each rule would have to be copied into all five places.

WHAT is this pull request doing?

  • getAutomationToken (new, in private/node/session/automation-token.ts) is what authentication calls. It returns {value, source}, and Add SHOPIFY_ORGANIZATION_AUTOMATION_TOKEN #8731 adds validation to it.
  • peekAutomationToken is what analytics calls (getLastSeenUserIdAfterAuth, getLastSeenAuthMethod). It never validates, so once Add SHOPIFY_ORGANIZATION_AUTOMATION_TOKEN #8731 adds errors, a misconfigured environment still can't drop a command's analytics event.
  • Precedence is unchanged. The app variable wins whenever it's set, even to an empty value, and an empty value means no token.
  • getAppAutomationToken() is untouched. It's a public cli-kit export, so external callers see no change.

One difference, analytics only: when SHOPIFY_APP_AUTOMATION_TOKEN is set but empty and a theme token is present, the analytics user ID now comes from the theme token instead of being skipped.

How to manually test your changes?

With an App Automation Token and no cached login (shopify auth logout), both of these behave exactly as on main:

SHOPIFY_APP_AUTOMATION_TOKEN=<token> shopify app versions list --client-id <client-id>
SHOPIFY_CLI_PARTNERS_TOKEN=<token> shopify app versions list --client-id <client-id>

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

@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 1, 2026
Every place that reads SHOPIFY_APP_AUTOMATION_TOKEN or SHOPIFY_CLI_PARTNERS_TOKEN
now goes through a single module, so later changes to token selection land in
one place. Authentication uses getAutomationToken; analytics uses
peekAutomationToken, which will stay free of validation so reporting never
fails a command.

Precedence is unchanged: the app variable wins whenever it is set, and an empty
value means no token. getAppAutomationToken keeps its current behavior for
external callers.

Co-authored-by: Eddie Gao <eddie.gao@shopify.com>
@egaodd
egaodd force-pushed the eddie/org-token-1-selector branch from 47cd7a5 to 4e2f86e Compare October 1, 2026 19:29
@github-actions

github-actions Bot commented Oct 1, 2026

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

packages/cli-kit/dist/private/node/session/automation-token.d.ts
/**
 * The environment variable an automation token was read from.
 */
type AutomationTokenSource = 'app' | 'partners';
interface AutomationToken {
    value: string;
    source: AutomationTokenSource;
}
/**
 * Reads the automation token from the environment without validating it, so it never throws.
 *
 * Telemetry reads the token through this function because reporting must not fail a command.
 * Authentication reads it through `getAutomationToken`.
 *
 * SHOPIFY_APP_AUTOMATION_TOKEN takes precedence over the deprecated SHOPIFY_CLI_PARTNERS_TOKEN whenever it is
 * set, even to an empty value. An empty value means no token.
 *
 * @param env - Environment variables to read the token from.
 * @returns The automation token and the variable it came from, or undefined when there is none.
 */
export declare function peekAutomationToken(env?: NodeJS.ProcessEnv): AutomationToken | undefined;
/**
 * Selects the automation token to authenticate with.
 *
 * @param env - Environment variables to read the token from.
 * @returns The automation token and the variable it came from, or undefined when there is none.
 */
export declare function getAutomationToken(env?: NodeJS.ProcessEnv): AutomationToken | undefined;
export {};

Existing type declarations

We found no diffs with existing type declarations

@egaodd
egaodd marked this pull request as ready for review October 1, 2026 22:41
@egaodd
egaodd requested a review from a team as a code owner October 1, 2026 22:41
*/
export function getAutomationToken(env: NodeJS.ProcessEnv = process.env): AutomationToken | undefined {
return peekAutomationToken(env)
}

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.

I don't understand, why the two functions? this one doesn't do anything differently 🤔
Why in some places you use one or the other?

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.

Its scaffolding for the next PRs... getting ahead of itself slightly though lol

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

True- we only need this split in #8731 once we make one of them throw. Most likely closing this as it isn't required here at all

Comment on lines +25 to +31
export function peekAutomationToken(env: NodeJS.ProcessEnv = process.env): AutomationToken | undefined {
const appToken = env[environmentVariables.appAutomationToken]
if (appToken !== undefined) return appToken === '' ? undefined : {value: appToken, source: 'app'}

const partnersToken = env[environmentVariables.partnersToken]
return partnersToken ? {value: partnersToken, source: 'partners'} : undefined
}

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.

What is the benefit in this interface design? Do we ever actually care about the source returned? I don't think so. I think if anything you should be able to specify which exact token you want back (since some flows require certain vars, mainly partners) and then app tokens can be aliased/rolled out together, where automation env var takes precedence, but the old env var will always be able to be used too. It would make ripping the partner token code out easier once we're able to as well.

@egaodd egaodd closed this Oct 2, 2026
@egaodd
egaodd deleted the eddie/org-token-1-selector branch October 2, 2026 16:57
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