Use crawler signatures#7907
Conversation
0187042 to
d4b78c1
Compare
Crawler signature values are request secrets, so verbose request logging should treat Signature, Signature-Input, and Signature-Agent like other sensitive headers before the dev server starts sending them.
f8ff3b4 to
15f866b
Compare
|
/snapit |
|
🫰✨ Thanks @graygilmore! Your snapshot has been published to npm. Test the snapshot by installing your package globally: pnpm i -g --@shopify:registry=https://registry.npmjs.org @shopify/cli@0.0.0-snapshot-20260702204507Caution After installing, validate the version by running |
18087d6 to
1187c62
Compare
Some internal Admin GraphQL fields are only available on unstable. Let raw Admin requests opt into a specific version while preserving the existing latest-supported-version behavior by default.
Add a theme helper that reuses an active CLI-managed crawler signature for the storefront domain or creates one through the internal Admin mutation when needed. The helper returns only the request headers the dev server needs.
Thread CLI crawler signature headers through the theme dev session so render requests, proxied storefront requests, session-cookie requests, and password checks all reach Storefront Renderer as verified Shopify crawler traffic.
Use the same Admin-backed crawler signature helper in app dev theme extension previews and pass the resulting headers into the shared theme dev server session. Assisted-By: devx/1fdba809-4102-4cb7-a3de-1ab7dd624a29
1187c62 to
92d78d2
Compare
There was a problem hiding this comment.
We need to define these types ourselves because these fields are hidden and only available to our client so they don't get automatically generated like our other GraphQL queries/mutations do.
| const storefrontPasswordPromise = await isStorefrontPasswordProtected(options.adminSession).then((needsPassword) => | ||
| needsPassword ? ensureValidPassword(options.storePassword, options.adminSession.storeFqdn) : undefined, | ||
| ) | ||
| const [crawlerSignatureHeaders, isPasswordProtected] = await Promise.all([ |
There was a problem hiding this comment.
Run these both at the same time since one doesn't block the other.
| headers = cleanHeader({ | ||
| ...headers, | ||
| ...defaultHeaders(), | ||
| ...(host === ctx.session.storeFqdn ? ctx.session.crawlerSignatureHeaders : {}), |
There was a problem hiding this comment.
This proxy handles a few kinds of requests so we only want to add the headers when the hosts match.
Differences in type declarationsWe 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:
New type declarationsWe found no new type declarations in this PR Existing type declarationspackages/cli-kit/dist/public/node/api/admin.d.ts@@ -9,9 +9,10 @@ import { TypedDocumentNode } from '@graphql-typed-document-node/core';
* @param query - GraphQL query to execute.
* @param session - Shopify admin session including token and Store FQDN.
* @param variables - GraphQL variables to pass to the query.
+ * @param version - API version.
* @returns The response of the query of generic type <T>.
*/
-export declare function adminRequest<T>(query: string, session: AdminSession, variables?: GraphQLVariables): Promise<T>;
+export declare function adminRequest<T>(query: string, session: AdminSession, variables?: GraphQLVariables, version?: string): Promise<T>;
export interface AdminRequestOptions<TResult, TVariables extends Variables> {
/** GraphQL query to execute. */
query: TypedDocumentNode<TResult, TVariables>;
|
EvilGenius13
left a comment
There was a problem hiding this comment.
Ran the latest snap build confirmed everything is still working and can find the necessary info in our logs. 🚀 Thanks for the PR @graygilmore
Summary
Adds support for Shopify-managed storefront request headers during local theme development in an effort to eliminate legitimate CLI users being flagged as a bot.
theme devstartup when availableNote
Does not work in combination with the
--passwordflag.