Apollo Client sending requests with expired session tokens
Issue summary
So i'm using the boilerplate code in the repo for ApolloClient
so the main code is
function MyProvider({ children }) {
const app = useAppBridge();
const client = new ApolloClient({
cache: new InMemoryCache(),
link: new HttpLink({
credentials: "include",
fetch: userLoggedInFetch(app),
}),
});
return <ApolloProvider client={client}>{children}</ApolloProvider>;
}
export function userLoggedInFetch(app) {
return async (uri, options) => {
const fetchFunction = authenticatedFetch(app);
const response = await fetchFunction(uri, options);
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;
};
}
Now when i run any query it fails giving an error of token expired, weird since i'm using the function userLoggedInFetch() to make a api call to express using
async function createDraftOrderJob(app, data) {
const fetch = await userLoggedInFetch(app);
return fetch("api/createDraftOrderJob", {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify(data),
});
}
now this works but the apollo client does not, not sure what i'm doing wrong
likely the app variable you're passing in lacks the required context. Also you're returning the entire fetch object not the response. Also userLoggedInFetch isn't async by default. Try something like
//MySuperCoolComponent.js
import { useAppBridge } from "@shopify/app-bridge-react";
import { userLoggedInFetch } from "../App";
export default function MySuperCoolComponent(){
const app = useAppBridge();
const fetch = userLoggedInFetch(app);
async function createDraftOrderJob(data) {
fetch("api/createDraftOrderJob", {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
body: JSON.stringify(data),
}
}
}
}
But I think what's most important here is the key understanding what this code is doing, if you understand that you can do this however you want, ideally leveraging axios or react-query.
Shopify requires a Bearer token for request authentication in the form of a JSON Web Token (JWT).
All userLoggedInFetch is doing is adding an /auth redirect on failure and calling authenticatedFetch from @shopify/app-bridge-utils, and all that function is doing essentially is making use of the getSessionToken function and attaching that Bearer token to all requests. So at the end of the day all you need is that JWT header on the request. Here is an example of a custom implementation using fetch and axios
fetch
const authFetch = async (url, options = {}) => {
const newToken = await getSessionToken(window.app);// stored in window variable on mount
const updateOptions = (newOptions) => {
const update = { ...newOptions };
update.headers = {
...update.headers,
Authorization: `Bearer ${newToken}`,
};
return update;
};
return fetch(url, updateOptions(options));
};
axios
import axios from "axios";
import { getSessionToken } from "@shopify/app-bridge-utils";
// This client is used to make authenticated requests from the app frontend, to the server.
const createServerClient = (useAPIUrl = true) => {
let baseURL = "/api/v1";
if (!useAPIUrl) {
baseURL = "";
}
const app = window.app; // stored in window variable on mount
const serverClient = axios.create({
baseURL,
timeout: 100000,
});
serverClient.interceptors.request.use(function (config) {
return 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 serverClient;
};
export default createServerClient;
Hope that helps, close out the issue if it does. Thanks
This issue is stale because it has been open for 90 days with no activity. It will be closed if no further action occurs in 14 days.
We are closing this issue because it has been inactive for a few months. This probably means that it is not reproducible or it has been fixed in a newer version. If it’s an enhancement and hasn’t been taken on since it was submitted, then it seems other issues have taken priority.
If you still encounter this issue with the latest stable version, please reopen using the issue template. You can also contribute directly by submitting a pull request– see the CONTRIBUTING.md file for guidelines
Thank you!