-
Notifications
You must be signed in to change notification settings - Fork 81
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
Invalid signature with App Proxy for Remix loaders and actions #455
Comments
@byrichardpowell it appears Remix strips |
Hey, thanks for reporting this issue, and thank you for all the details, they make our lives a lot easier :) We will investigate whether there's anything we can do from this package to solve it, but if this is happening on Remix it's possible we won't be able to. Based on the code comments though, it sounds like this might be fixed with the recent v2 release of Remix? In the meantime, do you still observe this behaviour if you use a name other than |
hi @paulomarg, The Regarding the code comments, I was hopeful that v2 would address this, but unfortunately it's still there in v2. The workaround I was pursuing was to put the original URL in a header in Don't know how closely the Remix team is working with you on these Shopify projects, but maybe @ryanflorence or someone on his team have an idea about this. I realize the app proxy behavior is new in this package, but presumably this affects everyone. Is anyone else having this issue? Thanks! |
Hey, sorry for the delayed response! Thank you for the detailed write-up, this is super helpful. Unfortunately, I think you're right, and I can't see a way to work around this right now. It sounds like this would only be a problem if the page needs to interact with the server after the initial load, since that's when Remix will set those search params, so it might not affect everyone, but it's still significant. Just in case it helps, it might be possible to work around this if you use the I'll bring this to the Remix team to see if we can work out a better solution. Thanks! |
OK thanks @paulomarg. Will keep an eye out for any changes in Remix. |
This issue is stale because it has been open for 90 days with no activity. It will be closed if no further action occurs in 14 days. |
I'm encountering the same issue. I managed to work around it by creating a custom
In server.ts I use my custom adapter:
Then in loader I restore the URL for authentication:
|
Hello Folks, I hope you are doing well. Have this issue is being resolved? I am also facing the same problem. Actions in remix are not validating signature but loader are. Showing "App proxy request has invalid signature" error message in terminal. Thanks |
Hi Guys 👋 , Any updates on this? I'm experiencing the same issue regardless of whether it's a loader or action. backend import { json } from '@remix-run/node'
import type { LoaderFunction } from '@remix-run/node'
import { authenticate } from '../shopify.server'
export const loader: LoaderFunction = async ({ request }) => {
console.log("🚀 ~ constloader:LoaderFunction= ~ request:", request)
try {
const { admin } = await authenticate.public.appProxy(request)
return await admin
?.graphql(
`#graphql
query Products {
products(first: 3) {
edges {
node {
id
title
}
}
}
}`
)
.then(async (response) => response.json())
.then((data) => json({ data }, { status: 200 }))
.catch((error) => {
console.error((error as Error).message) // Log the error for server-side debugging
return json(
{ error: 'Failed to fetch locations', err: error },
{ status: 500 },
)
})
} catch (error) {
console.error((error as Error).message) // Log the error for server-side debugging
return json(
{ error: 'Failed to fetch locations', err: error },
{ status: 500 },
)
}
} Frontend: fetch('/apps/proxy/location-action', {
method: 'GET', // or 'POST' if you are sending data to the server
headers: {
'Content-Type': 'application/json',
},
})
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
console.log(data); // Process and display the data on your page
})
.catch(error => console.error('There has been a problem with your fetch operation:', error.message)); |
Hey folks! We've just changed how app proxy authentication works to compensate for the The only caveat with this solution is that proxied route pathnames must match the value set in the dashboard (i.e. Both of these restrictions happen because Remix doesn't support URL rewriting yet, so the paths need to match. We're working with the Remix team on removing the need for the trailing slash, but if that happens it won't require any changes on apps that are using these new components. This will be included in the next release of the package, and hopefully it'll make the experience of working with proxies better. And please do let us know if you still run into issues with these. |
Hi @paulomarg I have a POST request from a Theme Extension App to a Remix App, like: App Proxy Config:
I think that the form submission URL, transfers the data to the app proxy URL, like this:
extensions/sb-user-extension-app-v2/assets/extension.js
app/routes/app.api.jsx:
And in my fly.io live logs, I see:
Now, the interesting thing is, that the authentication works, when I run this in development with my ngrok tunnel URL: https://alien-amazed-lab.ngrok-free.app But, with my production fly.io URL: https://sb-user-admin-app-v1.fly.dev The authentication fails, although the rest of the method's business logic succeeds [apart from the graphql part], if I remove:
But, then I cannot expose I am not sure whether my problem is caused by the stuff, you've been talking about, in this thread. Can you suggest a solution, to allow the authentication to work, properly? For instance, is there anyway I can import the If you want to see more detail in my fly.io log, I would be happy to send you the file. Thanks, in advance... |
@vincaslt I would be interested in trying out your solution.
Could you provide some more code, so that I can get a better understanding of what you are doing? I am using ngrok in development, as my tunnel, and fly.io, in production? In development, the app proxy works perfectly, but it fails with:
In production? I have looked at the search string, from the request
I am posting the data from a form, in my Theme App Extension, to my Remix app via an App Proxy. I am using POST, so my request hits the Remix action() method, and not the loader() method. The only weird thing that I have noticed is that some of the references to the app proxy URL, in the request data, have http and not https, as the protocol? Here is the request object:
Notice how the UPDATE: 03.04.2024 I managed to resolve the invalid signature issue, after a deep dive into: https://github.com/Shopify/shopify-api-js/tree/main I would be happy to share my solution with anyone, who can’t resolve this. Please reply to this post with my username @ reference and I will get back to you. Essentially, I think my signature was corrupted due to a Everything is now working well, in both development & production apps. 🙂 |
Good to hear is working @charlesr1971! Sorry for the delay in responding, but yes, since the requests are signed with a timestamp, that can be a problem. It's good to know that can happen so we can advise folks that run into this kind of thing in the future. Thanks for sharing your solution! |
@paulomarg To be honest, I am not sure whether my fix, is a particularly secure one, as it is difficult to know whether my signature has been tampered with or whether there is a timestamp discrepancy? But, I had to resolve this somehow. It would be better to use query params that are not so exposed to discrepancies between the Shopify and remote hosting server. It might be better to rethink whether using a timestamp is such a good idea? |
@paulomarg How can I get #710 installed in my current Remix project? Installing the latest |
Hey, good callout - it'll be included in the next release, which we're due for! |
I'm going to close this in favour of #436 since we have two discussions going in different issues, and the latest is there. |
I am running into what seems to be similar behavior. When I first publish to Fly.io and immediately test it -- all is well but after 10 minutes or so it's like it goes stale and fails. The only way I have found to get it to work again is to click the app inside the Shopify admin. Then all permissions work as expected for a bit. Can you share what you did please? |
@iamgerardm OK. My solution will only work if you get an |
@charlesr1971 Hmm, no. My request to the proxy comes from an App Block and when I use I thought maybe it was something to do with the initial fetch headers in particular the Even if your solution is not the exact solve for me I would love to see it to maybe get some ideas as to possible alternative solutions. |
@iamgerardm I don’t think my solution will work, for you, but here it is. Basically, I had to recalculate the signature. Please note that I am making my request from a Theme App Extension. And this issue only occurred in my fly.io production app, and not when used in development, using App Bridge and my NGROK tunnel. Also, note this is a précis version of my code, so I have removed unnecessary lines & variables etc. I hope it still works, but you should get an idea of what I’m trying to do. 🙂
|
Wow this is so mad that this has to be done by end users like @charlesr1971 ... 😱 Thanks for sharing your solution! |
@patrick-schneider-latori Yes. It’s crazy, but I think sometimes the timestamp between the Shopify server & the remote server, give different values, due to localisation divergence, which causes the signature to fail? |
@charlesr1971 Thanks for the snippets. In the end I determined it was a data persistence issue. The app is using the out of the box sqlite db and needed to add volumes on Fly.IO to maintain the offline session token when the servers went to sleep/restarted. |
Yes. Absolutely. I will add my fly.toml
start_with_migrations.sh
Notes: Error, running start_with_migrations.sh: The following problem, is only really an issue for Windows users. If you have an issue getting the
At the end of each line.
Here is a solution, on how to resolve this issue: https://community.fly.io/t/sqlite-not-getting-setup-properly/4386/11 There should only be an |
@charlesr1971 Thanks - this is pretty similar to what I ended up doing. I was talking about this topic on another thread as well and I believe in the future there may be a note/tip of this within the docs. |
Issue summary
When submitting a form with Remix, it appears that query string parameters are added to the form action without a value (e.g.
?index
). These parameters are removed from the request in the loader and action handler when there is no value, but remain when there is a value.This causes the App Proxy signature check to fail because Shopify signs the
index
query string parameter without a value, but it is no longer present when validating the signature in the Remix loader or action handler.We're using the new
await shopify.authenticate.public.appProxy(request)
feature for signature validation.@shopify/shopify-app-remix
1.2.1@remix-run/react
2.0.0The
GET
log appears after the loader and action handler logs, which is logged by the Shopify dev server.This is an example of the invalid signature:
This is an example of signature validation passing with
index=value
present (instead ofindex
orindex=
without a value).Expected behavior
Expect all query string parameters to be present to validate App Proxy signature.
Actual behavior
Query string parameters are missing in the loader where the App Proxy signature validation occurs.
Steps to reproduce the problem
await shopify.authenticate.public.appProxy(request)
to the loader in a page in your Remix app.?index
to the page URL and observe a bad request response (invalid signature)?index=value
to the page URL and observe a successful response (valid signature)The text was updated successfully, but these errors were encountered: