3

In a single page application using angular routing, how can I redirect a page after an api call. In my case, I want to redirect the user to the profile page after they have called the login api. So this is what I thought would work but it isn't.

On the client, main.js. I have the angular routing set up

app.config(function($routeProvider){   $routeProvider
//the home page display
.when('/', {
  templateUrl: 'main.html',
  controller: 'mainController'
})

.when('/login', {
  templateUrl: 'login.html',
  controller: 'loginController'
})

.when('/signUp', {
  templateUrl: 'signUp.html',
  controller: 'signUpController'
})

.when('/profile', {
  templateUrl: 'profile.html',
  //controller: 'mainController'
}); });

and from my controller I call the /login post api

app.controller('authController', function($scope, $http, $rootScope, $location){   
  $scope.user = {username: '', password: ''};   
  $scope.error_message = '';

  $scope.login = function(){
    $http.post('/login', $scope.user).success(function(data){
      if(data.state == 'success'){
        //set username authenticated property to true after successful log in
        //I am only pasting some of my code here, more logic before controller
        $rootScope.authenticated = true;
        $rootScope.current_user = "james";
        $location.path('/profile');
      }
      else{
        $scope.error_message = data.message;
      }
    });   
  }; 
});

and here is my login api

router.post('/login', passport.authenticate('local-login', {
        successRedirect : '/success', // redirect to the secure profile section
        failureRedirect : '/failure', // redirect back to the signup page if there is an error
        failureFlash : true // allow flash messages
    }));

and when it succeeds, it calls success which sends back the data which should trigger the callback in $http.post and redirect the page through $location.path('/profile');. However, the callback isn't called and my page displays the user information from res.send

//sends successful login state back to angular
    router.get('/success', function(req, res){
        res.send({state: 'success', user: req.user ? req.user : null});
    });

Am I on the right track? I am just following microsoft's tutorial https://www.microsoftvirtualacademy.com/en-us/training-courses/mean-stack-jump-start-8442 but their completed page on github doesn't even work so it doesn't help me debug this problem of mine.

NathanSuzuki
  • 157
  • 2
  • 8
  • Why aren't you using `res.redirect()`? – brandonscript Jul 18 '15 at 00:46
  • 1
    @remus I tried it doesn't work. I get 404 error page not found. I assume res.redirect calls the express routing which i guess will look for profile.ejs but I want to redirect to the profile.html of angular routing – NathanSuzuki Jul 18 '15 at 00:50
  • Ah. http://stackoverflow.com/questions/19370606/node-js-catch-all-route-with-redirect-always-renders-index-page-with-angular-reg – brandonscript Jul 18 '15 at 00:52
  • Have you tried putting a break point inside your $http callback to see what the value of data is? – Jesse Carter Jul 18 '15 at 00:55
  • Im sorry. I followed your suggestions are set breakpoints and realized I made a dumb mistake, I never called login(). My bad. Appreciate your time and help – NathanSuzuki Jul 18 '15 at 01:18

3 Answers3

3

Using successRedirect and failureRedirect in passport will redirect the client to the specified pages, which will prevent your client-side angularJS routing from taking place. The reason you're seeing the user info after logging in is because your client is being redirected to the /success page, rather than actually responding to the original request. The client then fetches the success page with a GET request, and the new GET request is then responded to with the user info.

I would suggest leaving the node.js redirects out when using AngularJS, since you probably want to handle redirection on the client side:

router.post('/login', passport.authenticate('local-login'), function(req, res){
    res.send(req.user);
});

The inline function will never execute if the user is not authenticated. Instead, passport will respond directly with a 401 error status, with a body of "Unauthorized". Therefore the success state is not required. On the client side, you should use the .error() function to deal with 401 errors, rather than checking your state variable:

$http.post('/login', $scope.user).success(function(user){
    $rootScope.authenticated = true;
    $rootScope.current_user = "james";
    $location.path('/profile');
  })
 .error(function(err){
    $scope.error_message = err;
  });

If you want to pass back a more specific reason as to why the request was unauthorized (which is not always a good idea), you can use flash messages, and issue another GET request from angular to get a more detailed authorization failure message.

veggiesaurus
  • 642
  • 1
  • 4
  • 14
  • @veggiesaurus How to pass flash messages. normally without angular I passed like req.flash(); but when Angular is used how to do. http://stackoverflow.com/questions/42903783/how-to-send-req-flash-messages-from-node-to-angular-js – charan tej Mar 22 '17 at 06:15
0

You seem to have a slight impedance mismatch on what the front-end and back-end want to do here. Your AngularJS code expects to make a POST to the API endpoint and get back a 200 (success) along with some JSON data which tells it about the success or failure of the login attempt.

The back-end, thinks it's going to receive a POST and then redirect the caller to a new location. At least that's the way I'm reading it. It's not simply sending back some data with an HTTP response code of 200 or an error code.

I think you want to tweak the back-end code to simply return data to the front-end to get the result you expect.

John Munsch
  • 19,530
  • 8
  • 42
  • 72
  • isn't that what my success api is doing? router.get('/success', function(req, res){ res.send({state: 'success', user: req.user ? req.user : null}); }); – NathanSuzuki Jul 18 '15 at 01:53
  • Hmm. Possibly so. You'll have to forgive that I'm very comfortable with AngularJS but much less so with Express. So let's try one thing just to confirm. Can you use a tool like Postman (https://chrome.google.com/webstore/detail/postman/fhbjgbiflinjbdggehcddcbncdddomop?hl=en) to make the POST for you and examine what gets returned to make sure it's not a redirect for the client but rather just a regular 200 response? – John Munsch Jul 18 '15 at 02:03
  • Or alternately, we can just flip over into the Chrome Developer Tools within the browser and look at the Network tab to see exactly what was sent and received for the API call (including the response code) if you'd rather do that. – John Munsch Jul 18 '15 at 02:08
0

So far I haven't seen success in making Ajax calls to API redirecting to a page. We have a similar situation where API call may result in redirecting to a error page. We wanted to handle that in the server rather than asking UI (Angular) to do it. But it's just frustrating to see none of the methods of redirect like res.redirect are working. Our scenario is Angular makes a API call through Ajax and API running on Node.js should redirect to a html page.

Nagaraj
  • 11
  • 1