Getting session token in axios intercept

Malthe
Tourist
7 0 3

Hey everyone!

I'm building an embedded app with Next.js as my frontend, and Next.js api routes as my serverless backend. 

I have set my backend up to accept and verify JWT session tokens, which seems to work fine. However my problem lies in my way of sending authenticated requests from my frontend to my backend.

I have set up the app  bridge and was using getSessionToken(app) function to  get the current JWT, but since that is only valid for one minute I have to request a the newest one on every request.

Since I'm using axios for requests to my backend, I followed this tutorial: https://shopify.dev/tutorials/use-session-tokens-with-axios. But after implementing that, I'm getting the following errors on my frontend: TypeError: Cannot read property 'subscribe' of undefined.

This is the exact code for my axios intercept:

import axios from 'axios';
import { getSessionToken } from "@shopify/app-bridge-utils";


const instance = axios.create({
    baseURL: process.env.DEV_TUNNEL_URL,
    headers: {
        post: { // Can be common or any other method
            'Content-Type': 'application/json'
        },
        patch: {
            'Content-Type': 'application/json'
        },
        delete: {
            'Content-Type': 'application/json'
        },
    }
});

// Intercept all requests on this axios instance
instance.interceptors.request.use((config) => {
    console.log(window.app)
    return getSessionToken(window.app) // requires an App Bridge instance
        .then((token) => {
            // Append your request headers with an authenticated token
            config.headers["Authorization"] = `Bearer ${token}`;
            return config;
        });
});

 

I also noticed that window.app is undefined. How can I get the correct app reference in a react app using axios intercept?

 

I hope you can help me shin some light on this (-:

Thanks very much in advance!

Replies 16 (16)

Mircea_Piturca
Shopify Partner
1547 44 345

Hey there,

Yeah, I've been playing with this all day today. Got the same error, not defined.

I've ended up creating an app by passing the API_KEY and shopOrigin on _app.js

  const app = createApp({
    apiKey: API_KEY,
    shopOrigin: shopOrigin
  });

 

I have both of those vars defined in _app.js. Looks like a hack but it's the only way I got it to work.

The docs are a bit confusing to me. 

Finally—Add variant descriptions to your products
JoshHighland
Shopify Expert
175 10 65
import axios from 'axios';
import { useAppBridge } from '@shopify/app-bridge-react';
import { getSessionToken } from '@shopify/app-bridge-utils';

const app = useAppBridge();

 

You can use useAppBridge to gain access to app also.

SEO Manager - The FREE all-in-One SEO solution for Shopify
A powerful suite of SEO tools that gets you found in Google results

- Install SEO Manager -
Malthe
Tourist
7 0 3

Thank you for your answer! Will this also work outside of a react component?

 

HerculesApps
Shopify Partner
19 1 14

Hey @Malthe, did u get this work now?

Malthe
Tourist
7 0 3

Hey. No not exactly I'm afraid. 

I ended up using the authenticatedFetch function that app bridge provides. But I would prefer to use axios to be honest. Let me know if you figure it out (-:

HerculesApps
Shopify Partner
19 1 14

I could get it to work in that way:

File: _app.js

 

function MyProvider(props) {
  const app = useAppBridge();

  // Create axios instance for authenticated request
  const authAxios = axios.create();
  // intercept all requests on this axios instance
  authAxios.interceptors.request.use(function (config) {
    return getSessionToken(app)
      .then((token) => {
        // append your request headers with an authenticated token
        config.headers["Authorization"] = `Bearer ${token}`;
        return config;
      });
  });

  const Component = props.Component;

  return (
      <Component {...props}  authAxios={authAxios} />
  );
}

 

Im passing authAxios down to Component  and use axios in that way, when i need authenticated requests. 

 

  authAxios.get('/api/products')
      .then(result => console.log(result))
      .catch(error => console.log(error))

 

Hope it helps.

 

Malthe
Tourist
7 0 3

Awesome! I will give that a try, thank you.

Bradyango
Visitor
1 0 0

Evening Sir!

Any luck on this? 

We are facing a very similar issue. 

scole954387
Shopify Partner
20 1 10

I'm very new to react/node.  I tried to use what you provided with the shopify CLI boilerplate app and it doesn't seem to work.  Can you tell me what I've done wrong?  Thanks!

 

function MyProvider(props) {
  const app = useAppBridge();

  // Create axios instance for authenticated request
  const authAxios = axios.create();
  // intercept all requests on this axios instance
  authAxios.interceptors.request.use(function (config) {
    return getSessionToken(app)
      .then((token) => {
        // append your request headers with an authenticated token
        config.headers["Authorization"] = `Bearer ${token}`;
        return config;
      });
  });


  const client = new ApolloClient({
    fetch: authenticatedFetch(app),
    fetchOptions: {
      credentials: "include",
    },
  });

  const Component = props.Component;

  return (
    <ApolloProvider client={client}>
      <Component {...props} authAxios={authAxios} />
    </ApolloProvider>
  );
}
dgtlmonk
Shopify Partner
38 3 7

i'm getting 'jwt expired' error

nkm22
Visitor
1 0 0

we're having a similar issues using axios interceptor. Any update. Thanks.

Avi_Ben_Zaken
Shopify Partner
18 0 6

Hey, thanks for this explanation.

can you please show a fully page code that use the authAxios?

Tomasz88
Visitor
1 0 0

Hi HerculesApps, 

 

I've applied your solution, but I'm getting a 302 status and HTML as a response when I make a API call request front side. Do you have any idea of what is going on ?

iozyigit
Shopify Partner
28 2 2

Seems like hook can be used in functional component.

the question is to use in app instance in the interceptor for the Axios instance. there is no component here and you can not use probably

const app = useAppBridge()

https://www.linkedin.com/in/ismailozyigit/
iozyigit
Shopify Partner
28 2 2

Here is how solve it.  Since I can use hook here I did use "app = useAppBridge()" and add JWT token to header and pass header to Axios instance. useFetchDataApiShopify is kind of single point to make api call. I was trying to use interceptor to add token to header but I was not able to use useAppBridge in the interceptor.

 

 

 

mport React from 'react';
import AxiosShopify from '../../AxiosShopify';
import { useAppBridge } from '@shopify/app-bridge-react';
import { getSessionToken } from '@shopify/app-bridge-utils';

const config = {
	headers: {
		'Content-Type': 'application/json',
		Accept: 'application/json',
	},
};
/*
this is custom hook that fetch data from my backend
*/
export const useFetchDataApiShopify = (url) => {
	const app = useAppBridge();
	const [responseData, setResponseData] = React.useState(undefined);
	const [isError, setIsError] = React.useState(undefined);
	const [isLoading, setIsLoading] = React.useState(undefined);

	React.useEffect(() => {
		const fetchData = async (url) => {
			setIsLoading(true);
			try {
				const token = await getSessionToken(app);
				config.headers['Authorization'] = `Bearer ${token}`;
				const result = await AxiosShopify.get(url, config);
				setResponseData(await result.data);
				console.log('set response fetch11', result);
				setIsLoading(false);
			} catch (error) {
				//	console.log('set response fetch error', error);
				setIsError(error);
			}
		};

		fetchData(url);
	}, [url]);
	return [responseData, isError, isLoading];
};

//*****Interceptor file became like this *******

mport React from 'react';
import axios from 'axios';
import { getSessionToken } from '@shopify/app-bridge-utils';

const AxiosShopify = axios.create();
AxiosShopify.interceptors.request.use(function (config) {
	// return getSessionToken(window.app) // requires a Shopify App Bridge instance
	// 	.then((token) => {
	// 		// Append your request headers with an authenticated token
	// 		config.headers['Authorization'] = `Bearer ${token}`;
	// 		return config;
	// 	});
	return config;
});
// Export your Axios instance to use within your app
export default AxiosShopify;

 

 

 

 

https://www.linkedin.com/in/ismailozyigit/

uhon
Shopify Partner
1 0 0

I'm using Axios this way:

 

const axios = userLoggedInAxios(app);

axios('url', {/* your options */}

 

 

This has identical behavior as userLoggedInFetch, showcased in shopify-app-template-node 

 

Here the required module:

 

/**
 * Adds Shopifys Authorization Header to Axios requests through the use of a request interceptor
 *  app Shopify app instance
 * @returns AxiosInstance
 */
function authenticatedAxios(app) {
  const axiosShopify = axios.create();
  axiosShopify.interceptors.request.use((config) =>
    getSessionToken(app) // requires a Shopify App Bridge instance
      .then((token) => {
        // Append your request headers with an authenticated token
        config.headers["Authorization"] = `Bearer ${token}`;
        return config;
      })
  );
  return axiosShopify;
}

/**
 * Assures that Shopify Request is made for a logged-in user through the use of a response interceptor
 * It uses Shopifys redirect mechanism to redirect unauthenticated user-requests to the '/auth' endpoint
 *  app Shopify app instance
 * @returns AxiosInstance
 */
export function userLoggedInAxios(app) {
  const authAxios = authenticatedAxios(app);
  authAxios.interceptors.response.use((response) => {
    if (
      response.headers.get("X-Shopify-API-Request-Failure-Reauthorize") === "1"
    ) {
      const authUrlHeader = response.headers.get(
        "X-Shopify-API-Request-Failure-Reauthorize-Url"
      );

      const redirect = Redirect.create(app);
      redirect.dispatch(Redirect.Action.APP, authUrlHeader || "/auth");
      return null;
    }
    return response;
  });
  return authAxios;
}