Skip to content

fix: require authentication key for GraphiQL dev server#7033

Open
isaacroldan wants to merge 1 commit intomainfrom
03-17-fix_require_authentication_key_for_graphiql_dev_server
Open

fix: require authentication key for GraphiQL dev server#7033
isaacroldan wants to merge 1 commit intomainfrom
03-17-fix_require_authentication_key_for_graphiql_dev_server

Conversation

@isaacroldan
Copy link
Contributor

@isaacroldan isaacroldan commented Mar 17, 2026

WHY are these changes introduced?

GraphiQL authentication keys were previously optional, requiring manual specification for security. This created a poor developer experience where keys had to be manually managed and could be lost across dev server restarts.

WHAT is this pull request doing?

  • Automatically derives GraphiQL authentication keys from the app secret and store FQDN when no explicit key is provided
  • Updates the --graphiql-key flag description to reflect that keys are now automatically generated by default
  • Adds authentication requirement to the /graphiql/status endpoint
  • Ensures GraphiQL URLs always include the authentication key parameter
  • Adds comprehensive tests for the key derivation function to verify deterministic behavior and uniqueness across different inputs

The derived keys are deterministic (same across dev server restarts) but not guessable without the app secret, providing both security and a seamless developer experience.

How to test your changes?

  1. Start a dev server without specifying --graphiql-key
  2. Verify that GraphiQL loads successfully with an auto-generated key
  3. Restart the dev server and confirm the same key is used (browser tabs should remain functional)
  4. Test with different stores/apps to ensure unique keys are generated
  5. Verify that providing an explicit --graphiql-key still works as expected

Measuring impact

How do we know this change was effective? Please choose one:

  • n/a - this doesn't need measurement, e.g. a linting rule or a bug-fix

Checklist

  • I've considered possible cross-platform impacts (Mac, Linux, Windows)
  • I've considered possible documentation changes

Copy link
Contributor Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@isaacroldan isaacroldan force-pushed the 03-17-fix_require_authentication_key_for_graphiql_dev_server branch from d754058 to 4890e97 Compare March 17, 2026 17:30
@github-actions
Copy link
Contributor

github-actions bot commented Mar 17, 2026

Coverage report

St.
Category Percentage Covered / Total
🟢 Statements 82.78% 14601/17638
🟡 Branches 75.15% 7239/9633
🟢 Functions 82.13% 3710/4517
🟢 Lines 83.23% 13805/16586

Test suite run success

3820 tests passing in 1476 suites.

Report generated by 🧪jest coverage report action from 04702b0

@isaacroldan isaacroldan force-pushed the 03-17-fix_require_authentication_key_for_graphiql_dev_server branch from 4890e97 to 8d8da1f Compare March 17, 2026 17:39
@isaacroldan isaacroldan marked this pull request as ready for review March 17, 2026 17:42
@isaacroldan isaacroldan requested a review from a team as a code owner March 17, 2026 17:42
Copilot AI review requested due to automatic review settings March 17, 2026 17:42
@github-actions
Copy link
Contributor

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.

@isaacroldan isaacroldan force-pushed the 03-17-fix_require_authentication_key_for_graphiql_dev_server branch from 8d8da1f to e5c235d Compare March 17, 2026 17:46
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR tightens access to the local GraphiQL dev server by ensuring an authentication key is always required (either user-provided or deterministically derived), and threads that key through the CLI/dev process and GraphiQL UI.

Changes:

  • Derive a deterministic GraphiQL auth key from apiSecret + storeFqdn when no key is provided, and always include it in the GraphiQL URL/process options.
  • Require the key for additional GraphiQL server endpoints and add basic api_version input validation.
  • Add tests covering key derivation and API version validation, and update CLI flag description.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/app/src/cli/services/dev/processes/setup-dev-processes.ts Always resolves a GraphiQL auth key (derive if absent) and uses it for the GraphiQL URL + process options.
packages/app/src/cli/services/dev/processes/setup-dev-processes.test.ts Adds coverage to ensure a key is auto-derived and included in URL/process options.
packages/app/src/cli/services/dev/graphiql/templates/graphiql.tsx Updates the in-browser status polling request to include the auth key.
packages/app/src/cli/services/dev/graphiql/server.ts Derives/forces a key on the server, enforces key checks on more routes, and validates api_version before building Admin URLs.
packages/app/src/cli/services/dev/graphiql/server.test.ts Adds unit tests for deriveGraphiQLKey and isValidApiVersion.
packages/app/src/cli/commands/app/dev.ts Updates --graphiql-key help text to reflect the new default derived-key behavior.
Comments suppressed due to low confidence (3)

packages/app/src/cli/services/dev/graphiql/server.ts:216

  • isValidApiVersion currently treats empty string as valid (if (!version) return true), but later /graphiql/graphql.json passes that value into adminUrl(storeFqdn, apiVersion). adminUrl uses version ?? 'unstable', so an empty string will produce an invalid URL segment (.../api//graphql.json). Normalize '' to undefined before calling adminUrl (or make isValidApiVersion('') return false and adjust callers/tests accordingly).
          Accept: 'application/json',
          'Content-Type': 'application/json',
          'X-Shopify-Access-Token': await token(),
          'User-Agent': `ShopifyCLIGraphiQL/${CLI_KIT_VERSION}`,
        }

        return fetch(graphqlUrl, {
          method: req.method,

packages/app/src/cli/services/dev/graphiql/server.ts:80

  • providedKey ?? deriveGraphiQLKey(...) will treat an empty string override as a real key. That effectively disables the protection (anyone can hit ?key=) and also makes it easy to accidentally misconfigure via env/flag. Consider rejecting empty-string keys (e.g., trim + check length) and falling back to the derived key, or throw a clear error if a custom key is provided but empty.
  function failIfUnmatchedKey(str: string, res: express.Response): boolean {
    if (str === key) return false
    res.status(404).send(`Invalid path ${res.req.originalUrl}`)
    return true
  }

  let _token: string | undefined
  async function token(): Promise<string> {

packages/app/src/cli/services/dev/graphiql/server.test.ts:53

  • The tests assert that isValidApiVersion('') is valid, but if an empty api_version query param slips through, adminUrl(storeFqdn, '') will generate an invalid .../api//graphql.json URL. Once the implementation is fixed (normalize '' to undefined or reject it), update this test expectation accordingly to match the real behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@isaacroldan isaacroldan force-pushed the 03-17-fix_require_authentication_key_for_graphiql_dev_server branch 2 times, most recently from 2415592 to eb44a92 Compare March 17, 2026 17:54
Copy link
Contributor

@dmerand dmerand left a comment

Choose a reason for hiding this comment

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

I'm aligned with the approach. The agents had some good security suggestions worth considering, I've left notes here.

const resolvedGraphiqlKey = resolveGraphiQLKey(graphiqlKey, apiSecret, storeFqdn)
const graphiqlURL = shouldRenderGraphiQL
? `http://localhost:${graphiqlPort}/graphiql${graphiqlKey ? `?key=${graphiqlKey}` : ''}`
? `http://localhost:${graphiqlPort}/graphiql?key=${resolvedGraphiqlKey}`
Copy link
Contributor

Choose a reason for hiding this comment

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

🐛 Bug: The resolvedGraphiqlKey may contain special characters if provided by the user via flags. Injecting it directly into the query string without encoding can result in a malformed URL or unexpected behavior.

Suggestion: ? http://localhost:${graphiqlPort}/graphiql?key=${encodeURIComponent(resolvedGraphiqlKey)}

@isaacroldan isaacroldan force-pushed the 03-17-fix_require_authentication_key_for_graphiql_dev_server branch 2 times, most recently from 7ce3874 to d5bb5f8 Compare March 18, 2026 08:22
The GraphiQL development server that ships with Shopify CLI proxied
requests to the Shopify Admin API without authentication by default.
Any process on the developer's machine could send requests to
localhost:3457 and have them forwarded with a valid OAuth token.

This change fixes three vulnerabilities:

1. Always require an auth key. When --graphiql-key is not provided,
   derive one deterministically via HMAC-SHA256(apiSecret, storeFqdn).
   This is stable across restarts (browser tabs survive) and not
   guessable without the app secret (which already grants full access).

2. Add failIfUnmatchedKey() to /graphiql/status endpoint, which
   previously leaked storeFqdn, appName, and appUrl even when a key
   was explicitly configured.

3. Validate api_version parameter to prevent path traversal attacks
   (e.g. ../rest/2024-01/products.json). Only YYYY-MM and 'unstable'
   are accepted.

Bug bounty report: https://bugbounty.shopify.io/reports/3596212

Co-authored-by: Isaac Roldán <isaac.roldan@shopify.com>
Co-authored-by: Claude Code <claude-code@anthropic.com>
@isaacroldan isaacroldan force-pushed the 03-17-fix_require_authentication_key_for_graphiql_dev_server branch from d5bb5f8 to 04702b0 Compare March 18, 2026 09:01
@isaacroldan isaacroldan requested a review from a team as a code owner March 18, 2026 09:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants