I'm trying to authenticate a user based on values entered in a given form. However, after using res.send(), the function at AngularJS controller is not able to correctly redirect user even if the password and username are correct. Am I handling the callbacks correctly?
Controller
<script>
var app = angular.module('myApp', []);
app.controller("loginController", function($scope,$http) {
$scope.sub = function() {
var config = {
headers : {
'Content-Type': 'application/x-www-form-
urlencoded;charset=utf-8;'
}
}
$http.post('/login', { data:{ username: $scope.username,
password: $scope.password} })
.then(function(response){
if(response.state==0){
console.log('Error!');
} else if(response.state==1){
console.log('action on success');
window.location.href = '/views/success.html';}
}).catch(function(error){
console.log('action on error');
});
Authentication
var db = require('../../config');
exports.login = function(req,res){
var username = req.body.data.username;
var password = req.body.data.password;
db.query('SELECT * FROM users WHERE username = ?',[username], function
(error, results, fields){
var result = "0";
if(error) {
console.log('Code 400, Error ocurred');
}
else{
if(results.length>0){
if(results[0].password == password){
console.log('Code 200, login sucessful');
res.json({ state : 1});
}
}
else{
console.log('Code 400, Password or username invalid');
res.json({ state: 0})
}
}
});
}
server.js
var express = require('express');
var bodyParser = require('body-parser');
var morgan = require('morgan');
var db = require('./config');
var app = express();
app.use(bodyParser.urlencoded({extended:true}));
app.use(bodyParser.json());
app.use(morgan('dev'));
app.use(express.static(__dirname + '/app'));
require('./app/routes')(app);
app.listen(3000,function(err){
if(err){
console.log(err);
}
else{
console.log("Listening on port 3000");
}
});
Route.js
var auth = require('../app/middleware/authenticateUser');
module.exports = function (app) {
app.get('/',function(req,res){
res.sendFile(__dirname + '/views/index.html');
});
app.post('/login', function(req, res){
auth.login(req,res);
});
}
Thanks in advance!
You need to inject $window in your controller and then in your successful response
$window.location.href = '/views/success.html';
Although using the $window service is considered AngularJS best practice, I don't think this is where the problem is.
Have you tried console.log() the response object of the $http call?
Maybe the problem is because you put if(response.state) instead of if(response.data.state).
Related
File router.js contains the routers that are used.
In the ionic2, providers are made with the post method by giving uri as http://localhost:8080/api/auth/login which gives 400 bad request error in the debugger tools.
I have found that when I removerequireLoginthen the post is working fine.But I needrequireLogin` for authenticating the local login of passport.
router.js
//require the passport strategy from the folder in the project.
var passport = require('passport');
var x = require('./usefile/file');
var requireLogin = passport.authenticate('local',{session: false});
module.exports = function(app){
var auth = express.Router();
app.use('/api/auth', auth);
auth.post('/login', requireLogin, function(req, res){
x.login(req, res)});
}
passport.js
var passport = require('passport');
var JwtStrategy = require('passport-jwt').Strategy;
var ExtractJwt = require('passport-jwt').ExtractJwt;
var LocalStrategy = require('passport-local').Strategy;
var config = require('./auth');
var User = require('../models/user');
var localOptions = {
usernameField: 'email',
};
var localLogin = new LocalStrategy(localOptions, function (email, password, done) {
User.find(
{ email: email }, function (err, user) {
if (err) {
return done(err);
}
if (!user) {
return done(null, false, { error: 'Login failed,Please try again.' });
}
if (!isMatch) {
return done(null, false, { error: 'Login failed, Please try again.' });
}
user.comparePassword(function(password, isMatch){
if(err){
return done(err);
}
if(!isMatch){
return done(null, false, {error:'Login failed, Please try again.'});
}
return done(null, user);
});
});
});
passport.use(localLogin);
Note: My code is not using the requireLogin which is used to authenticate from the passport, So it is showing 400 bad request error.
Please Help.
have you required your local passport file? You seem to be requiring the npm passport module, and not your local file.
You should change the first line of code in router.js to
//require the passport strategy from the folder in the project.
var passport = require('./passport');//<--change this line
var x = require('./usefile/file');
var requireLogin = passport.authenticate('local',{session: false});
module.exports = function(app){
var auth = express.Router();
app.use('/api/auth', auth);
auth.post('/login', requireLogin, function(req, res){
x.login(req, res)});
}
Let me know how that goes.
I'm learning Nodejs and ExpressJS. I'm trying to use ExpressJS and 2 Node modules (request-ip and geoip2) to get the client IP address for geolocation and then outputting the geolocation in the browser using AngularJS (1.x).
So far for my Nodejs and Expressjs code I have
var express = require('express');
// require request-ip and register it as middleware
var requestIp = require('request-ip');
// to convert the ip into geolocation coords
var geoip2 = require('geoip2');
// Init app
var app = express();
var port = process.env.PORT || 8000;
geoip2.init(); // init the db
//app.use(requestIp.mw({ attributeName: 'myCustomAttributeName'}));
var ip = '207.97.227.239';//67.183.57.64, 207.97.227.239
// respond to homepage req
app.get('/', function (req, res, next) {
//var ip = req.myCustomAttributeName;// use this for live
//var ip = '207.97.227.239';/* use this for testing */
console.log('requestIP is ' + ip);
next();
// geolocation
geoip2.lookupSimple(ip, function(error, result) {
if (error) {
console.log("Error: %s", error);
}
else if (result) {
console.log(result);//ipType was causing console.log duplication, IDK why
}
});
});
// set static folder
app.use('/', express.static(__dirname + '/public'));
app.listen(port, function(){
console.log('user location app is running');
});
And for Angular I have
angular.module('UserLocation', []);
angular.module('UserLocation')
.controller('MainController', MainController);
MainController.$inject = ['$http'];
function MainController($http) {
var vm = this;
vm.result = '';
vm.message = 'Hello World';
vm.getLocation = function() {
console.log();
return $http.get('localhost:8000', {
params: {result: result}
})
.then(function(result){
console.log(result);
})
};
};
vm.result in the Angular controller is for the result from the geoip2 Node module that performs the geolocation.
I can get the result in the console no problem but I'm not to sure how to pass it to Angular. I'm using the $http service but I'm not sure where to go from here...?
How do I pass the result from the geoip2 Node module to my Angular controller with $http?
The problem is that you are calling next before you are even done.
app.get('/', function (req, res, next) {
//next(); this line should be commented
// geolocation
geoip2.lookupSimple(ip, function(error, result) {
if (error)
return res.status(400).json({error: 'Something happened'});
return res.send(result);
});
});
Then on angular
$http({
method: 'GET',
url: '/yourURL'
}).then(function (response) {
console.log(response);
});
If you want to use the user IP to get location:
app.get('/', function (req, res, next) {
//next(); this line should be commented
// geolocation
var ip = req.headers['x-forwarded-for'] ||
req.connection.remoteAddress ||
req.socket.remoteAddress ||
req.connection.socket.remoteAddress;
geoip2.lookupSimple(ip, function(error, result) {
if (error)
return res.status(400).json({error: 'Something happened'});
return res.send(result);
});
});
I am new to MEAN applications.Here I have a REST based sample application using node-restful library in which I can perform operations(get,save,delete) except 'put'. However 'put' operation works well on rest clients (advanced REST, postman) but not on angular client.
mongoose Model
var restful = require('node-restful');
var mongoose = restful.mongoose;
// Schema
var productSchema = new mongoose.Schema({
name: String,
college: String,
age: Number
});
// Return model
module.exports = restful.model('Products', productSchema);
Node-express code
var express = require('express');
var methodOverride = require('method-override');
var mongoose = require('mongoose');
var bodyParser = require('body-parser');
var cors =require('cors');
// MongoDB
mongoose.connect('mongodb://localhost/rest_test');
var autoIncrement = require('mongoose-auto-increment');
// Express
var app = express();
app.use(methodOverride('_method'));
app.use(cors());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
// Routes
app.use('/api', require('./routes/api'));
// Start server
app.listen(4000);
console.log('API is running on port 4000');
angular function to update the data
$scope.updateData = function (userID) {
$scope.actionData = {
"name": $scope.name,
"college": $scope.college,
"age": $scope.age
}
RoleEditF.updateUserData({
userId: userID
}, $scope.actionData).then(function (response) {
$scope.userData = response;
console.log($scope.userData)
$scope.getData();
}).catch(function (response) {
$scope.error = "Unable to get Files (code: " + response.status + "). Please try later.";
});
}
angular.module('myapp')
.factory('RoleEditF', function (updateS) {
return {
updateUserData: function (parm, data, callback) {
var cb = callback || angular.noop;
return updateS.save(parm, data,
function (res) {
return cb(res);
},
function (err) {
return cb(err);
}.bind(this)).$promise;
}
}
})
Factory to call API
angular.module('myapp')
.factory('updateS',function($resource) {
return $resource('http://localhost:4000/api/products/:userId', { userId: '#userId' }, {
update: {
method: 'PUT'
}
}, {
stripTrailingSlashes: false
});
});
I'm getting following error on browser
"NetworkError: 404 Not Found - http://localhost:4000/api/products/57161e0fe4fbae354407baa3"
it has to be 'update' in
'update': {
method: 'PUT'
}
inside your $resource() factory
documentation here
https://docs.angularjs.org/api/ngResource/service/$resource
under Creating a custom 'PUT' request
I have an angular-fullstack app generated from here -
https://github.com/angular-fullstack/generator-angular-fullstack
I am using the same directory structure as angular-fullstack.
Now I am trying to authenticate users with facebook sdk and did the following steps -
1) specify passport facebook login strategy
// created auth/facebook/index.js
'use strict';
var express = require('express');
var passport = require('passport');
var auth = require('../auth.service');
var router = express.Router();
router
.get('/', passport.authenticate('facebook', {
scope: ['email', 'public_profile', 'user_friends', 'user_events'],
failureRedirect: '/',
session: false
}))
.get('/callback', passport.authenticate('facebook', {
failureRedirect: '/',
session: false
}), auth.setTokenCookie);
module.exports = router;
// created auth/facebook/passport.js
var passport = require('passport');
var FacebookStrategy = require('passport-facebook').Strategy;
var config = require('../../config/environment');
var jwt = require('jsonwebtoken');
exports.setup = function (User, config) {
passport.use(new FacebookStrategy({
clientID: config.facebook.clientID,
clientSecret: config.facebook.clientSecret,
callbackURL: config.facebook.callbackURL
},
function(accessToken, refreshToken, profile, done) {
User.findOne({'facebookId':profile.id}, function(err, user){
if(err) return done(err);
if(user) {
return done(null, user);
} else {
var newUser = {};
newUser['facebookId'] = profile.id;
newUser['providerData'] = {
name: 'facebook',
username: profile.username,
displayName: profile.displayName,
gender: profile.gender,
profileUrl: profile.profileUrl
};
newUser['name'] = profile.name.givenName ? profile.name.givenName: '';
newUser['email'] = profile.emails.length>0? profile.emails[0].value : done('email not found');
function generatePassword() {
var length = 8,
charset = "abcdefghijklnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
retVal = "";
for (var i = 0, n = charset.length; i < length; ++i) {
retVal += charset.charAt(Math.floor(Math.random() * n));
}
return retVal;
}
newUser['password'] = generatePassword();
newUser['role'] = 'user';
var user = new User(newUser);
user.save(function (err, user) {
if (err) { console.log(err); done(err); }
var token = jwt.sign({_id: user._id }, config.secrets.session, { expiresInMinutes: 60 * 5 });
res.json({ token: token });
});
}
});
}
));
};
// added entry in auth/index.js for facebook module
'use strict';
var express = require('express');
var passport = require('passport');
var config = require('../config/environment');
var User = require('../api/user/user.model');
// Passport Configuration
require('./local/passport').setup(User, config);
require('./facebook/passport').setup(User, config);
var router = express.Router();
router.use('/local', require('./local'));
router.use('/facebook', require('./facebook'));
module.exports = router;
In client side I made the following changes -
// installed ng-facebook from https://github.com/GoDisco/ngFacebook using bower install ng-facebook
// added ngFacebook in Angular App module
// set App Id in app.config -
$facebookProvider.setAppId('XXXXXXXXXXXX');
Then added this - in app.run
app.run(function ($rootScope, $location, Auth) {
(function (d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) {
return;
}
js = d.createElement(s);
js.id = id;
js.src = "//connect.facebook.net/en_US/sdk.js";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
// Redirect to login if route requires auth and you're not logged in
$rootScope.$on('$stateChangeStart', function (event, next) {
Auth.isLoggedInAsync(function (loggedIn) {
if (next.authenticate && !loggedIn) {
$location.path('/access/signin');
}
});
});
})
Then finally calling /auth/facebook from my client now I am getting the data from facebook after a user logs in and I am able to save it in Database, but the homepage always gets redirected to login state and not the dashboard.
I have the following http interceptors in my client app -
app.factory('authInterceptor', function ($rootScope, $q, $cookieStore) {
return {
// Add authorization token to headers
request: function (config) {
config.headers = config.headers || {};
if ($cookieStore.get('token')) {
config.headers.Authorization = 'Bearer ' + $cookieStore.get('token');
}
return config;
},
// Intercept 401s and redirect you to login
responseError: function (response) {
if (response.status === 401) {
$cookieStore.remove('token');
return $q.reject(response);
}
else if (response.status === 403) {
$cookieStore.remove('token');
return $q.reject(response);
} else if (response.status === 405) {
$cookieStore.remove('token');
return $q.reject(response);
}
else {
return $q.reject(response);
}
}
};
})
Now after I login with my facebook account, when I receive the callback from facebook. It is getting redirected to the same login state again even when the API /api/users/me is giving me the logged in information in my browser console -
{"_id":"569bc8d6b0c2e8315539539e","facebookId":"XXXXX","name":"Harshit","email":"XXXX","__v":0,"providerData":{"name":"facebook"},"messages":[],"notifications":[],"subjects":[],"date":"2016-01-17T17:01:10.000Z","role":["user"]}
So, I am thinking passport is not setting the authorization headers properly or there is something other than my http interceptor that is redirecting me to the login page only
How can I debug this issue or find out where I am going wrong, or missing something ?
In my opinion I prefer just checking whether the server still has your sesssion information.
In your routing you can do check
when('/url', {
templateUrl: 'partial',
controller: 'controller',
resolve: { loggedin: checkLoggedin}}).
Just create your function checkLoggedin like this
var checkLoggedin = function($q, $http, $location){
var deferred = $q.defer();
$http.get('/loggedin').then(function(response){
deferred.resolve();
},function(response){
deferred.reject();
$location.path('/home');
});
return deferred.promise;
}
Basically your making a promise here. Your saying here go to the backend and check I will wait for the backend to send me back a response. If the response is 200 let them on the page or else redirect to home.
Your back-end should return a 401 (unauthorize) or 200 (success) response letting your front-end know whether to let the user on the page.
function(req,res){
if (req.isAuthenticated() == true) {
res.status(200).send("Authenticate.");
}else{
res.status(401).send("Not Authenticate");
}
}
You can check more info about resolve here
Angular Route Provider
The resolve will run all dependencies that is passed to it before loading your view.
This will allow you to make a http request to your back end and verify with passport to see if the session is still there.
Angular has to make a http request to the backend everytime to make sure that the user is indeed logged in.
I am sending http request and when that request is finished I am trying to go to another state but the problem is it does not goes in the success callback I thought my be I'm getting an error so I wrote the error callback it does not goes in that to. Can anybody tell me what am I doing wrong
$scope.submitUpdatedData= function(user){
debugger;
// $http.post('/url',{params: value}).sucess(function(){
API.updateRecord(user).success(function(res){
$state.go('app' ,{} , {reload: true });
console.log("Hello");
});
}
The API code is given below. Here I invoke the http call
.factory('API', function($http) {
var api = {};
var baseURL = 'http://localhost:3000';
api.addRecord = function(record) {
console.log(record);
// $http.post(baseURL + '/addData', {name: record.name}.);
return $http.post(baseURL + '/addData', {rec:record});
};
api.deleteRecord = function(id){
return $http.get(baseURL +'/delete/' + id );
};
api.updateRecord = function(user){
return $http.post(baseURL + "/update/" ,{rec:user});
};
api.getAllRecord = function(){
return $http.get(baseURL+'/getAll');
};
api.getOneRecord = function(id){
return $http.get(baseURL + '/getOne/' + id)
};
return api;
})
UPDATE
I have replaced the .success part with then but it still not works
Second Update
This is my server side code
var express = require('express');
var mongoose = require('mongoose');
var util = require('util');
var bodyParser = require('body-parser')
var app = express();
var Schema = mongoose.Schema;
require('node-monkey').start({host: "127.0.0.1", port:"50500"});
var allowCrossDomain = function(req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.header('Access-Control-Allow-Headers', 'Content-Type');
next();
};
app.use( bodyParser.json() );
app.use(bodyParser.urlencoded({ // to support URL-encoded bodies
extended: true
}));
// app.use(express.json()); // to support JSON-encoded bodies
// app.use(express.urlencoded()); // to support URL-encoded bodies
app.use(allowCrossDomain);
// app.use('/' , require('./index'))
mongoose.connect('mongodb://localhost:27017/myappdatabase');
var userSchema = new Schema({
name: String,
password: String
});
var Todo = mongoose.model('Todo', userSchema);
app.get('/getAll' , function(req, res){
Todo.find({} , function(err , todos){
if (err){
res.send(err);
}
console.log(todos);
res.send(todos);
});
});
app.get('/delete/:name' , function(req , res){
console.log(req.params);
console.log(req.params.name);
Todo.remove({
name : req.params.name
}, function(err, todo) {
if (err)
res.send(err);
// get and return all the todos after you create another
Todo.find(function(err, todos) {
if (err)
res.send(err)
res.json(todos);
});
});
});
app.get('/getOne/:id' , function(req , res){
Todo.find({name : req.params.id}, function(err, todo) {
if (err)
res.send(err);
res.send (todo[0]);
// get and return all the todos after you create another
});
});
app.post('/update', function(req , res){
console.log(req.param('rec').name);
Todo.update({_id:req.param('rec').id} , {$set : {name:req.param('rec').name , password:req.param('rec').password}} , function(err){
if(err)
res.send("Error occured");
res.send("true");
});
});
app.post('/addData' , function(req , res){
console.log( req.param('rec').name);
var p = new Todo({name: req.param('rec').name , password: req.param('rec').password});
p.save(function(err){
if(err){
res.send(err);
console.log(error);
}
res.json(p);
});
});
var server = app.listen(3000, function () {
var host = server.address().address;
var port = server.address().port;
console.log('Example app listening at http://%s:%s', host, port);
});
// module.exports = app;
Seems like success and error are deprecated, you should use then instead:
API.updateRecord(user).then(function(res){
$state.go('app' ,{} , {reload: true });
console.log("Hello");
});
The $http legacy promise methods success and error have been
deprecated. Use the standard then method instead. If
$httpProvider.useLegacyPromiseExtensions is set to false then these
methods will throw $http/legacy error.
Source here
Seems like your request is never answered by the API Server. Maybe you can set a timeout for your request. Here it says you can do:
$http.post(url, data, {timeout: 100});
That should timeout your request after 100ms.