I've built a simple RESTful API using NodeJS, Mongoose, and Express. I am using the database to store simple string quotes and am not planning to allow access to any other users to the database nor to the api.
I've read up on securing my RESTful API but it seems as if most methods focus on using a username and password to limit access. However, that seems like an overkill for such a simple API especially since i do not consider on allowing anyone else access except for requests that come from the server itself.
So I want to make it so that if anyone else tries to access the API he would be denied access. The only way the API should be accessible is from requests from the server itself i.e from the JavaScript files on the server.
I am currently learning all those things so sorry if i am not using the proper technical terminology :)
I am considering doing something like checking the IP of the person/thing trying to access the API and if that is not the ip of the server then deny access. Would something like this work and how would I got about implementing it.
EDIT: I am looking for something simple since I dont think that most people will take the time to 'hack' the API just so they can access a database of quotes.
Here is my server.js
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var mongoose = require('mongoose');
var Quote = require('./mongodb/models/mainModel.js');
mongoose.connect('mongodb://localhost:27017/myappdb');
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
var port = process.env.PORT || 8080;
var router = express.Router();
function grantAccess(req) {
if(req.ip === '::1' ||
req.ip === '127.0.0.1' ||
req.ip === '::ffff:127.0.0.1') {
return true;
}
return ["IP Address Unknown " + req.ip]
}
router.get('/', function(req, res) {
res.json({ message: 'hooray! welcome to our api!' });
});
router.route('/maindb')
.post(function(req, res) {
var quote = new Quote();
quote.name = req.body.name;
quote.severity = req.body.severity;
quote.createdAt = new Date();
quote.updatedAt = new Date();
quote.save(function(err) {
if (err)
res.send(err);
res.json({ message: 'Quote created!' });
});
})
.get(function(req, res) {
if(grantAccess(req) !== 'boolean')
Quote.find(function(err, quotes) {
if (err)
res.send(err);
res.json(quotes);
});
});
router.route('/maindb/:quote_id')
.get(function(req, res) {
Quote.findById(req.params.quote_id, function(err, quote) {
if (err)
res.send(err);
res.json(quote);
});
})
.put(function(req, res) {
Quote.findById(req.params.quote_id, function(err, quote) {
if (err)
res.send(err);
quote.name = req.body.name;
quote.severity = req.body.severity;
quote.updatedAt = new Date();
// save the bear
quote.save(function(err) {
if (err)
res.send(err);
res.json({ message: 'Quote updated!' });
});
});
})
.delete(function(req, res) {
Quote.remove({
_id: req.params.quote_id
}, function(err, quote) {
if (err)
res.send(err);
res.json({ message: 'Successfully deleted' });
});
});
app.use('/api', router);
app.listen(port);
console.log('Magic happens on port ' + port);
you can add apiKey in your project. It will be required if anyone hits any of your api.
exmaple:
"apiKeys": {
"web": "7fe642cabe6855cd4175937fa8fadd876c1af6b499ab941db6a8a362c0f30f97"
}
similarly you can set apikey for mobile user or accordance to requirment of project.
Link to genrate RandomKey
By this you will allow only those users who have your api key.As api key is shared by you so you will provide it to only appropriate user.
Api key checking:
You can check api key as first middleware before any request to server
example:
router.use(function(req,res,next){
var apiKey = req.get('api_key'); // assuming user will send api key in headers
// code to check api key basic comparison
})
Related
I am trying to get the data my nodeJS server is receiving from a form on the front end to send that data to my email. I have tried to use nodemailer and haven't succeeded much. Can someone tell me perhaps what I am doing wrong with the following code?
const express = require("express");
const app = express();
const nodemailer = require("nodemailer");
var smtpTransport = require("nodemailer-smtp-transport");
const PORT = process.env.PORT || 4000;
app.use(express.static(__dirname + "/front-end"));
app.get("/", (req, resp) => {
resp.sendFile(__dirname + "/front-end/index.html");
});
app.use(express.json());
app.use(express.urlencoded());
app.post("/formData", (req, resp) => {
const data = req.body;
var transport = nodemailer.createTransport(
smtpTransport({
service: "Gmail",
auth: {
user: "user#gmail.com",
pass: "123456",
},
})
);
transport.sendMail(
{
//email options
from: "Sender Name <email#gmail.com>",
to: "Receiver Name <receiver#email.com>", // receiver
subject: "Emailing with nodemailer", // subject
html: data, // body (var data which we've declared)
},
function (error, response) {
//callback
if (error) {
console.log(error);
} else {
console.log("Message sent:");
resp.send("success!");
}
transport.close();
}
);
});
app.listen(PORT, () => {
console.log(`server running on port ${PORT}`);
});
Your code, at a glance, looks fine to me. I think the problem is (since you’re not stating you have set that up), that you want to send email with GMail. If you want to send email from your own app or web service via Gmail, you should set up a project in the Google Cloud Platform. Read more here.
Alternatively, you could use a service like Postmark, which you can configure to send emails via a domain that you own. There’s a free trial. Mailgun is a similar service. (I’m not affiliated to either).
I want to trigger my bot with http request (for example just entering http://localhost:3978/api/messages/http) so after triggering it, it will send every user that is connected to this bot some message.
I have seen this topic: How to send message later in bot framework?
And this is what I have so far:
var restify = require('restify');
var builder = require('botbuilder');
var server = restify.createServer();
server.listen(process.env.port || process.env.PORT || 3978, function () {
console.log('%s listening to %s', server.name, server.url);
});
var connector = new builder.ChatConnector({
appId: process.env.MICROSOFT_APP_ID,
appPassword: process.env.MICROSOFT_APP_PASSWORD
});
server.post('/api/messages', connector.listen());
var bot = new builder.UniversalBot(connector);
bot.dialog('/',function (session) {
var reply = session.message; // address: reply.address
reply.text = 'Wake up!'
console.log(reply.text);
bot.send(reply);
});
// Create response function
function respond(req, res, next) {
res.send('hello ' + req.params.name);
bot.send(reply);
next();
}
server.get('/api/messages/:name', respond);
Unfortunately, it doesn't send any messages while I am acessing my http://localhost:3978/api/messages/http. I also tried to use
connector.send('message');
But it always throughs me "ERROR: ChatConnector: send - message is missing address or serviceUrl."
UPDATE:
I have announced a global var for the reply with
var globalreply;
bot.dialog('/',function (session) {
globalreply = session.message; // address: reply.address
globalreply.text = 'Wake up!'
console.log(globalreply.text);
bot.send(globalreply);
});
// Create response function
function respond(req, res, next) {
res.send('hello ' + req.params.name);
bot.beginDialog;
bot.send(globalreply);
next();
}
But now it throughs me an error:
TypeError: Cannot read property 'conversation' of undefined.
At my bot.send(globalreply); line.
Looking forward your help.
Best regards.
If you want to set up a normal HTTP API route, I suggest using the Restify API style routing, rather than the bot's /api/messages route handler.
For example:
function apiResponseHandler(req, res, next) {
// trigger botbuilder actions/dialogs here
next();
}
server.get('/hello/:name', apiResponseHandler);
Using ReactJS, Redux, Webpack, Node.js and Express with MongoDB, I am following the tutorial https://github.com/vasansr/mern-es6 and trying to integrate it into my project. First, I am trying to make a POST request to the server I created. And it gets a response with a success and no error is logged. Yet inside the server POST API, it does not log console.log('Req body', req.body);, and in terminal I checked to see if the database has been created with mongo -> show dbs but it is empty.
Could it be that something is intercepting the request from the server? What could be the issue?
This...
app.use('/', function (req, res) {
res.sendFile(path.resolve('client/index.html'));
});
comes before:
app.post('/api/users/', function(req, res) {
//...
});
Since it's app.use the POST /api/users will still hit that middleware, and res.sendFile ends the request/response. You'll probably see that your post is getting back the client HTML.
Try moving your client HTML endpoint to the end of your middleware, just before the error handlers if you have them. That way, it'll only get used if none of your API endpoints match. Or if you want just GET / to return the HTML, change use to get:
app.use(webpackDevMiddleware(compiler, {noInfo: true, publicPath: config.output.publicPath}));
app.use(webpackHotMiddleware(compiler));
app.use(express.static('dist')); //where bundle.js is
app.use(bodyParser.json());
app.post('/api/users/', function(req, res) {
console.log('Req body', req.body);
var newUser = req.body;
db.collection('users').insertOne(newUser, function(err, result) {
if(err) console.log(err);
var newId = result.insertedId;
db.collection('users').find({_id: newId}).next(function(err, doc) {
if(err) console.log(err);
res.json(doc);
});
});
});
app.get('/', function (req, res) {
res.sendFile(path.resolve('client/index.html'));
});
app.post('/api/users/', function(req, res) {
console.log('Req body', req.body);
var newUser = req.body;
db.collection('users').insertOne(newUser, function(err, result) {
if(err) console.log(err);
var newId = result.insertedId;
db.collection('users').find({_id: newId}).next(function(err, doc) {
if(err) console.log(err);
res.json(doc);
});
});
});
I have a small comments about this code, for if(err) console.log(err); i think you should change to if(err) return console.log(err);.
For error case, i think you need return, otherwise the below part will be excuted, and there will report some error.
Has anyone successfully navigated Jawbone's OAuth2.0 authentication for their REST API?
I am unable to figure out how to access and send the authorization_code in order to obtain the access_token (steps 4 & 5 in the Jawbone API Authorization Documentation). I want to reuse the access_token for subsequent (AJAX-style) calls and avoid asking the user to reauthorize each time.
Each call of the API (get.sleeps) requires a full round trip of the auth process including this reauthorization to get an authorization_token (screen shot). Both the Jawbone and Passport Documentation is vague on this point.
My stack involves, node.js, the jawbone-up NPM, express.js and passport.js. The Passport Strategy for Jawbone appears to work correctly as I get valid data back.
The jawbone-up NPM explicitly does not help maintain the session (access_token), saying "This library does not assist in getting an access_token through OAuth..."
QUESTION: how do I actually use the OAUTH access_token in the API call? Can someone show me some code to do this?
Thanks
var dotenv = require('dotenv').load(),
express = require('express'),
app = express(),
ejs = require('ejs'),
https = require('https'),
fs = require('fs'),
bodyParser = require('body-parser'),
passport = require('passport'),
JawboneStrategy = require('passport-oauth').OAuth2Strategy,
port = 5000,
jawboneAuth = {
clientID: process.env.JAWBONE_CLIENT_ID,
clientSecret: process.env.JAWBONE_CLIENT_SECRET,
authorizationURL: process.env.JAWBONE_AUTH_URL,
tokenURL: process.env.JAWBONE_AUTH_TOKEN_URL,
callbackURL: process.env.JAWBONE_CALLBACK_URL
},
sslOptions = {
key: fs.readFileSync('./server.key'),
cert: fs.readFileSync('./server.crt')
};
app.use(bodyParser.json());
app.use(express.static(__dirname + '/public'));
app.set('view engine', 'ejs');
app.set('views', __dirname + '/views');
// ----- Passport set up ----- //
app.use(passport.initialize());
app.get('/',
passport.authorize('jawbone', {
scope: ['basic_read','sleep_read'],
failureRedirect: '/'
})
);
app.get('/done',
passport.authorize('jawbone', {
scope: ['basic_read','sleep_read'],
failureRedirect: '/'
}), function(req, res) {
res.render('userdata', req.account);
}
);
passport.use('jawbone', new JawboneStrategy({
clientID: jawboneAuth.clientID,
clientSecret: jawboneAuth.clientSecret,
authorizationURL: jawboneAuth.authorizationURL,
tokenURL: jawboneAuth.tokenURL,
callbackURL: jawboneAuth.callbackURL
}, function(token, refreshToken, profile, done) {
var options = {
access_token: token,
client_id: jawboneAuth.clientID,
client_secret: jawboneAuth.clientSecret
},
up = require('jawbone-up')(options);
up.sleeps.get({}, function(err, body) {
if (err) {
console.log('Error receiving Jawbone UP data');
} else {
var jawboneData = JSON.parse(body).data;
console.log(jawboneData);
return done(null, jawboneData, console.log('Jawbone UP data ready to be displayed.'));
}
});
}));
// HTTPS
var secureServer = https.createServer(sslOptions, app).listen(port, function(){
console.log('UP server listening on ' + port);
});
You weren't too far off, you were already getting the token. To make your code work a few steps are needed:
Add the concept of a "session", data that exists from request to request as a global variable. When you do a full web app use express-sessions and passport-sessions and implement user management. But for now we just add a global for a single user state.
var demoSession = {
accessToken: '',
refreshToken: ''
};
Pass in a user object in the done() of JawboneStrategy. This is because the "authorize" feature of passport is expecting a user to exist in the session. It attaches the authorize results to this user. Since we are just testing the API just pass in an empty user.
// Setup the passport jawbone authorization strategy
passport.use('jawbone', new JawboneStrategy({
clientID: jawboneAuth.clientID,
clientSecret: jawboneAuth.clientSecret,
authorizationURL: jawboneAuth.authorizationURL,
tokenURL: jawboneAuth.tokenURL,
callbackURL: jawboneAuth.callbackURL
}, function(accessToken, refreshToken, profile, done) {
// we got the access token, store it in our temp session
demoSession.accessToken = accessToken;
demoSession.refreshToken = refreshToken;
var user = {}; // <-- need empty user
done(null, user);
console.dir(demoSession);
}));
Use a special page to show the data "/data". Add a route to separate the auth from the display of service.
app.get('/done', passport.authorize('jawbone', {
scope: ['basic_read','sleep_read'],
failureRedirect: '/'
}), function(req, res) {
res.redirect('/data');
}
);
Lastly the Jawbone Up sleeps API is a little tricky. you have to add a YYYYMMDD string to the request:
app.get('/data', function(req, res) {
var options = {
access_token: demoSession.accessToken,
client_id: jawboneAuth.clientID,
client_secret: jawboneAuth.clientSecret
};
var up = require('jawbone-up')(options);
// we need to add date or sleep call fails
var yyyymmdd = (new Date()).toISOString().slice(0, 10).replace(/-/g, "");
console.log('Getting sleep for day ' + yyyymmdd);
up.sleeps.get({date:yyyymmdd}, function(err, body) {
if (err) {
console.log('Error receiving Jawbone UP data');
} else {
try {
var result = JSON.parse(body);
console.log(result);
res.render('userdata', {
requestTime: result.meta.time,
jawboneData: JSON.stringify(result.data)
});
}
catch(err) {
res.render('userdata', {
requestTime: 0,
jawboneData: 'Unknown result'
});
}
}
});
});
I have created a gist that works for me here thats based on your code: https://gist.github.com/longplay/65056061b68f730f1421
The Jawbone access token expires in 1 year so you definitely don't need to re-authenticate the user each time. Also you are provided with a refresh_token as well, so you can refresh the access token when needed.
Once you have the access_token you have to store it somewhere, preferably in some sort of a database or a file storage for later use, then you use that token for each request made to the Jawbone REST API.
The jawbone-up module uses request internally, so I'm going to show you how to make a request with it (it should be pretty much the same with any other module).
Here is how you can get the user's profile (the most basic API call):
var request = require('request')
request.get({
uri:'https://jawbone.com/nudge/api/v.1.1/users/#me',
auth:{bearer:'[ACCESS_TOKEN]'},
json:true
}, function (err, res, body) {
// body is a parsed JSON object containing the response data
})
There is another module called Purest which also uses request internally, but hides some of the complexity around using a REST API. Here is how the same request would look like using that module:
var Purest = require('purest')
var jawbone = new Purest({provider:'jawbone'})
jawbone.get('users/#me', {
auth:{bearer:'[ACCESS_TOKEN]'}
}, function (err, res, body) {
// body is a parsed JSON object containing the response data
})
Alternatively for authenticating the user (getting the access_token) you can use another module called Grant which I personally use, but either one should work.
I am having trouble with a project I am working on. I want to create a database in which I can store dates and links to YouTube videos in a MongoDB database. I am using Mongoose as the ORM. The problem seems to be that the database and collection is created and I can read and update it outside the routes but not inside (if anyone can understand what I am saying). I want to be able to make a GET request for the current items in the database on the /database route as well as make a POST to the /database route.
My code is below. Please help:
//grab express and Mongoose
var express = require('express');
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
//create an express app
var app = express();
app.use(express.static('/public/css', {"root": __dirname}));
//create a database
mongoose.connect('mongodb://localhost/__dirname/data');
//connect to the data store on the set up the database
var db = mongoose.connection;
//Create a model which connects to the schema and entries collection in the __dirname database
var Entry = mongoose.model("Entry", new Schema({date: 'date', link: 'string'}), "entries");
mongoose.connection.on("open", function() {
console.log("mongodb is connected!");
});
//start the server on the port 8080
app.listen(8080);
//The routes
//The route for getting data for the database
app.get("/database", function(req, res) {
Entry.find({}, function(err, data) {console.log(err, data, data.length); });
});
//The route for posting data on the database
app.post("/database", function(req, res) {
//test new post
var newMonth = new Entry({date: '1997-10-30', link: 'https://wwww.youtube.com/'});
newMonth.save(function(err) {
if (err !== null) {
//object was not save
console.log(err);
} else {
console.log("it was saved!")
};
});
});
//create an express route for the home page at http://localhost:8080/
app.get('/', function(req, res) {
res.send('ok');
res.sendFile('/views/index.html', {"root": __dirname + ''});
});
//Send a message to the console
console.log('The server has started');
Your GET request should have worked because a browser executes a GET request by default. Try the following.
app.get("/database", function(req, res) {
Entry.find(function(err, data) {
if(err) {
console.log(err);
} else {
console.log(data);
}
});
});
As far as testing your POST route is concerned, install a plugin for Google Chrome called Postman. You can execute all sorts of requests using it. It's great for testing purposes.