FROM CACHE - en_header

Introducing cookieless authentication beta with App Bridge

Liam
Shopify Staff
Shopify Staff
690 19 265

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
4812 58 570

You are not loading an authenticated session inside the iframe. Shopify is hoisting you into an iframe but it cannot hoist your session in there, without using cookies. Anyway, if you are happy and your App works, why worry. If you are actually leaking and not secure, someone will hack you and or inform you and you'll fix things up later. I am sure you are fine though. It is tough to screw up and get away with it.

Custom Shopify Apps built just for you! hunkybill@gmail.com http://www.resistorsoftware.com
releod
Tourist
9 0 8

I agree with HunkyBill here. It may also be worth hearing about how you've accomplished this.. worst case we can poke some security holes in your setup, best case we can learn something new.

Martin_Caum
Shopify Partner
40 3 22

Absolutely. If I have any concern it is that I am doing something insecure. But I think I have thought it all out pretty well. Feel free to pick it apart. I actually wanted to work in PHP for multiple reasons (main one being I tried getting started with the tutorials out there for Ruby with Shopify's gem and I ended up with a 96MB app that just authenticated (and barely did that even) before I even started coding out the actual functionality of the app). The only PHP app I found was outdated and did not work with Shopify's current Oauth and API, so I wrote my own bare-bones PHP app and put it on GitHub. It includes all steps to get it up and running easily as well as the added security measures I spoke about in my first comment on here. Check it out here and let me know what you think or if there are any flaws you find in it: https://github.com/XenithTech/php-shopify-app-skeleton

Martin_Caum
Shopify Partner
40 3 22

Also, I have not gone through and commented anything yet so feel free to ask me either on here or GitHub about what any of it is doing. But it should be mostly straightforward.

HunkyBill
Shopify Expert
4812 58 570

I agress on the bloat. I used Sinatra for years to avoid it. But you have to also remember this. That bloat buys you code battle-tested and proven, code you eventually need, and code you cannot hope to replicate yourself. Rare is the person that writes an App these days without some kind of framework. So while it is true you get little out of the box for your initial foray into Rails, for just a few extra minutes of work, you could put into play a useful API call pattern, that is battle-tested for the same 96MB. Good luck rolling your own there. Also PHP? Hardcore hanging on to yesteryear! It is still a thing for sure, but you won't stumble across anything nice for Shopify. Stick with Ruby, or Python (or even gasp, Node) for the most current community efforts. And don't discount the fact that Shopify's gems for App and API are always going to expose cutting edge before anything else by default. PHP will almost always be your long distant cousin on a dial-up modem with electricity only 4 hours a day, from a bicycle feeding a battery when the sun does not shine. 

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

I definitely get what you are saying about having the pre built out API but the Admin API for Shopify is very well documented and very straightforward as far as the calls being made. The wrapper API doesn't seem to give me much advantage overall. As I go and need to make calls I am going to be building on this PDP bare-bones app to have a similar built in API structure of reusable functions but I also gain a lot out of doing this by hand in way of learning. Now the choice of PHP is for a whole different discussion. I would argue (as an active professional web developer) though that PHP is actually by no means a dying language. The new versions of PHP have fixed a lot of old performance concerns and between just WordPress and Magento alone, there are thousands upon thousands of sites written in PHP that will likely never be rewritten to another language. That being said, I am familiar with Node and may give that a shot at some point. 

HunkyBill
Shopify Expert
4812 58 570

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
releod
Tourist
9 0 8

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

 

rezaansyed
Shopify Staff
Shopify Staff
7 0 3

@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

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
4812 58 570

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
shophelp20
Shopify Partner
11 0 4

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:

Dashboard.png

What are we doing wrong? We just want to grab the session token to replace the cookies for authenticated AJAX calls.

HunkyBill
Shopify Expert
4812 58 570

I think this thread needs to be closed and have more specific new threads dedicated to specific problems. Just my 0.02.

Custom Shopify Apps built just for you! hunkybill@gmail.com http://www.resistorsoftware.com
shophelp20
Shopify Partner
11 0 4

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

KisukaKiza
Shopify Partner
38 0 31

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.

MathewsJoseph
Shopify Partner
4 0 3

Hey Michael! I have created a complete app using NodeJs, React, and MongoDB. You can check that out on:
https://github.com/MathewsJoseph25/create-shopify-app

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 31

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