Shopify / shopify_app

A Rails Engine for building Shopify Apps
MIT License
1.76k stars 683 forks source link

V22.2 new token exchange strategy results in 404 #1856

Closed resistorsoftware closed 4 months ago

resistorsoftware commented 4 months ago

Issue summary

Before opening this issue, I have:

Installed the shopify_app and to a new rails app and ran the generators. Added Shopify CLI and Shopify App to the package JSON and the new token code to the header of the embedded app layout, so the API key of the App is available, as is App Bridge 4. Scopes were deployed using Shopify CLI and both the shopify.app.toml and shopify.web.toml files exist.

Migrations were run on created DB for testing, and App renders the /login route. Fired up the CLI with the dev command and the App booted and provided the CLI redirect route into the shop where the App should be installed.

My two cents on this, is the id_token is missing from the CLI command, so the token exchange mechanism is not triggering, so things don't work at all, hence 404? Just curious if this is it, and when it might be possible to play with token exchange install?

Expected behavior

What do you think should happen?

Shopify should recognize the store in question has not installed the App and go through the App install screens, so I expected to see the App Install Dialog a merchant would see

Actual behavior

What actually happens?

The App renders the home controller, with the HMAC, Shop and Host parameters but then renders a 404 There is No Page At This Address

Steps to reproduce the problem

  1. bundle install latest Shopify App
  2. set newest token authentication in config
  3. use Shopify CLI to redirect into store using client ID to install app

Debug logs

GraphiQL server started on port 3457
web.1    | started with pid 20813
js.1     | started with pid 20814
tunnel.1 | started with pid 20815
js.1     | $ esbuild app/javascript/*.* --bundle --sourcemap --format=esm --outdir=app/assets/builds --public-path=/assets --watch
web.1    | /Users/uncle/.rbenv/versions/3.3.1/lib/ruby/gems/3.3.0/gems/bootsnap-1.18.3/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:30: warning: /Users/uncle/.rbenv/versions/3.3.1/lib/ruby/3.3.0/mutex_m.rb was loaded from the standard library, but will no longer be part of the default gems since Ruby 3.4.0. Add mutex_m to your Gemfile or gemspec. Also contact author of activesupport-7.0.8.3 to add mutex_m into its gemspec.
js.1     | [watch] build finished, watching for changes...
web.1    | DEBUGGER: Debugger can attach via UNIX domain socket (/var/folders/hd/l5prgnt94cg4qx9yp5dd91000000gn/T/rdbg-501/rdbg-20813)
web.1    | => Booting Puma
web.1    | => Rails 7.0.8.3 application starting in development
web.1    | => Run `bin/rails server --help` for more startup options
web.1    | ================================================
web.1    | => Upcoming deprecation in v23.0:
web.1    | * 'CallbackController::perform_after_authenticate_job' and related methods 'install_webhooks', 'perform_after_authenticate_job'
web.1    | * will be deprecated from CallbackController in the next major release. If you need to customize
web.1    | * post authentication tasks, see https://github.com/Shopify/shopify_app/blob/main/docs/shopify_app/authentication.md#post-authenticate-tasks
web.1    | ================================================
web.1    | Puma starting in single mode...
web.1    | * Puma version: 5.6.8 (ruby 3.3.1-p55) ("Birdie's Version")
web.1    | *  Min threads: 5
web.1    | *  Max threads: 5
web.1    | *  Environment: development
web.1    | *          PID: 20813
web.1    | * Listening on http://127.0.0.1:3209
web.1    | * Listening on http://[::1]:3209
web.1    | Use Ctrl-C to stop
web.1    | Started GET "/?hmac=9490111318ca63a2796e787ecba6073475e1690b52e34b32970ba5f4b12e7351&host=YWRtaW4uc2hvcGlmeS5jb20vc3RvcmUvaG90d2lyZS1yZXNpc3Rvcg&shop=hotwire-resistor.myshopify.com&timestamp=1717471452" for 69.156.40.241 at
web.1    | Cannot render console from 69.156.40.241! Allowed networks: 127.0.0.0/127.255.255.255, ::1
web.1    |   ActiveRecord::SchemaMigration Pluck (1.8ms)  SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
web.1    | Processing by HomeController#index as HTML
web.1    |   Parameters: {"hmac"=>"9490111318ca63a2796e787ecba6073475e1690b52e34b32970ba5f4b12e7351", "host"=>"YWRtaW4uc2hvcGlmeS5jb20vc3RvcmUvaG90d2lyZS1yZXNpc3Rvcg", "shop"=>"hotwire-resistor.myshopify.com", "timestamp"=>"1717471452"}
web.1    | Redirected to https://admin.shopify.com/store/kubasa-tasty/apps/6a5b231756088d5669f7a2b128f79be2
web.1    | Completed 302 Found in 50ms (ActiveRecord: 0.0ms | Allocations: 76630)

────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

› Press d │ toggle development store preview: ✔ on
› Press g │ open GraphiQL (Admin API) in your browser
› Press p │ preview in your browser
› Press q │ quit

Preview URL: https://kubasa-tasty.myshopify.com/admin/oauth/redirect_from_cli?client_id=6a5b231756088d5669f7a2b128f79be2
GraphiQL URL: http://localhost:3457/graphiql
zzooeeyy commented 4 months ago

Hey @resistorsoftware, 404 errors aren't expected for token exchange, do you know what path it's requesting when it encountered the 404? I wonder if that's something else causing this issue. If you could create a HAR file for the install, we could take a look to see where the 404 came from.

Other steps to troubleshoot:

resistorsoftware commented 4 months ago

@zzooeeyy I traced it to a very simple App but with the latest in Token Exchange turned on. One line of code in the config. The problem is, when trying to install the App using the CLI link, the App gets hit from Shopify with the HMAC, shop, and host parameters, but since id_token is not present, that whole path of using token exchange busts?

Just my guess, but that was as far as my debugging got. If there is no shopify_id_token to work with, token exchange reverts to some path that results in 404?

So I am using CLI 3 to click the link provided, to install the App in my dev shop, whereby the App is configured as Token Exchange and not just the old OAuth exchange mechanism.

zzooeeyy commented 4 months ago

Hey! Token exchange concern should detect whether id_token or HTTP_AUTHORIZATION header is available to retrieve the session token for the exchange. If it's not available it'll redirect the app to a bounce page we added to get a session token from app bridge. Does this bounce redirect happen for your app and is this route/controller available ShopifyApp::SessionController::patch_shopify_id_token?

resistorsoftware commented 4 months ago

I am guessing that since the token does not exist, and then the App redirects to a bounce page, then that is the issue.

How is a token even available when the App is not even installed? I am confused. Should the App not just be redirected to the usual oAuth so the merchant can approve the App install in the first place?

zzooeeyy commented 4 months ago

If your app has Shopify Managed Install configured (required for Token Exchange), then when Shopify admin tries to loads your app, Shopify will prompt the user to install the app.

The /login route should redirect the user to the Shopify managed install page to install the app

resistorsoftware commented 4 months ago

OK. Well, that is not what is happening, hence my initial POST about the 404.

Latest Shopify CLI was used to do a DEPLOY to the App in question (at least 8 times)... so the scopes should have been transferred to the App for use in a managed installed situation. I do not know what else to do in this respect.

Since this App is not installed in the development store, that installation whereby there is no detected ID token, should work. When I turn off token exchange by commenting out that one line, the app installs fine, as expected. Hence my confusion about what has changed with that token exchange. I could not see how the missing ID Token was detected by the token exchange, allowing the App to follow the usual install principles. All I saw was the App dying with a 404.

zzooeeyy commented 4 months ago

From the logs, it looks like it's redirecting you to Shopify admin to load the app

web.1    | Redirected to https://admin.shopify.com/store/kubasa-tasty/apps/6a5b231756088d5669f7a2b128f79be2

404 usually indicates Shopify isn't able to load your app URL. Could you make sure your tunnel is running properly and the Application URL in your Partners app is pointing to the correct URL?

image

Could you provide your shopify.app.toml ?

resistorsoftware commented 4 months ago

@zzooeeyy The ngrok tunnel works perfectly fine without the token exchange activated! The App works fine too! It is only when I activate the token exchange in the App config and then try an install that it borks out. For completeness and to answer your question, the shopify.app.toml represents as below, and I am not sure there is anything to change in it to get the token exchange right?

That being said, if there is something weird going on under the hood with token exchange and tunnels, exclusive to token exchange, I would love to be the first to know what that might be. Since it works perfect without token exchange, either my tunneling is just luck, or I am blessed with half a tunnel half the time.

client_id = "6a5b231756088d5669f7a2b128f79be2"
name = "Frankie Goes to Hollywood"
handle = "frankie-goes-to-hollywood"
application_url = "https://kubasa.ngrok.io/"
embedded = true

[build]
automatically_update_urls_on_dev = false
dev_store_url = "kubasa.myshopify.com"
include_config_on_deploy = true

[access_scopes]
scopes = "read_inventory,write_price_rules,write_products"
use_legacy_install_flow = true

[auth]
redirect_urls = [ "https://kubasas.ngrok.io/auth/shopify/callback" ]

[webhooks]
api_version = "2024-01"

[pos]
embedded = false
zzooeeyy commented 4 months ago

Ah ha -- use_legacy_install_flow = true

When omitted or false, scopes are saved in your app's configuration, and are automatically requested when the app is installed on a store or when you update the scopes value. This is referred to as Shopify managed installation.

See docs here

Shopify won't handle installing your app unless this value is false or omitted, and your app won't redirect the user to old OAuth install flow since it's configured to the new auth. So you'll need to either remove this line or set it to false and deploy your app again before re-trying.

resistorsoftware commented 4 months ago

@zzooeeyy Thanks for spotting my mistake. Much appreciated. All good. You were right. Changing that to false allowed the token exchange version to install same same.

zzooeeyy commented 4 months ago

Awesome! Token exchange is great, thanks for trying it out, you'll love it. Install and auth process is much quicker since it doesn't require any OAuth redirects.. I'm closing this ticket now the 404 problem is resolved.

elioncho commented 3 months ago

use_legacy_install_flow

Ah ha -- use_legacy_install_flow = true

When omitted or false, scopes are saved in your app's configuration, and are automatically requested when the app is installed on a store or when you update the scopes value. This is referred to as Shopify managed installation.

See docs here

Shopify won't handle installing your app unless this value is false or omitted, and your app won't redirect the user to old OAuth install flow since it's configured to the new auth. So you'll need to either remove this line or set it to false and deploy your app again before re-trying.

Important! Had the same issue! Thanks!

zzooeeyy commented 3 months ago

Thanks! I've added a PR to add clarification docs so this isn't missed in the guide