Creating a shopify app with express and mongoose. The shop's domain and access tokens are saved to the database on the callback route of the install process. The index of the app is verified with the following function:
const verifyOAuth = query => {
if (!query.hmac) {
return false;
}
const hmac = query.hmac;
delete query.hmac;
const sortedQuery = Object.keys(query).map(key => `${key}=${Array(query[key]).join(',')}`).sort().join('&');
const calculatedSignature = crypto.createHmac('sha256', config.SHOPIFY_SHARED_SECRET).update(sortedQuery).digest('hex');
if (calculatedSignature === hmac) {
return true;
}
return false;
}
How can I create a middleware function for a request to access a shop's data from the mongo database.
EX:
router.get('/content', auth, (req, res) => {
const content = Content.findOne({shopifyDomain: 'shopify-domain-here'})
res.send(content);
});
var auth = (req, res, next) => {
// Get shop domain from authentication
next();
};
Would I have to add the shop domain and hmac as a query for every get request to '/content', or should I use res.setHeader to set them as headers when the index of the app is loaded, or is there a better solution?
You cannot add routes to Shopify. You will never have a request come to you from /Content. You obviously can make that a route in your own App though, and service that route.
If you want to sent content to Shopify, you should use the App Proxy. You receive a request for content, and then you fulfill that request with content formatted as Liquid or as JSON for example.
Related
I am consuming an external API with NodeJs. I'm confused, because I thought APIs could only be consumed from the frontend. The only way to return the data I get is through another API created by me, but I don't know if it's the best way to do this. Is there another way?
This is what I did to get the 'id' required by the url.
const express = require('express');
const axios = require('axios');
const app = express();
app.get('/example/:id', (req, res) => {
const { id } = req.params;
const url = `https://theexternalapiurl/${id}`;
const config = { token }
axios.get(url, config).then(response => {
//here I should query a database and return a response
console.log(response.data);
res.json(data);
});
});
Is it a good solution to create a second API to return the data?
Yes, this is how it is usually done! It is a good practice to not expose third-party APIs directly to the client. You'll face unnecessary charges when duplicate or unnecessary calls are made.
I have a NodeJS application which I have begun to separate out in to smaller files since the original became a little bloated.
In my index.js I have routes that are protected by a function a freelancer wrote to provide JWT authentication. These routes work as required.
app.use(require('./lib/api-calls/convert.js'));
// Security Enabled index.js
//
const { app } = require ('./lib/deps/init_dependencies.js');
const { enableSecurity } = require("./security");
const main = async () => {
// Enable API JWT Security = Comment out the line below to turn off security.
await enableSecurity(app);
app.get('/v1/createSession:key/:limit', function (req, apiResponse) {
// My route, working well
});
}
main()
I've created /lib/routes/convert.js and am wanting to write new routes in this file which also require JWT authentication. However, I always receive status 200 'OK', regardless of whether the authentication header is correct or not... I'm using Postman to make my calls. Here's my code:
const app = require('express')();
//JWT authentication
const { enableSecurity } = require('../../security');
const main = async () => {
// Enable API JWT Security = Comment out the line below to turn off security.
await enableSecurity(app);
app.get('/v3/convertw3w/:locationValue/:countryCode', function (req, res) {
res.status(200).send({ status: 'OK' });
});
}
main()
module.exports = app;
Can anyone spot the problem? I spent far to long on this last night!
thanks
Just some food for thought here and we do something similar, but if you use
enableSecurity(app)
As middleware on the route and the
next()
function in the middleware you can omit the need to make this a promise, because middelware is designed to process in order of the middleware and the next function tells express to move to the next middleware.
How we do it, is to have the middleware 'auth', because middleware will pass the req and res objects to each one in the stack you can have all your JWT decode in one place.
We typically will pass the token in the header OR the req object, just depends on the mimetype we pass, so in out auth we check if the token is in the header or the req, if so we decode, if it passes decode we pass
next()
in that code block otherwise we res.json({"response":"not authorized"})
I have a Rest API made with Koa with some routes, but, at the same time, it will serve my Front (made with a JS framework and its own router).
The fact is, when I access from a browser "localhost/user" I want to display the front but when I reach the same url from fetch / ajax / XMLHttpRequest I want to display a JSON result (the one gave by the Koa-router).
So I would like to enable the /user route from the API only if it's called from XHR.
I did my isXMLHttpRequest middleware like this :
module.exports = async (ctx, next) => {
if(ctx.request.get('X-Requested-With') === 'XMLHttpRequest') {
return next()
}
}
Then, in my koa-router I did something like :
const Router = require('koa-router')
const isXMLHttpRequest = require("#middlewares/isXMLHttpRequest")
const router = new Router()
const user = require("#routes/user")
router.use('/user', isXMLHttpRequest, user.routes(), user.allowedMethods())
And then, it works when I do some XHR request, I have the JSON as planned, but if I try to access the /user from the browser, the API is giving me a Not Found Error and not my front...
I was looking on how to skip the router.use function if the request isn't made in XHR, but I can't find a solution...
I think it's in the middleware else condition, I have to return something, but what can I do to skip the koa-router from giving me 404 ...
Maybe you can help me ?
OK, so if you are using the SAME routes for static and XMLHttpRequests (which is probably not the best strategy), then this could work:
const Koa = require('koa')
const Router = require('koa-router')
const app = module.exports = new Koa();
isXmlRequest = (ctx) => {
// here you could also compare e.g. "accept" header
return (ctx.request.header && ctx.request.header['x-requested-with'] === 'XMLHttpRequest');
}
// static routes
const staticRouter = new Router()
staticRouter.get('/user', (ctx, next) => {
ctx.body = 'OK from static route';
next();
});
// XMLHttpRequest routes
const xmlRouter = new Router()
xmlRouter.get('/user', (ctx, next) => {
if (isXmlRequest(ctx)) {
// serve it
ctx.body = { ok: 'from JSON/XML' }
} else {
// downstream to next handler
next();
}
});
app.use(xmlRouter.routes());
app.use(staticRouter.routes());
const server = app.listen(3000)
This is not using middleware bwcause here you can only allow downstream with next but if there is no next, then this stops. There is no else ;-)
Just for reference
Not sure If I got your question right. So you have a backend that acts like a static web server AND a REST API, right?.
I would try to do it the other way round. Using e.g koa-static (https://www.npmjs.com/package/koa-static) would FIRST try to serve your files and if no matching files are found in your defines public directory, all other routes (so your REST API) are handled. Then you only have to make sure, that endpoint names do not overlap with files you are serving.
this route takes information json
router.get('/sendOtpforMobileVerification', async (req, res) => {
console.log("\n//Registration PID 9".bold)
console.log("\nVOID API (PID 9) : Sending Verification Email".cyan.bold);
const userIdForOtpVerification = req.body.id;
const userPhoneForOtpVerification = req.body.mobile;
...bla bla bla
});
The above URL is working as expected.
what I am trying to do is perform its action from another route
//some other route
router.get('/change', async (req, res) => {
res.redirect('/void/api/authentication/sendVerificationMail');
-here i want to call the above route with json data
});
I think you can pass query params to the redirect route, which are accessible from the query parameters, as follows:
const url = require('url');
res.redirect(url.format({
pathname:"/anythingyouwant",
query: {
"something":"your infos here"
}
}));
Then you can get access to the passed query with the following:
app.get('/anythingyouwant', function(req, res) {
var passedVariable = req.query.something;
});
Router functions is independ functions among their. You can create a global value(class, static value etc.) and you send a request from client to server for first get function and thus you store the data and then you can send a new request from client to server, thus you can use the stored data.
I'm a little new to this. I have REST API made with Node.js and Express.js. Some routes have authentication middleware. To use those routes, a header has to be set with the user's auth token which gets verified. I have been doing this with no problem with static sites using local storage. I'm making my first dynamic site now (using Express) and for certain routes I have middleware that loads all the data I need to display the page. How do I access and use auth tokens now that I don't have local storage's help?
EDIT(for clarification):
So here is one of my api routes that fetches all transactions from a database(mongoDB).
app.get('/transactions', authenticate, (req, res) => {
Transaction.find().then((transaction) => {
res.send({transaction});
}, (e) => {
res.status(400).send();
});
});
This is the authentication middleware that gets run.
var authenticate = (req, res, next) => {
var token = req.header('x-auth');
User.findByToken(token).then((user) => {
if (!user) {
return Promise.reject();
}
req.user = user;
req.token = token;
next();
}).catch((e) => {
res.status(401).send();
});
};
Now on my express webserver, I have a following route, where I use getTransactions to fetch all my data. (which I display with handlebars)
router.get('/orders', getTransactions, (req, res) => {
res.render('orders.hbs', {
transaction: req.transactions.data.transaction
});
});
and this is the middleware
var getTransactions = (req, res, next) => {
axios.get('https://serene-wave-28270.herokuapp.com/transactions')
.then((response) => {
req.transactions = response;
console.log(req.transactions.data.transaction);
next();
}).catch((e) => {
console.log(e);
})
}
So when I was just making a static site without using express as a webserver, I would just have the user sign in and save the auth token in local storage. Also, I should note that the first two blocks are from my api, and the bottom two from webserver, both hosted separately on Heroku. I'm not sure if that's standard design so I thought I should mention it.
There's not a whole lot of detail in your question for exactly what you're trying to do, but I can explain the general concepts available to you in Express:
The usual scheme for Express is to authenticate the user initially and then set a session cookie that indicates that user has been authenticated. Since the cookie is automatically stored by the browser and then automatically sent from the browser to the server with every request, you will have that cookie which the server can then use to identify a server-side session and then you can use any info you want from the session (user identify or other state you store in the session object) when creating pages or responding to API requests for that user.
The NPM module express-session handles much of this work for you as it will automatically create a session object, a session cookie and hook the two together on every request.
If, on the other hand, you already have an auth token in the client and you just want that to be automatically communicated to the server with every request, then you can just put that auth token into a cookie and have the server look for it in the cookie on each request. You can even make it a bit more secure by setting the cookie to HttpOnly so that the auth token cannot be accessed from client-side Javascript (this will not affect the server's ability to access it).
There is not much detail in your question but here are a few thoughts.
You can either use cookies (as detailed by #jfriend00 below) or use the requests' headers to check for a valid authorization token (which I describe below)
In Express you can access the headers through req.headers so you can just write a middleware that you will call before your current middleware loading all the data to ensure that the user is authorized to continue (calling next() to call the next middleware) or using a custom Error type to flag the authentication error if he is not (calling next(err) to skip all the other middleware and jump to your error middleware)
For example (assuming you have a subclass of Error named AuthorizationError defined somewhere):
const express = require('express');
const AuthorizaztionError = require('<some path>');
const app = express();
function checkAuthTokenMiddleware(req, res, next) {
if (req.headers && req.headers.authorization) {
let token;
const parts = req.headers.authorization.split(' ');
if (parts.length == 2) {
const [scheme, credentials] = parts;
if (/^Bearer$/i.test(scheme)) { // or any other scheme you are using
token = credentials;
}
if (token === undefined) {
// access token - missing
return next(new AuthorizationError(
"Invalid access token.", // error_description
"invalid_token" // error
));
}
// add something here to ensure the token is valid
return next();
}
} else {
// No authorization header => invalid credentials
return next(new AuthorizationError(
"Authorization header required.", // error_description
"invalid_request" // error
));
}
}
// Add this in your route declaration
app.use(
"/auth/test",
checkAuthTokenMiddleware,
function(req, res, next) {
// do something
}
);
// this must come last
app.use(function errorMiddleware(err, req, res, next) {
// return something
if (err instanceof AuthenticationError) {
// do something for example
res.status(401).send(err.error_description);
} else {
// generic error handling, for example
res.status(500).send("Error "+err);
}
})
// ...