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
Thanks for the opinion. My two cents, your english does not communicate your true knowledge on the subject, but instead provides a fleeting glimpse that perhaps you know and understand the subject, but not well enough to really explain it. Perhaps in a blog post, with explicit examples, what you just said would be comprehensible.
This is not a thread for code, teaching or evaluating JWT. Shopify simply threw it out there and expects it to have uptake. Clearly this is the beginning of the journey for most. Throwing out terms like dest and sub with no context as to what those are, or mean, adds nothing to the convo.
Sorry. Just my opining.
I expect that one day soon, JWT will in fact be a useful pattern for most, but at this time, it remains sketch at best to bother with it, unless one of two things:
1. you already implemented JWT as an authentication scheme, no one hacked you when you did it wrong, it all works, and you can do it in your sleep now
2. you are sick and tired of the third-party cookie effort, and desperately want to get off it, and will invest N hours (n between 0 and 100) in learning JWT ala Shopify.
Personally, with all the stuff currently broken, borked or painful with Shopify Apps, I am going to wait a bit to see how JWT takes off and becomes mainstream before I waste too many hours with it.
@KisukaKiza If you're at all concerned about security you wouldn't use the shopify domain to verify the logged in user/access. There is a possibility for it to be modified (until or unless Shopify says otherwise).
As for your idea to maintain a user ids list: What happens when a store owner removes access for a user and your list is out of date now? Now, this is just hypothetical and I'm pretty sure that the token will expire soon enough after so it won't be valid anymore. But, that being said; "almost" secure isn't an option for me if I want to sleep at night.
Shopify doesn't provide partner with any information through Open Auth about "who" the logged in user is unless you are using online tokens in which case associated_user is returned but that might not be useful for most of the Partners/App developers who require offline tokens to check for new orders/customers. Cleanest solution to this probably would be to allow the developers to receive token or a Shop or a User. Shop token for apps using offline tokens and User token for apps using online tokens.
i.e. getSessionToken(APP, USER_OR_SHOP);
This can return a shop or user id in the token it returns depending upon what if we're trying to authenticate a shop or a user. This is just my opinion and I haven't thought too much about this. I'm just trying to throw my two cents in the basket.
For what it's worth this is a beta release and bound to have issues in it. Let's just all calm down and help Shopify build a better solution for us by testing it and providing feedback. I'm sure I'm not the only one who's annoyed by the safari ITP issues and I'm sure other browsers aren't too far behind in adding similar limitations on 3rd party cookies.
Not sure what's with the aggro attitude guys. I don't work for Shopify, I'm just another developer trying to provide some help as someone who has experience with JWTs and implemented the solution that Shopify has put into beta. My example I gave a few posts ago was for an online access token based app.
Regarding the dest and sub terminology, I assumed you read the docs they linked at the start of this topic which explains what those contain: https://shopify.dev/tools/app-bridge/authentication See "Payload" to know what those fields are.
As for JWT at a whole, this is not new. This authentication method has been around since 2010. You can read up on JWTs here: https://jwt.io
In regards to the security of JWT, they are signed with their payloads. You cannot manipulate a JWT because it will fail the validation check. Second, you always validate the integrity of the JWT when it's sent to your server / protected route prior to doing anything else. JWTs are also short-lived tokens, they do not last forever and are always different. You do not store JWT tokens ever either because they are stateless.
I'm not entirely sure what you mean on the last part. Those functions should NEVER be returning an access token. Access tokens are only ever generated after your oauth callback. The JWT is your "login" mechanism to know what access token you need to use to make queries.
Sorry if I'm not helping much, but I'm not entirely sure you guys are doing much research into JWTs in general judging from the content of your replies.
Honestly, the flow charts on https://shopify.dev/tools/app-bridge/authentication sum up the entire concept very well once you understand both the oAuth flow on Shopify with App Bridge, and the underlying principals of how JWTs work.
Not a question about JWT. They exist, are not new, and can be understood when you choose to dig in! You win there.
The problem is that recently (last 3 years or so), things have changed a lot with how Shopify works and how browsers work. Shopify has gamely tried to keep up, but it turns out the be both hard and painful since some things (browsers) are out of their control.
So this introduction to JWT could have gone smooth as butter, had the whole thing been introduced from scratch, as a thing. Instead, it was merged into the ongoing, constantly fluxing App gem, and the two do not necessarily mesh/merge well. To me, that is the main issue. I leave out and skip how this affects other projects and their individual woes with it (anything Node or Python or PHP for example).
And as a final note, it is new, and not the easiest thing in the world to comprehend and understand the App Bridge itself. Can you point out any nice deep dives into it? I have found zero, save for scrounging the code and issues. I guess my point is not being aggro to you, but to point out, that while for YOU it may all be easy and make sense, your specialization in that may prevent you from recognizing how much work it is to not only dissect the finer points of it (JWT) but also to make it servicable inside Shopify Apps, and with the constantly evolving App Bridge. Moving targets are not ideal to trick out with even the grand daddy's of authorization, so of course this issue persists.
You make out like it is a lack of desire on the community to learn JWT as the issue, which is in some part true. Over the years I myself have conferred that advice a lot, RTFM, when appropriate. I am not seeing anything yet where it is RTFM but instead, this is just complaints directed at no one in particular, because we have to adopt something that is awkward to use, and soon.
FWIW, while I think we would've been better off using something other than the myshopify.com domain as a permanent identifier, you can rely on the myshopify.com domain as a stable identifier for a shop.
Our Ruby reference implementation associates the myshopify.com domain with the Admin API token returned via OAuth. You can then use the `dest` claim in the JWT (which contains the myshopify.com domain) to lookup the correct Admin API token and use it to make requests on behalf of the shop. (This assumes that you're not using "online tokens", which are tied to a particular user, in which case you should use the `sub` claim which contains the user ID.)
To learn more visit the Shopify Help Center or the Community Blog.
@Michael_Ragalie Thank you for the clarification! I just wanted to make sure that we can use the shopify domain. Do we have a ballpark when this will be out of beta? We already have our implementation ready to roll out. This will make the experience consistent and much better for users.
We're aiming to bring it out of beta end of September/early October. First we're following up on some of the feedback from this thread and other venues to make sure this is something developers feel comfortable adopting 🙂
If it's working for you, feel free to use it in production (we have several apps doing so already, including some of our most used internal apps). Technically we reserve the right to make breaking changes until it's out of beta, but it's looking unlikely we will actually do so.
To learn more visit the Shopify Help Center or the Community Blog.
@Michael_Ragalie yeah it's pretty solid already honestly, I have it running in production with my app. The only issue I really have is you guys need to do some better documentation for app bridge in general. I wanted to know more about the new app bridge utils involving this new auth method, but I had to resort to looking in node_modules to find how how each of those new classes worked under the hood and what options I had with them. There are links to README files on the npm package but those take you to the internal repo which nobody has access to. Would be nice to at the very least have some kind of public documentation on some of these classes and other app bridge elements so we can fully understand them.
I was able to able to get this running in production as well using the info that @KisukaKiza posted on this thread along with the docs. I did end up taking the koa-shopify-auth and graphqlproxy packages and stripped them of cookie cruft, and consolidated a few things.
One of the things that would be nice to know more about is whether we can also still use a Provider after using createApp. It would be great if there was something like an app prop on the Provider where we could pass the already instantiated app. Without that it seems like the useAppBridge hook fails looking for the context.
Otherwise it seems to be working great! I've also tested on Safari, and the Safari Technology Preview and it's loading fast without issues.
Like already mentioned it would be cool to have access to the source on @shopify/app-bridge-utils.
@KisukaKiza thanks again for the great post earlier. Here it is again if anyone missed it:
Thanks for the feedback! We published some documentation on how the helpers work so you don't have to dig into node_modules to use them 🙂
https://shopify.dev/tools/app-bridge/authentication#using-session-token-helpers-directly
To learn more visit the Shopify Help Center or the Community Blog.
Is it possible to use this in the old Embedded App SDK?
To learn more visit the Shopify Help Center or the Community Blog.
I'm coming late to this thread, but only because my app was just rejected due to, ostensibly, a cookies issue (https://community.shopify.com/c/Shopify-APIs-SDKs/App-Rejected-shopify-app-gem-In-Shopify-POS-we-wer...).
I have another app live in the app store where I did the exact same thing as @Martin_Caum (before I knew about the existence of these canned Shopify Node and Rails apps). I just grab shopOrigin from the url parameter each time and then validate the hmac to confirm the request is from Shopify (and also do my REST API calls manually using fetch without a wrapper). No cookies involved and I have never had issues. I, like Martin, have also asked multiple times if there's an issue with doing it this way and haven't been told this is vulnerable in some way. And now that I'm facing some unknown cookies issue that I can't debug and is causing my app to not be approved, I'm kicking myself for having leaned on this library.
The problem is how are your REST API calls authenticated?
Hi @BGilbert thanks for the quick response - not sure if this is answering your question specifically, but I do this:
Unfortunately with what explained, anyone can just call the REST API calls easily to manipulate other shops by just knowing the other shops .myshopify.com URL
-----------------------------------------
Unrelated to the above, I'm trying to use the ES5 AppBridge but my call to getSessionToken throws an error:
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="https://unpkg.com/@shopify/app-bridge"></script>
<script src="https://unpkg.com/@shopify/app-bridge-utils"></script>
<script>
var AppBridge = window['app-bridge'];
var createApp = AppBridge.createApp;
var actions = AppBridge.actions;
var Redirect = actions.Redirect;
var appBridgeUtils = window['app-bridge-utils'];
var app = createApp({
...
});
appBridgeUtils.getSessionToken().then(function (result) {
console.log(result);
});
</script>
Uncaught (in promise) TypeError: Cannot read property 'subscribe' of undefined
at app-bridge-utils:1
Any ideas? @Michael_Ragalie @Liam
@BGilbert you're right, I just re-read what I wrote and realized I should put a second hmac validation check for each and every server side request (and probably validate the timestamp is current as well). I think should handle it though?
Unfortunately since the subsequent request come from your own website, they won't have any of the HMAC stuff.
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.