App reviews, troubleshooting, and recommendations
Using shopify cli I have created boilerplate code to build app using React.
How did you set up your server-side code, especially the part where you're using `shopify.validateAuthenticatedSession()`?
First, make sure you've got the right environment variables set up for redis session storage. Your `.env` file should have these variables.
SHOPIFY_APP_SECRET=<your_app_secret>
SHOPIFY_APP_API_KEY=<your_app_api_key>
SHOPIFY_APP_API_SECRET_KEY=<your_app_api_secret_key>
SHOPIFY_APP_API_SCOPES=<your_app_scopes>
SHOPIFY_APP_IS_EMBEDDED_APP=true
SHOPIFY_APP_HOST=<your_app_host> /* Make sure you set the `SHOPIFY_APP_HOST` to your app's hostname, this is important for the authentication process to work correctly.*/
SHOPIFY_APP_BACKEND_PORT=<your_app_backend_port>
SHOPIFY_APP_FRONTEND_PORT=<your_app_frontend_port>
REDIS_HOST=<your_redis_host>
REDIS_PORT=<your_redis_port>
REDIS_PASSWORD=<your_redis_password>
Your API request from the UI side already has a valid JWT token in the `Authorization` header, so let's update the server-side code to use this token instead of session storage.
replace this
app.use("/api/*", shopify.validateAuthenticatedSession());
with this
app.use("/api/*", shopify.verifyToken());
lastly update your `/api/products/count` endpoint to use the GraphQL API for fetching the product count. Replace the commented-out code with
const { data } = await shopify.api.graphql.query({
session: res.locals.shopify.session,
query: `{
productsCount: products(first: 0) {
count
}
}`,
});
res.status(200).send(data.productsCount.count);
After you've made these changes, your app should be able to grab the data from the GraphQL API just fine and display it on your React UI.
Thank you for your replay with explanation.
I tried and realise that I don’t have shopify.verifyToken() method available.
"@shopify/shopify-app-express": "^2.0.0",
And I am using shopifyApp method from that package like this -
const shopify = shopifyApp({
api: {
apiVersion: LATEST_API_VERSION,
restResources,
billing: undefined, // or replace with billingConfig above to enable example billing
},
auth: {
path: "/api/auth",
callbackPath: "/api/auth/callback",
},
webhooks: {
path: "/api/webhooks",
},
// This should be replaced with your preferred storage strategy
sessionStorage: new RedisSessionStorage(
`redis://${Config.REDIS_HOST_IP}:${Config.REDIS_HOST_PORT}`
),
});
export default shopify;
You can create a utility function to decode and verify the JWT token.
// utils/decodeJWT.js
import jwt from "jsonwebtoken";
const decodeJWT = (token) => {
try {
const decoded = jwt.verify(token, process.env.SHOPIFY_APP_API_SECRET_KEY);
return decoded;
} catch (error) {
return null;
}
};
export default decodeJWT;
and then create a custom middleware to verify the JWT token
// middleware/verifyToken.js
import decodeJWT from "../utils/decodeJWT.js";
const verifyToken = (req, res, next) => {
const authHeader = req.headers.authorization;
if (!authHeader) {
return res.status(401).json({ message: "Unauthorized" });
}
const token = authHeader.split(" ")[1];
const decoded = decodeJWT(token);
if (!decoded) {
return res.status(401).json({ message: "Unauthorized" });
}
res.locals.shopify = { session: { accessToken: decoded.dest_access_token } };
next();
};
export default verifyToken;
import the `verifyToken` middleware in your server-side code
import verifyToken from "./middleware/verifyToken.js";
Replace this line
app.use("/api/*", shopify.validateAuthenticatedSession());
With this line:
app.use("/api/*", verifyToken);
This middleware should now verify the JWT token sent from the client-side and save the access token in `res.locals.shopify.session.accessToken`.
Make sure to restart your server after making these changes.
from above example, what is this part -
decoded.dest_access_token
I am able to decode the token and get data, but I don't have any dest_access_token key in decoded part, I have checked with jwt.io as well, all I have is this -
{
"iss": "xxxx",
"dest": "xxxx",
"aud": "xxxxx",
"sub": "xxxx",
"exp": xxxx,
"nbf": xxxx,
"iat": xxxx,
"jti": "xxxx",
"sid": "xxxx"
}
The `dest_access_token` should be replaced with the appropriate field from the decoded JWT token.
In your decoded JWT token, you have a `sid` field, which might be the session ID. You can use this session ID to retrieve the access token from your Redis storage.
update the `verifyToken` middleware to store the session ID-
// middleware/verifyToken.js
import decodeJWT from "../utils/decodeJWT.js";
const verifyToken = (req, res, next) => {
const authHeader = req.headers.authorization;
if (!authHeader) {
return res.status(401).json({ message: "Unauthorized" });
}
const token = authHeader.split(" ")[1];
const decoded = decodeJWT(token);
if (!decoded) {
return res.status(401).json({ message: "Unauthorized" });
}
res.locals.shopify = { session: { sessionId: decoded.sid } };
next();
};
export default verifyToken;
And then, you need to create a middleware to retrieve the access token from Redis storage using the session ID. You can use the `shopify.sessionStorage` instance you created in your `shopifyApp` configuration.
// middleware/retrieveAccessToken.js
const retrieveAccessToken = async (req, res, next) => {
const sessionId = res.locals.shopify.session.sessionId;
const session = await shopify.sessionStorage.loadSession(sessionId);
if (!session) {
return res.status(401).json({ message: "Unauthorized" });
}
res.locals.shopify.session.accessToken = session.accessToken;
next();
};
export default retrieveAccessToken;
Lastly update your server-side code to use both middlewares-
import verifyToken from "./middleware/verifyToken.js";
import retrieveAccessToken from "./middleware/retrieveAccessToken.js";
// ...
app.use("/api/*", verifyToken, retrieveAccessToken);
The access token should be stored in `res.locals.shopify.session.accessToken` after going through both middlewares.
I am using
"@shopify/shopify-app-express": "^2.0.0",
so sessionStorage does not exists -
shopify.sessionStorage.loadSession(sessionId)
We can fetch data from Redis directly but sessionId does not match with anything. in Redis I can see below data -
"[[\"id\",\"xxxx\"],[\"shop\",\"xxxx\"],[\"state\",\"xxxx\"],[\"isOnline\",false],[\"accessToken\",\"xxxx\"],[\"scope\",\"xxxxx"]]"
I started getting "session token is not valid" after I deleted my app then installed it again. Tried a whole host of changes and after hours didn't understand what the issue was. I implemented this and it's fixed it. Still don't understand what the cause was but thank you for this.
Hey Community! As we jump into 2025, we want to give a big shout-out to all of you wh...
By JasonH Jan 7, 2025Hey Community! As the holiday season unfolds, we want to extend heartfelt thanks to a...
By JasonH Dec 6, 2024Dropshipping, a high-growth, $226 billion-dollar industry, remains a highly dynamic bus...
By JasonH Nov 27, 2024