Introducing cookieless authentication beta with App Bridge

HunkyBill
Shopify Expert
4509 46 492

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

0 Likes
HunkyBill
Shopify Expert
4509 46 492

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
0 Likes
shophelp20
Excursionist
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

0 Likes
KisukaKiza
Shopify Partner
33 0 28

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 1

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
33 0 28

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

0 Likes
dsingh
Shopify Partner
13 0 1

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

0 Likes
dsingh
Shopify Partner
13 0 1

@Liam FYI ^^ Please see my post above.

0 Likes