Reputation: 6281
I've been following this tutorial on how to build a public Shopify app using Node, Nextjs and React.
Everything is going well, however I am now at a point where I need to store some of my app data in a database. For this, I've chosen Firestore.
What I am doing is when the user authenticates the app with their store, I use the Admin-SDK within my Next JS server to write their shop and accessToken to a document:
Note: I'm using the accessMode "offline" as I need a permanent access token so that my app can make Shopify API requests in the background. I also know that I should really encrypt the accessToken before storing it, however just trying to get the logic working first
server.use(
createShopifyAuth({
apiKey: SHOPIFY_API_KEY,
secret: SHOPIFY_API_SECRET_KEY,
scopes: ['read_products', 'write_products'],
accessMode: "offline",
async afterAuth(ctx) {
const {shop, accessToken} = ctx.session;
ctx.cookies.set('shopOrigin', shop, {
httpOnly: false,
secure: true,
sameSite: 'none'
});
await getSubscriptionUrl(ctx, accessToken, shop);
await db.collection('shops').doc(shop).set({
name: shop,
accessToken
}, {merge: true});
},
}),
);
I am also using the Koa middleware which validates any requests to my app to ensure they are from a Shopify app
server.use(verifyRequest());
What I now want to do is to add a way for my app to pull data from Firebase. I've created an api endpoint within /pages/api/firebase.js
that uses the shopOrigin
cookie to get the store name, and then pull the data for that store.
export default async(req, res) => {
const shop = req.cookies.shopOrigin;
const doc = await db.collection('shops').doc(shop).get()
res.status(200).json({success: true, data: doc.data()});
}
The problem I'm facing though, is that a user can simply just change that cookies value and pull data for another store. The verifyRequest
function doesn't seem to check if the shopOrigin name calling it is from the store that calls it.
Does anyone know how I can validate these requests, so that the store calling my API can only access that stores data (via the admin-sdk on the Next server)?
Upvotes: 2
Views: 2794
Reputation: 6281
Thinking about this, I can use a custom authentication method.
Within the Next server.js file, the below creates a custom token using the shop name, and then sets that token in a cookie. For the example, it then creates a document in the shops collection with the document ID as the store name.
async afterAuth(ctx) {
const {shop, accessToken} = ctx.session;
await getSubscriptionUrl(ctx, accessToken, shop);
const customToken = await admin.auth().createCustomToken(shop)
ctx.cookies.set('shopOrigin', shop, {
httpOnly: false,
secure: true,
sameSite: 'none'
});
ctx.cookies.set('token', customToken, {
httpOnly: false,
secure: true,
sameSite: 'none'
});
await db.collection('shops').doc(shop).set({
domain: shop,
token: cryptr.encrypt(accessToken),
}, {merge: true});
},
Within the React app, initialise your Firebase App instance and then within the index.js listen to see if the authState changed or log the user in using the token.
useEffect(() => {
firebase.auth().onAuthStateChanged(user => {
if(user) {
setSignedIn(true)
} else {
firebase.auth().signInWithCustomToken(Cookies.get('token'));
}
})
},[])
Then in the Firebase rules, set your permissions as required
match /shops/{shop}/{documents=**} {
allow read, write: if request.auth != null && request.auth.uid == shop
}
Upvotes: 5