I'm new to MEAN development, and I'm using bcrypt to encrypt the password from UserSchema (everything works wonders), but since I have to authenticate from the client side I've made a function to the UserSchema:
UserSchema.methods.comparePassword = function(candidatePassword, cb) {
bcrypt.compare(candidatePassword, this.password, function(err, isMatch) {
if (err) return cb(err);
cb(null, isMatch);
});
};
Now.. how do I call this function from the client side?
I'm using Restangular, and I'm trying something like this:
function authLogin() {
if(vm.user && vm.user.email && vm.user.password){
User.getList({ email: vm.user.email }).then(function(user){
user.comparePassword(vm.user.password, function(err, isMatch) {
if (err) throw err;
console.log(vm.user.password, isMatch);
});
});
}}
But then occurs an error saying that user doesn't have the funciton:
user.comparePassword is not a function
So, what is wrong about it? Or it is not possible to call a Schema function directly from the client side?
I believe the answer to your immediate question (if i understand which of your code samples lives to which layer): why is it "not possible to call a Schema function directly from the client side" is that the server and client side javascript run in a completely different context so functions you might declare in server-side models or middleware are not available to your client context.
The general solution to "call a schema function" from the client side with node and javascript frameworks is to POST or GET data to a server-side API endpoint and then you call your schema methods within the API code (or middleware attached to that API endpoint).
I would suggest that you take a look at the https://www.npmjs.com/package/passport passport authentication API module which is a market standard way to handle client authentication that you should be able to pretty easily bolt onto your application.
Related
I am trying to figure out what is the best way to prevent random people access/make requests to the server.
Currently, everyone that have the url and the endpoint can easily make requests and I believe it's kind of security breach.
I am using Express.js that hosting static build of React.js
Here is an example of a call:
// Client:
async function getData(id){
try {
const res = await axios.get(`${backendDomain}/data/${id}`)
const data = res.data
return data
} catch (error) {
...
}
}
// Server:
app.get('/data/:id', function(req, res) {
...logic
res.send(data);
});
I tried adding to the Client "x-api-key" header and pass an api key that only I have and add a middleware that will check the api and see if it passed currectly from the client. But obviously it is not a good solution because you can see the key on "Network" section while inspecting.
What can I do?
Looking for the best way to prevent random people accessing the data
I have a simple angular resource that I've defined as below:
CompanyService.factory('CompanyService',
function ($resource) {
return $resource('https://baseurl.com/api/values/');
}
);
I then have a controller that calls that resource passing in a success and fail function:
.controller('companyList', function($scope, CompanyService) {
$scope.companies = CompanyService.query(
function(data) {
console.log(data);
return data;
},
function(error){
console.log("Error:");
console.log(error);
}
);
The rest API is a .NET MVC Web API that is extremely basic. I've configured it to return JSON and it simply returns an array of two objects like below. I've also enabled CORS so my angular app, which is hosted in a different domain, can call the api.
[{ID:1, Name:"TEST1"}, {ID:2, Name:"TEST2"}]
I've tested the REST call using jquery and just straight call through browser. All was functional (including the cross site scripting when calling from my angular app just using a straight JavaScript HTTP call).
When I try to call the api from my controller however, it always ends up in the error function. The error object contains a data property that is always populated with the string "resource is required|resource is required|undefined"
When I check the network I see no call to the values end point. It's as if the call is failing before ever being made.
If I change out the url to point to some sample REST api like https://jsonplaceholder.typicode.com/users/ it works fine and I'm able to see the call to "users" in the network traffic, which makes me think there is something wrong with my C# REST endpoint, however all my tests to call the REST endpoint outside of angular work successfully.
Can anyone help? I can't find anyone reporting this issues before anywhere on the net.
should the code be the one below? i didn't test it, just guess.
myModule.factory('CompanyService',
function ($resource) {
return $resource('https://baseurl.com/api/values/');
}
)
.controller('companyList', function($scope, CompanyService) {
CompanyService.query(
function(data) {
$scope.companies = data;
console.log(data);
return data;
},
function(error){
console.log("Error:");
console.log(error);
}
);
I ended up rebuilding my angular app from scratch. My first app was from the angular-seed github and had a handful of libraries already added in for testing and other things. One of those things is was was leading to this error as once I started a new project completely from scratch and added in angular and my REST call things worked perfectly. I've already spent too much time working through this so not going to spend any more time identifying exactly what it is but in case anyone else runs into the problem I did want to answer this one and close the book on it.
I'm having a bit of trouble figuring out how to use Waterlock. I'm working with Sails.js and Angularjs, and I'm not sure how to update user attributes on a Waterlock authenticated user in a Sails controller. Should I not write my own controller for this? Is there a standard way to update a user through an API call in Sails or Waterlock?
I found the solution in case anyone else is stuck here. You can access the authenticated user's data in req.session.user, and you will update the user through the Waterline ORM.
Here's some sample code:
module.exports = require('waterlock').actions.user({
updateCurrentUser: function(req, res) {
User.update(req.session.user.id, req.body)
.exec(function(err, changes) {
return res.json(changes);
});
}
});
I have an API <> SPA architecture.
But I don't know exactly where to put translations, specially when errors are send.
I would prefer to just have translated text (i18n etc) at my angular app. The API should only send out english texts.
But I don't know how to
Imagine an Error message like this: (nodejs+restify)
server.get('/api/', function (req, res, next) {
return next(new server.errors.UnauthorizedError('You are not authorized'));
});
This will send a 401 with json in body.
If the API only speaks english, how can I translate it at best at the SPA?
How can I put this to, one of these translation approaches:
<span>{{message | translate}}</span>
or
<span>{{lang.message}}</span>
Of course I use the $translateProvider at Angular and have a locate files with translation in it.
Is there no other way by sending language keys? Like this:
server.get('/api/', function (req, res, next) {
return next(new server.errors.UnauthorizedError('NOT_AUTHORIZED_ERROR'));
});
With this I could have a locate-en.json file like:
{
errors: {
NOT_AUTHORIZED_ERROR: "You are not authorized"
}
}
I find this kind of uncommon.
You have 2 options: either you presume that your client knows upfront all the errors the API can throw. This is usually only the case if both API and the client are implemented by the same team. In that case, let your API return an error object, that contains a code:
{
"code": "unauthorized",
"message": "some reason in english, usefull for debugging/logging"
}
Then use this code to translate the error client side:
<span>{{error.code | translate}}</span>
The second option is to let the API translate the error message. Use this option if the API can evolve (and throw new errors) without the knowledge of the client. Of course, in this case you're limitted to the languages the API supports.
How about put dictonary with code and message into $translateProvider and read the error from local storage, like this:
app.config(['$translateProvider', function ($translateProvider) {
$translateProvider.translations('en', {
error : 'messageEN'
})
$translateProvider.translations('de', {
error : 'messageDE'
})
$translateProvider.preferredLanguage('en');
// remember language
$translateProvider.useLocalStorage();
}]);
I am trying to run the IBM Watson's Tradeoff Analytics widget to show the trade-off analytics graph in a webpage. The Tradeoff Analytics API is starting properly but when I submit the problem to the show the graph, I get some undefined error.
Here is the sample code that I am using the run the Tradeoff Analytics Widget.
function errorHandler(payload){
alert(payload.errorMessage);
}
function onShowCompleteCB(payload){
alert('show Tradeoff graph complete');
}
function onStartCB(payload){
alert('sending trade-off problem');
var problem = <problem-json>;
taClient.show(problem, onShowCompleteCB);
}
var options = {
dilemmaServiceUrl : <tradeoff-service-url>,
username : <username>,
password : <password>
};
var taClient = new TradeoffAnalytics(options , document.getElementById('watson_widget'));
var s = taClient.subscribe('afterError', errorHandler);
taClient.start(onStartCB);
I also noticed from javascript debugger that HTTP response to the last request returned the reponse header WWW-Authenticate:Basic realm="IBM Watson Gateway Log-in". Moreover I get the following error in javascript console XMLHttpRequest cannot load . No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin '' is therefore not allowed access. The response had HTTP status code 401.
Can somebody help me out with what might be going wrong here?
PS: I have cross checked my username and password and they seem to be working fine through REST based API invocation.
Based on your code, you are trying to use the client widget. You need to have a proxy app that will receive the request and use your username and password.
On your client side you will need something like:
HTML:
<div id='DIV_ID'></div>
JS:
taClient = new TA.TradeoffAnalytics({
customCssUrl: 'https://ta-cdn.mybluemix.net/v1/modmt/styles/watson.css',
dilemmaServiceUrl: '/proxy',
profile: 'basic'
}, 'DIV_ID');
taClient.subscribe('afterError', function onError(){ /* on error */});
taClient.start(function onLoad(){ /* on load */});
}
Server side(nodejs):
var tradeoffAnalytics = watson.tradeoff_analytics({
version: 'v1',
username: '<username>',
password: '<password>'
});
app.post('/proxy', function(req, res) {
tradeoffAnalytics.dilemmas(req.body, function(err, dilemmas) {
if (err)
return res.status(err.code || 500).json(err.error || 'Error processing the request');
else
return res.json(dilemmas);
});
});
Above you will find an example of how to implement the proxy using express and the watson-developer-cloud npm module.
There is a full sample you can look in github