How should theme app extension get session token from the online store user's logged in session

Hi, I have a theme app extension that should detect when a user logged in to the online store powered by Shopify.

Here is a diagram of what I want to do

This is an online store powered by Shopify. The store has its own login capability. It could be Google login, facebook login or just username password login. Now when a user logs in, I need the theme app extension I added to the store to know that and somehow get a session jwt token and use authenticated api call to the backend. The backend in my case is a graphql api endpoint. When the user logs off, the theme app extension should know that as well and stop calling the authenticated api and start calling the non-authenticated api only.

I read that I should use app bridge to get session token but if I want to get a logged in session token from the online store, does the same approach still applies?

Can someone give me some pointers on how to achieve this?

Thanks!

Hi there :waving_hand:

You won’t be using App Bridge and session tokens for authenticating customers on the online store. Those mechanisms are for authenticating merchants when they are using an app’s merchant facing UI.

Instead I would recommend researching the Liquid Custom object. When a customer is logged into the Shopify store this globally accessible object will be populated with the customer data.

As well as App Proxies. This allows you to forward requests made to Shopify Domains to external sources like your GraphQL API endpoint. The forwarded request will include the ID of the logged in customer if one exists.

Depending on your exact use case you should be able to use either one or both of these methods.

Hi @lizk thanks for the reply. I have a GraphQL api backend that both the theme app extension and embedded app should call. These api calls can only be made if they are authenticated. Now if I only get customer id and not a jwt session token, will that be enough? Or should I separate the resolvers called by embedded app and the resolvers called by theme app extension?

Also, do you mind explaining app proxy a bit more? When you said Shopify domain, do you mean the online store’s domain? Don’t those stores usually have their own domain name? And if possible, would you be able to provide some code examples?

Thank you a ton!!!

1 Like

Hi there!

If you have a chance I would definitely recommend reading the page in the Shopify.dev documentation on App Proxies, it should answer most of your questions.

When you said Shopify domain, do you mean the online store’s domain?

App proxies take requests to Shopify links, and redirect them to external links. For example [https://johns-apparel.myshopify.com/apps/my-app-proxy/app_path](https://johns-apparel.myshopify.com/apps/my-app-proxy/app_path) forwards to the proxy URL at [https://my-app-proxy.com/app_proxy/app_path](https://my-app-proxy.com/app_proxy/app_path). That second URL can be the route of your GraphQL endpoint.

In your Theme App extension you can use the liquid customer property. If the customer property is truthy then you know there is a logged in customer. You can then render a theme app extension for a logged in customer. This would include submitting to your authenticated endpoint.

Otherwise you could render a theme app extension that would submit to your unauthenticated graphql endpoint.

Now if I only get customer id and not a jwt session token, will that be enough?

To verify that the request came from Shopify, you need to compute the HMAC digest according to the SHA-256 hash function and compare it to the value in the signature property. If the values match, then the request was sent from Shopify.

This is one possible way that you could implement your functionality.

1 Like

I have a problem regarding the App Proxies documentation.

The document says

When the following HTTP request is sent from the client, Shopify forwards the request using the specified proxy URL:

```markup
GET /apps/awesome_reviews/extra/path/components?extra=1&extra=2 HTTP/1.1
  Host: shop-domain.com
  Cookie: csrftoken=01234456789abcdef0123456789abcde;
  _session_id=1234456789abcdef0123456789abcdef;
  _secure_session_id=234456789abcdef0123456789abcdef0

The forwarded request looks like the following:

GET /proxy/extra/path/components?extra=1&extra=2&shop=shop-name.myshopify.com&logged_in_customer_id=1&path_prefix=%2Fapps%2Fawesome_reviewsĂ—tamp=1317327555&signature=4c68c8624d737112c91818c11017d24d334b524cb5c2b8ba08daa056f7395ddb HTTP/1.1
  Host: proxy-domain.com
  X-Forwarded-For: 123.123.123.123

It then says there should be a field called "logged_in_customer_id" in the request.

In my code, because I have a GraphQL backend rather than REST backend, all of the requests should be a POST request:

```javascript
function validateAuthenticatedCall() {
  const QUERY_ALL_GARMENT_DIMENSIONS = `
  query{
    garmentDimensions {
      size
      measurements
    }
  }`;

  const requestOptions = {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      // Add any other headers, such as authorization, if needed
    },
    body: JSON.stringify({
      query: QUERY_ALL_GARMENT_DIMENSIONS
    }),
  };

  fetch('/apps/api/graphql', requestOptions)
    .then(response => {
      if (!response.ok) {
        throw new Error(`Network response was not ok: ${response.status}`);
      }
      return response.json();
    })
    .then(data => {
      console.log('Data:', data);
      const finalSize = document.getElementById("body-measurement");
      finalSize.textContent = JSON.stringify(data)
    })
    .catch(error => {
      console.error('Fetch Error:', error);
    });
}

For App Url and App Proxies settings, the App URL is https://peterpot-store.myshopify.com, just my dev store’s url and app proxies settings are like this

The graphql backend runs on localhost of my machine so I use ngrok to expose it. When I execute the validateAuthenticatedCall function in my frontend, I don’t see a “logged_in_customer_id” in the request header. I also don’t see an additional forwarded traffic under the network tab of my browser’s developer tool.

I’m not quite sure what I did wrong here. Do you mind sharing some insights to my configuration?

Thanks!

1 Like

I finally was able to validate customer login by adding the HTTP request to the GraphQL context. The app module of my NestJS GraphQL backend looks like this now:

NestGraphQLModule.forRootAsync

And I followed the App Proxies document to install the

"shopify-application-proxy-verification" package and import it with

```javascript
import { verifyAppProxyHmac as queryHasValidSignature } from 'shopify-application-proxy-verification';​

and then use the validation function like this:

@Query(() => [GarmentDimension])
  async books(@Context() context: any): Promise

I forgot that I need to make changes in my backend and not my theme app extension. And that the request isn't passed to graphql resolver by default.

With this working I can go back and visit the authentication for my admin app.

 @lizk thanks again for your helps!!