Introducing cookieless authentication beta with App Bridge

Liam
Shopify Staff
Shopify Staff
1347 126 431

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)
Michael_Ragalie
Shopify Staff
Shopify Staff
38 2 12

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.

releod
Tourist
9 0 5

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

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

rezaansyed
Shopify Staff
Shopify Staff
7 0 3

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.

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
4839 60 357

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

dsingh
Shopify Partner
13 0 3

---- removed ----

KisukaKiza
Shopify Partner
38 0 15

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

dsingh
Shopify Partner
13 0 3

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.

jam_chan
Shopify Partner
826 20 141

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

  1. My test page with App Bridge under my URL. (Load skeleton like the doc said?)
  2. App Bridge force redirects me to Shopify admin (cannot create app with App Bridge here since the origins are unmatched)
  3. It redirects to https://mystore.com/admin/apps/appID/login/?hmac=a668c39fd49ca4ff80ce38dee3ae213072270ce8250188e9605... and gives 404, where [login] is the handle to my app url
  4. Stop here

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?

BYOB - Build Your Own Bundles, SPO - SEO App to research keywords & edit social link preview
Adriano_Corte_R
Shopify Partner
6 0 8

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.

 

jam_chan
Shopify Partner
826 20 141

Hi @Adriano_Corte_R 

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. 

BYOB - Build Your Own Bundles, SPO - SEO App to research keywords & edit social link preview
Adriano_Corte_R
Shopify Partner
6 0 8

@jam_chan 

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?

jam_chan
Shopify Partner
826 20 141

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.

BYOB - Build Your Own Bundles, SPO - SEO App to research keywords & edit social link preview
policenauts1
Trailblazer
174 13 24

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

Michael_Ragalie
Shopify Staff
Shopify Staff
38 2 12

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.

policenauts1
Trailblazer
174 13 24

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

Michael_Ragalie
Shopify Staff
Shopify Staff
38 2 12

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.

policenauts1
Trailblazer
174 13 24

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

aedr
Visitor
1 0 1

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.

Michael_Ragalie
Shopify Staff
Shopify Staff
38 2 12
I think you'll need to use App Bridge to get a token. The "manual" approach
is still using App Bridge, just lower level aspects of it.

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

optizio
Shopify Partner
7 0 2

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.

tolgapaksoy
Shopify Partner
103 7 50

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.

optizio
Shopify Partner
7 0 2

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?

policenauts1
Trailblazer
174 13 24

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

 

policenauts1
Trailblazer
174 13 24

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

optizio
Shopify Partner
7 0 2

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

policenauts1
Trailblazer
174 13 24

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

HunkyBill
Shopify Expert
4839 60 357

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.

Custom Shopify Apps built just for you! hunkybill@gmail.com http://www.resistorsoftware.com
optizio
Shopify Partner
7 0 2

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

optizio
Shopify Partner
7 0 2

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 😉

HunkyBill
Shopify Expert
4839 60 357

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.

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

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

 

JoshHighland
Shopify Expert
100 2 35

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