Implement `ensureAuthenticatedAdminAsApp` for Bulk Operations
Resolves: https://github.com/orgs/shop/projects/208/views/34?pane=issue&itemId=140363951&issue=shop%7Cissues-api-foundations%7C1128 Inspired by: https://github.com/Shopify/cli/pull/6596#discussion_r2543018389
Background
The bulk operations execute command currently authenticates using the CLI user's credentials, which has two critical limitations:
- No access to $app metafields - Cannot query or mutate app-specific metafields
- Ignores app scopes - Does not respect the app's configured access scopes.
This prevents bulk operations from working properly with app-owned data.
Solution
Implemented ensureAuthenticatedAdminAsApp() that authenticates as the app using OAuth 2.0 client credentials grant. This provides:
- Full access to $app metafields
- Respects the app's configured scopes
- Proper app-context authentication
This idea originally came from a hack days.
Implementation Notes
- The API secret extraction pattern follows the existing approach in
webhook/trigger-options.ts. - Currently both
appandremoteAppare passed to executeBulkOperation. Theappparameter is only used for:- Display name (app.name)
- Client ID (app.configuration.client_id)
Both values are also available on remoteApp as title and apiKey. Could be simplified in the future to only use remoteApp for consistency.
Testing
To test with this new authentification, you must install the app on the store. You can do this by:
cd <PATH_TO_TEST_APP>
pnpm shopify app dev
Then, we can use the existing command works unchanged:
For queries:
pnpm shopify:run app execute \
--path <PATH_TO_TEST_APPP> \
-q '{ products(first: 10) { edges { node { id title } } } }'
For mutations:
pnpm shopify:run app execute \
--path <PATH_TO_TEST_APPP> \
-q 'mutation productUpdate($input: ProductInput!) { productUpdate(input: $input) { product { id tags } userErrors { field message } } }' \
-v '[{"input": {"id": "gid://shopify/Product/9721830834416", "tags": ["bulk-test"]}}]'
Now authenticates as the app instead of the CLI user, enabling access to app-scoped resources.
- #6643
: 2 dependent PRs (#6663
, #6667
) π (View in Graphite) - #6636

- #6632

- #6622

- #6596

- #6588
: 1 other dependent PR (#6595
) main
This stack of pull requests is managed by Graphite. Learn more about stacking.
Coverage report
St.:grey_question: |
Category | Percentage | Covered / Total |
|---|---|---|---|
| π‘ | Statements | 79.27% (+0.04% πΌ) |
13725/17314 |
| π‘ | Branches | 73.17% (+0.06% πΌ) |
6699/9155 |
| π‘ | Functions | 79.42% (+0.04% πΌ) |
3530/4445 |
| π‘ | Lines | 79.64% (+0.06% πΌ) |
12970/16286 |
Show new covered files π£
St.:grey_question: |
File | Statements | Branches | Functions | Lines |
|---|---|---|---|---|---|
| π’ | ... / admin-as-app.ts |
100% | 100% | 100% | 100% |
| π’ | ... / bulk-operation-run-mutation.ts |
100% | 100% | 100% | 100% |
| π’ | ... / bulk-operation-run-query.ts |
100% | 100% | 100% | 100% |
| π’ | ... / get-bulk-operation-by-id.ts |
100% | 100% | 100% | 100% |
| π’ | ... / staged-uploads-create.ts |
100% | 100% | 100% | 100% |
| π’ | ... / download-bulk-operation-results.ts |
100% | 100% | 100% | 100% |
| π’ | ... / execute-bulk-operation.ts |
90.91% | 80.56% | 100% | 92.31% |
| π’ | ... / format-bulk-operation-status.ts |
100% | 100% | 100% | 100% |
| π’ | ... / run-mutation.ts |
100% | 100% | 100% | 100% |
| π’ | ... / run-query.ts |
100% | 100% | 100% | 100% |
| π‘ | ... / stage-file.ts |
72.73% | 62.5% | 83.33% | 71.88% |
| π’ | ... / watch-bulk-operation.ts |
100% | 100% | 100% | 100% |
Show files with reduced coverage π»
St.:grey_question: |
File | Statements | Branches | Functions | Lines |
|---|---|---|---|---|---|
| π‘ | ... / specification.ts |
68.52% (-0.57% π») |
75.61% (+2.44% πΌ) |
76.47% (-1.31% π») |
68.09% (-0.66% π») |
| π’ | ... / developer-platform-client.ts |
84.62% (-1.5% π») |
73.68% (+3.1% πΌ) |
81.82% (+1.82% πΌ) |
90.63% (-2.71% π») |
| π’ | ... / api.ts |
87.07% (-0.43% π») |
76.71% (-0.1% π») |
100% | 86.49% (-0.43% π») |
| π’ | ... / ConcurrentOutput.tsx |
98.36% (-1.64% π») |
92% (-4% π») |
100% | 98.33% (-1.67% π») |
| π΄ | ... / ui.tsx |
50.82% (-0.79% π») |
42.86% (-5.53% π») |
54.55% (+1.42% πΌ) |
50% (-0.82% π») |
| π’ | ... / console.ts |
81.82% (+15.15% πΌ) |
75% (-25% π») |
100% (+33.33% πΌ) |
81.82% (+15.15% πΌ) |
| π΄ | ... / dev.ts |
12.77% (-0.57% π») |
2.78% (-0.16% π») |
57.14% | 12.77% (-0.57% π») |
| π‘ | ... / theme-environment.ts |
69.57% (-1.86% π») |
50% | 55.56% (-3.27% π») |
69.57% (-1.86% π») |
Test suite run success
3406 tests passing in 1386 suites.
Report generated by π§ͺjest coverage report action from 02b54eddab964b222f3b837f4015f597f63370e6
We detected some changes at packages/*/src and there are no updates in the .changeset.
If the changes are user-facing, run pnpm changeset add to track your changes and include them in the next release CHANGELOG.
[!CAUTION] DO NOT create changesets for features which you do not wish to be included in the public changelog of the next CLI release.
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
mainyou might see odd diffs, rebasemaininto this branch.
New type declarations
We found no new type declarations in this PR
Existing type declarations
packages/cli-kit/dist/public/node/session.d.ts
@@ -97,6 +97,17 @@ export declare function ensureAuthenticatedStorefront(scopes?: StorefrontRendere
* @returns The access token for the Admin API.
*/
export declare function ensureAuthenticatedAdmin(store: string, scopes?: AdminAPIScope[], options?: EnsureAuthenticatedAdditionalOptions): Promise<AdminSession>;
+/**
+ * Ensure that we have a valid Admin session for the given store, acting on behalf of the app.
+ *
+ * This will fail if the app has not already been installed.
+ *
+ * @param storeFqdn - Store fqdn to request auth for.
+ * @param apiKey - API key for the app.
+ * @param apiSecret - API secret for the app.
+ * @returns The access token for the Admin API.
+ */
+export declare function ensureAuthenticatedAdminAsApp(storeFqdn: string, apiKey: string, apiSecret: string): Promise<AdminSession>;
/**
* 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.
Merge activity
- Nov 26, 7:37 PM UTC: This pull request can not be added to the Graphite merge queue. Please try rebasing and resubmitting to merge when ready.
- Nov 26, 7:37 PM UTC: Graphite disabled "merge when ready" on this PR due to: a merge conflict with the target branch; resolve the conflict and try again..
Yeah, sorry for the outdated description -- I'll update at least the title so it'll be clearer to future readers what this PR is actually doing.
Thanks for the quick review!