Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

App doesn't authenticate call through proxy unless machine receives call from App embed first. #925

Closed
SandyWyper opened this issue Jan 9, 2025 · 4 comments

Comments

@SandyWyper
Copy link

SandyWyper commented Jan 9, 2025

Description

I have a simple app that takes a call from the theme, parses some data and then updates a metafield via the admin-API. The theme uses an app proxy route to make the call, with a simple POST request with json. I have a route that hits this action...

import type { ActionFunction, ActionFunctionArgs } from "@remix-run/node";
import { authenticate } from "../shopify.server";

export const action: ActionFunction = async ({
  request,
}: ActionFunctionArgs) => {
  const { admin } = await authenticate.public.appProxy(request);
  ...

Everything works perfectly when running a dev server. Then I deployed to fly.io, and my app works fine, when opening up the embedded app iframe in the Shopify admin. Also, if i then go immediately and test the call from the theme it works fine and updates the metafield as expected. However, it seams that this only happens if the virtual machine on fly.io has first authenticated by opening the app embed in the shopify admin. If the virtual machine boots up from cold, only to receive the call from the theme through the proxy, it fails to authenticate (admin is undefined).

Steps to reproduce

(I've done this about 6 or 7 times, to make sure i wasn't missing something!)

Standard Remix starter
npm init @shopify/app@latest

then create a new route, for me is /app/routes/app.scores.tsx.

import type { ActionFunction, ActionFunctionArgs } from "@remix-run/node";
import { authenticate } from "../shopify.server";

export const action: ActionFunction = async ({
  request,
}: ActionFunctionArgs) => {
  console.log(" --------------------------- proxy hit ----------------------");
  const { admin } = await authenticate.public.appProxy(request);
  console.log({ admin });
};

set-up the proxy for your app so that the call from the theme is hitting the correct route.

deploy app, install on store and deploy to fly, following the steps here - https://shopify.dev/docs/apps/launch/deployment/deploy-web-app/deploy-to-fly

Thoughts

The proxy is working, and everything works great in development. My thinking is that my app is taking calls outside of the app-bridge wrapper, so not authenticating... but shouldn't remix handle this token exchange using the session token in the request?

Is this a bad approach, or is there some way that Remix would handle this auth process that I have missed?

Thanks in advance! I've been tackling this one on and off for about 5 weeks. Been around and around the docs more times than is healthy 😵

@nboliver-ventureweb
Copy link

nboliver-ventureweb commented Jan 10, 2025

Not sure if this could be related or not, but what settings do you have in your fly config for auto_stop_machines and auto_start_machines? I had to set machines to run continuously to get an app working on Fly (it's a Node app though, not Remix): https://fly.io/docs/launch/autostop-autostart/#keep-all-machines-running-continuously

@SandyWyper
Copy link
Author

Thanks for your reply @nboliver-ventureweb .
Yeah, that would probably solve the issue. Especially as the app is only for one store. But it would make the cost of the app skyrocket, when really it will only be called occasionally, and it shouldn't be necessary.

@lizkenyon
Copy link
Contributor

Hi there 👋

The default Remix template comes with a Sqlite database using Prisma.

Some hosting providers, such as Fly.io, might put web containers to sleep when they're idle, which resets the disk and wipes the SQLite database. You can persist your data across redeploys by creating a separate volume for the database file.
From our docs

Because your database is being wiped, the app has no record of the Shop, and cannot authenticate the request.

For the App to perform token exchange, and recreate the access token for the Shop, the incoming request would need to have an ID Token/Session Token. At this time only requests the originate from the Shopify admin can be used to perform token exchange.

You will need to determine what is the best route for your scenario, for ensuring your app can persist the access tokens for the shops that have installed your app if you will be using an App Proxy.

@SandyWyper
Copy link
Author

Hi @lizkenyon

Thank you so much for your comprehensive answer. This makes perfect sense now!

I will close this issue now.

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

No branches or pull requests

3 participants