Questions and discussions about using the Shopify CLI and Shopify-built libraries.
With the strong focus on privacy, browsers have recently started phasing out support for the 3rd party cookie. This has caused issues for embedded apps which until now have required the use of 3rd party cookies.
Our new App Bridge auth beta introduces “session tokens” to empower developers to create faster, more flexible, and more compatible apps. Session token based auth does not depend on cookies and instead relies on a Shopify-generated token that your app needs to send with every request.
In order to get started with App Bridge auth, or migrate your current app from traditional, cookie-based authentication, you can follow the guide in our developer documentation.
Questions about App Bridge auth or session tokens? Post them in this board or reach out to support through your partner dashboard.
Liam | Developer Advocate @ Shopify
- Was my reply helpful? Click Like to let me know!
- Was your question answered? Mark it as an Accepted Solution
- To learn more visit Shopify.dev or the Shopify Web Design and Development Blog
Hi! This looks like a mistake in our documentation.
The `getSessionToken` utility takes the `app` as its argument.
We'll update the docs, thanks for reporting!
To learn more visit the Shopify Help Center or the Community Blog.
Hi @rezaansyed - where are the docs for `userAuthenticatedFetch` ? I only see `authenticatedFetch` mentioned. Just plugging it in the same way throws errors.. is this tested at all??
@releod sorry I had meant to say `authenticatedFetch`. This is the link showing the example for `authenticatedFetch`: https://shopify.dev/tools/app-bridge/authentication#frontend-changes
To learn more visit the Shopify Help Center or the Community Blog.
Hi @Bengk,
As mentioned by Mike, there is App Bridge support for frontend changes, as well as a library by a community member to validate the session token on a node backend: https://community.shopify.com/c/Shopify-APIs-SDKs/Introducing-cookieless-authentication-beta-with-Ap...
We're still figuring out the best way for us to surface the backend logic for node. The good news is that most of the logic lives in App Bridge, and you can use the `authenticatedFetch` fetch implementation with pretty much any modern JS framework (see more about `authenticatedFetch` here: https://shopify.dev/tools/app-bridge/authentication#setup).
I did notice that a community member created a helper to validate the session token for node: https://github.com/leighs-hammer/shopify-jwt-auth-verify You could see if that meets your needs.
To learn more visit the Shopify Help Center or the Community Blog.
Hi!
We're running into an issue where app.getState() and getSessionToken(app) do not resolve to anything - no results or errors are return. authenticatedFetch has the same issue but that is because it is uses getSessionToken(app). We can can confirm that the app bridge is loaded and app exists.
Here is the code:
import React, { useContext } from 'react';
import ReactDOM from 'react-dom';
import { createBrowserHistory } from 'history';
import { Router, Route, Switch } from 'react-router-dom';
import { Context, Provider } from '@shopify/app-bridge-react';
import { authenticatedFetch, getSessionToken } from '@shopify/app-bridge-utils';
import COMPONENT from 'app/component.jsx';
let component = COMPONENT;
let apiKey = 'XXXXXXXXXXXX';
let shop = 'YYYYYYYYYYY';
const MyApp = () => {
const app = useContext(Context);
if (app) {
console.log(app)
app.getState()
.then((state) => console.log('state'))
.catch((err) => console.log(err));
getSessionToken(app)
.then((result) => console.log(result))
.catch((err) => console.log(err));
authenticatedFetch(app)('https://www.espn.com/').then(response => console.log(response)).catch((err) => console.log(err))
}
return (
<Router history={createBrowserHistory()}>
<Switch>
<Route path='/' component={component} />
</Switch>
</Router>
);
};
ReactDOM.render(
<Provider config={{ apiKey: apiKey, shopOrigin: shop }}>
<MyApp />
</Provider>
, document.getElementById('root'));
The app variable looks like this:
What are we doing wrong? We just want to grab the session token to replace the cookies for authenticated AJAX calls.
I think this thread needs to be closed and have more specific new threads dedicated to specific problems. Just my 0.02.
HunkyBill Agreed, I just moved the question to this new thread. Please let me know if you have any ideas there.
@Liam @rezaansyed @Michael_Ragalie We have an issue where app.getState() and getSessionToken(app) do not resolve - no results or errors are returned. Do you have any idea why that may be? We created a thread here
---- removed ----
@dsingh replying to your "Considering that now there is no need for use of cookies. Is there a way to retrieve the myshopify.com domain an embedded app is loaded in? That is probably the last part which is needed before embedded apps can be truly cookie-less." question:
The domain is passed as a query parameter in the url on every request.
Yes, I figured out that was happening. Issue was on my end. My CustomRouter wasn't using the bridge properly. I realized that after posting.
Can Shopify provide better support for Python / Django? I find it hard to implment the new authentication on my own.
Now, I can only render a page with App Bridge setup. When I clicked Test app on development store => select my development store, it shows
I don't know how to go further. I suppose to load App Bridge in Shopify admin and get my session token with getSessionToken(App)
How can I go further?
So if I'm understanding correctly, the jwt token replaces the old access_token? Instead of X-Shopify-Storefront-Access-Token as the header name and the access_token as the value, you send `Authorization: Bearer [JWT HERE]` to Shopify instead?
I can't use a react app or rails app on this project so the shopify provided examples aren't doing me much good. Plus I'm not a ruby dev and the code looks like a bunch of undocumented magic to me. My backend is a Golang app and my frontend is framework-less, bundled through webpack. Might throw VueJs in there eventually.
How is the app install process supposed to work now? Does app-bridge handle that...?
I really wish the docs had a step-by-step guide.
I'm not understanding what is now the job of the backend vs the frontend and what I'm supposed to do with the token that getSessionToken(window.app) returns. Should I be saving that somewhere like my db? Or in a session? If app-bridge is responsible for everything now, how is my frontend supposed to know that the app is already installed? How does app-bridge redirect to the install/permissions URL? What's my /auth/login/callback route supposed to do? Re-init app-bridge somehow and do [something] with it?
A step-by-step guide in the docs of what your backend _and_ frontend should be individually responsible for and how would be amazing. Right now I'm having to make a whooooole lot of assumptions.
After struggling for several hours, I found that there was a line from the doc:
If your app doesn't have a shop token for the shop, then the route should respond with a redirect to send the user to the OAuth login flow. This ensures that the app is installed and receives its shop token.
I guessed that it meant generating the permission url like OAuth does. After that, redirect user to the permission url so that the user can install your app. So I suppose it will reuse the OAuth install / permission flow before proceeding to the jwt auth flow.
After installation, redirect user to the redirect url which you set in the App setup page. The redirect url will "load skeleton" as the flow chart (under Authentication flow using JWT) in the doc. For testing, 'Load Skeleton' just renders a simple HTML template with the App Bridge init within the <head> tag.
Now, I can successfully init App Bridge and see my embedded app.
This is crazy, I thought it would ask users to install app somewhere in the flow chart.
So the backend first checks for the presence of a session token (or maybe it should check for a shop or hmac query param?), and if not redirects the user to the oauth permissions URL, correct?
The user then grants permissions and gets redirected to your callback URL. This callback URL loads the skeleton with app bridge and does nothing other than display the frontend and init app bridge?
Or should the callback URL also be redirecting back to your main app URL after doing something with the session token that app bridge responds with? Did you do something at this step like set a browser <-> server session to store the JWT or something?
My implementation is to check shop param. If it exists, construct the permission url for user installation.
Session token is given by App Bridge later.
The user then grants permissions and gets redirected to your callback URL. This callback URL loads the skeleton with app bridge and does nothing other than display the frontend and init app bridge?
Yes
After you get the session token, you can send it in your request. Your server-side (backend) will parse to get the info. I am handling this part today.
@Liam @Michael_Ragalie I'll admit I haven't yet attempted to implement this, but in plain English can you please help me understand the difference between the new JWT vs. authorizing requests using hmac validation (as appended by Shopify in every URL request to one's app)?
In looking through the documentation, the two seem very similar - using SHA-256 and signing the string using the HS256 algorithm by using the app’s secret as the signing key. I get that the JWT is provided by App Bridge and doesn't just live in the URL like hmac (and perhaps therein lies its advantage security wise - I'm clearly not an expert on web security), but the main difference as far as I can tell is that it includes expiration while hmac doesn't expire. However, it should be simple to add one's own timestamp check (also provided by Shopify in each URL request) to confirm the request isn't too stale or being reused. In my mind, if one validates hmac and also checks the timestamp, it seems eminently difficult to spoof a URL and fool one's app into thinking it's a valid app request from Shopify.
Am I missing something here? Thank you!
A problem with using the HMAC is that the mechanics for getting a new one are pretty ugly.
Every X minutes and every time a page navigation occurs, the app would need to redirect to Shopify and then Shopify would need to redirect back to the app with a new HMAC + timestamp.
Another issue is that the HMAC in the URL might show up in the Referer header, which could leak it to some other actor.
With the JWT mechanism via App Bridge, getting a new token is pretty seamless, delivery of that token to the correct origin is enforced by the browser, and the token is short-lived.
To learn more visit the Shopify Help Center or the Community Blog.
@Michael_Ragalie thanks for the response, makes sense. In my case, both of my apps are POS embedded apps and so the "session" is always quite short and moment in time for the merchant. Please keep us updated when JWT becomes available for POS embedded apps.
I don't know if we have any apps using it on POS, but I think it _should_ work. Was there a problem you were running into with it?
To learn more visit the Shopify Help Center or the Community Blog.
@Michael_Ragalie I actually haven't tried it yet, I thought I saw on page 1 or 2 of this thread that someone else asked about it for POS and was told it wasn't ready. But based on reading the documentation, I also don't see a reason it shouldn't work. Thanks for following up.
I'm finding the documentation lacking, i've tried trawling through the app-bridge code but that wasn't much help either.
Ideally it would be great if i could embed my app admin functionality but its not a shopify specific application so i don't really want to pollute it with shopify specific libs and code. I appreciate I might have to but I would like to have as little impact as possible. From the Documentation it is my understanding that the minimal shopify code I need is `Provider` (is that true can i get the jwt token without app-bridge?) but how do I actually get the JWT? The docs say i can get it manually but with no examples, am i supposed to use react component? Is it in the session storage? is it on a header?
I'm already using JWT for non shopify based users so it would be great if I could get it working.
To learn more visit the Shopify Help Center or the Community Blog.
Are apps that don't use this method getting rejected? We've had an application rejected because (we believe) the tester was using incognito mode.
If you're redirecting users to a subscription URL from your app, the redirect will cause an IFRAME issue in the browser.
You need to perform the redirection from outside your app IFRAME. Either using the AppBridge redirection, or using window.top.location.href.
This isn't the issue. I've been sent a screencast by the tester show he/she had been testing in Incognito mode. They failed the submission (again), naming 'SameSite' as the issue (I guarantee this is done correctly).
You can validate by visiting https://samesite-sandbox.glitch.me/ in a normal chrome window, and an incognito window.
As far as I'm concerned, if testers are expecting your application to work in incognito mode, then you MUST implement cookie-less authentication. Is this mentioned anywhere int he documentation?
@optizio are you using the shopify_app gem? I had built an app using the shopify_app gem and it also failed naming the samesite issue with an infinite redirect issue (though I simply could not reproduce). I ended up migrating away from shopify_app gem and using my own auth and got it to pass.
@Michael_Ragalie I've finally gotten around to playing around with this and I have a question. I found the npm library which seems to work well (thank you Leigh Barnes).
However, there's part of my app's server that will be hosted on Google Apps Script and I have to manually decode and verify the JWT there. When I do:
var byteSignature = Utilities.computeHmacSha256Signature(<header>.<payload>, 'sshhh');
var signed = Utilities.base64EncodeWebSafe(byteSignature);
signed equals the <signature> from the JWT, except signed has an extra = padding character(s) at the end. It seems all the Apps Script methods for Base64Encode will pad it. Is it safe to just drop any padding = characters for the purposes of the boolean comparison? Or is there a different / better way?
@policenauts1 - we were using the node shopify app cli, with no changes, and the app got rejected (for the same infinite loop issue when there no cookies allowed).
@optizio gotcha - from reading the JWT documentation, it's not clear to me whether they've incorporated JWT into that library or have provided steps to override the built-in auth with JWT. Perhaps someeone from Shopify can weigh in here.
So you tested your App thoroughly, never experienced this infinite loop issue, but the Shopify Review experienced infinite loop, so clearly, the App Review process is broken? Usually, Shopify would admit to this, as they can tell when their tests are flakey, as per their admission that the whole Same Site cookie test issued false positives for the problem.
@HunkyBill - Yes that's right, tested extremely thoroughly and agree with your sentiment. I can replicate the issue on my own app, as well as many big production apps, when I block ALL third party cookies (either manually or by browsing in incognito mode). If they are testing in Incognito mode, I'm of the opinion that all apps that use cookies will fail review with this infinite loop issue (it happens with the default app built with the Shopify cli (node)).
Just to add to the topic at hand. If you are implementing cookie-less auth and use { authenticatedFetch } - make sure you use the exact version mentioned in the docs here (https://shopify.dev/tutorials/authenticate-your-app-using-session-tokens) (1.23.0) - newer versions fail when proxying graphql requests. Having already spent hours figuring out that a patch version bump was the cause of my pain, I haven't had the time to figure out exactly what's different in those two versions!
To be fair, it does state you 'NEED' that version, silly me 😉
This thread is supposed to be nothing but an intro to JWT being useful for Apps. We should collectively try and establish other threads for other problems. If oAuth to establish credentials is somehow borked with a Node App, it belongs in a Node oAuth thread. Otherwise we are all losing focus on the aspect of what is actually a problem, and for who.
Hi everyone,
This thread is great knowledge base for the session tokens subject
Thank you @KisukaKiza and @MathewsJoseph for your great contributions
However, I didn't get to see any response from Shopify team about official way to do this, must I remove koa packages and implement authentication logic on my own in order to use the new functionality?
Isn't there an official support planned to provide managed components integrated with it?
Would be great to get an official response here
Can I get clarity on "dest" in the decoded JWT payload?
will "dest" ALWAYS be the *.myshopify.com account name, or can it be the TLD?
Example:
Domain = myshop.com
Shopify Account = myshop.myshopify.com
In the JWT payload will:
dest = myshop.com
or
dest = myshop.myshopify.com