Introducing cookieless authentication beta with App Bridge

Liam
Shopify Staff
Shopify Staff
1315 119 426

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

Replies 133 (133)
HunkyBill
Shopify Expert
4839 60 595

What is this wrapper API you speak of? I am not familiar with referring to anything as a wrapper API

Custom Shopify Apps built just for you! hunkybill@gmail.com http://www.resistorsoftware.com
Martin_Caum
Shopify Partner
40 3 22

@HunkyBill Wrapper API refers to an API that wraps around another one. In other words, a class/object that contains functions taking in arguments to make calls to the REST API and returns an object native to that language. Something like `$products = $Store->getProduct('29249471');`.

HunkyBill
Shopify Expert
4839 60 595

Thanks @Martin_Caum I get it!

Kind of a precocious thing to say though, as almost everyone in conversation just makes API calls. Hence why it can be a head-scratcher for a second. You did what?

If you used PHP, sure it looks different than Ruby or Javascript code would, because they are different languages with unique syntaxes. I guess the years have dulled my ability to just let it go. Of course ActiveResource is the common Ruby wrapper around making Rest API calls. I would never refer to it as such, as a Wrapper API, but that is succinct in case it were up for debate with someone, sure!

 

Custom Shopify Apps built just for you! hunkybill@gmail.com http://www.resistorsoftware.com
KisukaKiza
Shopify Partner
38 0 33

For those trying to figure out how to implement this having built your app using the Node + Koa + Next + Apollo tutorial featured on Shopify:

  • Apollo Boost was deprecated in July 2020. You should remove boost and install the following instead:
    • apollo-client
    • apollo-link-http
    • apollo-cache-inmemory
    • @Apollo/react-hooks
  • You'll also want to replace @shopify/app-bridge-react with @shopify/app-bridge. Create the app bridge in your _app.js using the createApp function. Then pass that to authenticatedFetch as the fetch operation function for HttpLink. Set credentials on the HttpLink to 'omit'.

  • Create the apollo client by using apollo-client, pass the HttpLink to the apollo client as a link. Set cache to InMemoryCache.

  • Using the apollo provider from @Apollo/react-hooks, set the apollo client prop.

For the server-side. You will need to remove the Koa Auth package and write your own oauth method. You can simply take the koa auth package and strip out the parts that do a bunch of cookie things before it's oauth process begins.

You'll want to perform the JWT verification on your /graphql endpoint for your graphql proxy. You will also need to store the store's access token you get during the oauth callback in a database and retrieve that access token to make the call to graphQL.

The basic flow:

  • Merchant installs or visits your app, /auth is visited. This triggers the start of your oAuth process, you need to generate an auth URL. You can use the shopify-token package to do this.

  • Once approved, the merchant is sent to your redirectUri / Callback. On here you store the merchant's access token to your database. If it's an offline token it lasts forever, if it's online it expires after 24 hours currently. Set an expire time, you will also wanna store the user's user id if it's an online token. At this stage you also register your webhooks and perform your shop plan / charge checks. At the end of this process you should perform redirects to your app homepage.

  • When you perform a graphQL query on your frontend, it makes a call to /graphql on your app, sending the JWT token with it in an authorization header. On the server send, you will want to either replace the graphQL proxy middleware with your own or perform a check beforehand. You will want to validate the JWT token, if it's valid you grab the latest access token for that store or user. You can pull it from your DB using the 'sub' variable within the JWT payload for user_id, or can use the shop variable.

  • Once you pull the access token from your database for that store / user, pass it to your graphQL proxy function. You should pass the shop and access token to this function. This will perform the graphql query on the shop using the access token. It'll then return the results to the frontend.

Some other things to be aware of: You may want to perform a "check if this is a known shop" anytime a shop accesses your app. You perform a query to see if they have any valid access tokens in your database. If they're not a known shop, pass them to the oauth start process.

Regarding "How do I get the shop parameter into my frontend?" You don't have to pass it via a cookie. Anytime a merchant accesses your app, a shop query parameter is passed with it. If you are creating links within your frontend, make sure you are also appending the shop query parameter to the end of your links using that query parameter.

dsingh
Shopify Partner
13 0 3

I was playing around with the JWT tokens issued by Shopify. This seems like a great solution to get around the recent 3rd party cookies issues. However, I do have a few questions.

  1. I'm guessing that this only supports online tokens? When I decode the JWT token I'm getting a user id and I think partners get shop id when requesting offline tokens? (Please correct me if I'm wrong)
  2. Are we supposed to match against shop's myshopify domain? If so, maybe getSessionToken() should support a parameter to receive authenticated shop id instead?

Can you please provide some clarification on this?

KisukaKiza
Shopify Partner
38 0 33

@dsingh JWT is separate from the access token. You generate the access token, online or offline, during your oauth callback and handle storing it in your db depending on if it's online or offline. You always should be storing the myshopify domain imo. If you are doing offline tokens, match it up with the access token via the domain name and make your shop domain column in your db a unique constraint. Basically just ignore the sub field from jwt if offline. The sub is always the user id of user who that jwt token is for.

dsingh
Shopify Partner
13 0 3

@KisukaKiza My concern is security related here. i.e. Are the shopify domains recycled? or changed? If answer to either of those questions is yes then it'll break the authentication process if we're matching based on shopify domain.

dsingh
Shopify Partner
13 0 3

@Liam FYI ^^ Please see my post above.

KisukaKiza
Shopify Partner
38 0 33

@dsingh In the 5 years I've used Shopify I've never seen a myshopify url changed or recycled into a new store. When you try to signup using one that was ever used before it gives an error. Even if it were recycled at some point, in that scenario, you should be processing the web hook you receive a few days after any app is uninstalled and wipe out all the data for that store. 

HunkyBill
Shopify Expert
4839 60 595

A few days! Heck, it should be dealt with prompter than that. When a shop closes, and Shopify sends the App Uninstalled webhook, we're talking mere minutes at the most! Why wait around days! It is likely that recycling those names is possible, but I would not stress on it either, as you always start with a fresh slate of name, and ID's if you're doing it right.

I think I still own flowers.myshopify.com and cars.myshopify.com if anybody wants... haha. Going back to 2006 I bet. When it failed to occur to me that shop domain name matters for nothing save it is the unique ID of the shop.

Custom Shopify Apps built just for you! hunkybill@gmail.com http://www.resistorsoftware.com
dsingh
Shopify Partner
13 0 3

@HunkyBill Agreed, I'd prefer to use the shop id (just to be certain). But We can't depend on callbacks for data security. Callbacks fail, endpoints can be unavailable, etc. FWIK I think that JWT only gives us the user id for the active user and the shop url/domain. That probably isn't the best information to authenticate using JWT. Does that makes sense?

HunkyBill
Shopify Expert
4839 60 595

I am not going to argue for or against JWT. If a couple of billion dollar companies decided it was OK to use them, and the code provided to use them was not written by me, but by experts in that domain, I leave it to them that it just works. Rolling your own always involves mistakes and holes, so I leave you to decide for yourself what you do. I just know that webhooks are retried till they succeed, or the webhook is deleted because the App never responded in a good many days.

 

Custom Shopify Apps built just for you! hunkybill@gmail.com http://www.resistorsoftware.com
dsingh
Shopify Partner
13 0 3

@HunkyBill I'm not arguing for or against JWT.  There is no issue with that. What I'm saying is that maybe Shopify should pass the Shop ID, instead of User ID in the JWT token. We(partners) don't receive the User ID during the OAuth process. The ID which we get through OAuth is the Shop ID (AFAIK).

OAuth

- We're receiving Shop ID and Shop Domain

JWT Auth

- We're receiving User ID and Shop Domain

HunkyBill
Shopify Expert
4839 60 595

Ok cool. I am surprised Shopify would screw up the basics, but it what you're pointing out is a basic screwup, then best it get sorted now, before everyone gets pumped and primed and then lost due to this. I think you can setup a flow where you do get a UserID, but certainly not everyone uses that, so it remains a fringe benefit, not the main course meal. 

Custom Shopify Apps built just for you! hunkybill@gmail.com http://www.resistorsoftware.com
KisukaKiza
Shopify Partner
38 0 33

I think you both aren't understanding the point of JWT. It's stateless authentication. It has no knowledge if you are storing offline or online access tokens behind the scenes. A JWT sub will always be a user id because it's the user who is invoking the action. If it's an offline token, simply use the domain to pull the token via the domain. myshopify domains don't recycle and cannot be changed. You can add an uninstall webhooks that trigger right at uninstall and you also receive revoke webhooks automatically if you don't have an uninstall webhook. You're making it way more confusing than it needs to be.

Offline token? Use `dest` for the check to grab the offline token.

Online token? Use `sub`.

Hell you could even make another table that associates user_ids with a specific domain.

HunkyBill
Shopify Expert
4839 60 595

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.

Custom Shopify Apps built just for you! hunkybill@gmail.com http://www.resistorsoftware.com
dsingh
Shopify Partner
13 0 3

@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.

 

 

KisukaKiza
Shopify Partner
38 0 33

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.

KisukaKiza
Shopify Partner
38 0 33

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.

 

HunkyBill
Shopify Expert
4839 60 595

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.

 

Custom Shopify Apps built just for you! hunkybill@gmail.com http://www.resistorsoftware.com
Michael_Ragalie
Shopify Staff
Shopify Staff
38 2 12

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.

dsingh
Shopify Partner
13 0 3

@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. 

Michael_Ragalie
Shopify Staff
Shopify Staff
38 2 12

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.

KisukaKiza
Shopify Partner
38 0 33

@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. 

composed
Excursionist
10 0 5

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:

https://community.shopify.com/c/Shopify-APIs-SDKs/Introducing-cookieless-authentication-beta-with-Ap...

Michael_Ragalie
Shopify Staff
Shopify Staff
38 2 12

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.

BGilbert
Shopify Expert
30 0 15

Is it possible to use this in the old Embedded App SDK?

Michael_Ragalie
Shopify Staff
Shopify Staff
38 2 12
This is only available via App Bridge. Another reason to upgrade! 🙂

To learn more visit the Shopify Help Center or the Community Blog.

policenauts1
Trailblazer
174 13 39

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.

BGilbert
Shopify Expert
30 0 15

The problem is how are your REST API calls authenticated?

policenauts1
Trailblazer
174 13 39

Hi @BGilbert thanks for the quick response - not sure if this is answering your question specifically, but I do this:

  1. Request comes in through my app - validate hostname and hmac validity. If it passes, they can proceed to the actual app 
  2. At that point, during the app session the user can perform certain app actions which will trigger requests to my server side
  3. In each request payload to my server side, I include their shopOrigin and use this to look up their access token (which I store in a secure db) which I'll then use to make fetch requests to the Shopify Admin API
  4. And finally, I'll send back the API response (usually manipulated with server side business logic) 

 

 

BGilbert
Shopify Expert
30 0 15

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 

policenauts1
Trailblazer
174 13 39

@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?

BGilbert
Shopify Expert
30 0 15

Unfortunately since the subsequent request come from your own website, they won't have any of the HMAC stuff.

policenauts1
Trailblazer
174 13 39

@BGilbert I'll message you privately so as not to clutter up this thread!