Authentication and Routing with Express/Passport, AngularJS and ensureLoggedIn not working on "/" url

I have a MEAN stack app generated with the Yeoman Generator Angular Fullstack generator. You should only have access to the site by logging in.

The repo for ensureLoggedIn is here

While logged out, if I try to navigate to '/products' I'm redirected to '/login' just fine. However, I'm having an issue redirecting users who aren't logged in when the url is '/' or even 'localhost:9000' without the slash.

If I'm not logged in, and at the login screen, and I modify '/login' to just '/' or '' I'm sent to "main" in AngularJS and treated as logged in(I'm assuming because it recognizes the session?) and able to click links through to '/products' or '/users'.

My current routes.js looks like this:

/**
 * Main application routes
 */

'use strict';

var errors = require('./components/errors');
var auth = require('./controllers/auth');
var passport = require('passport');
var ensureLoggedIn = require('connect-ensure-login').ensureLoggedIn;
module.exports = function(app) {

  // Insert routes below

  // All undefined asset or api routes should return a 404
  app.route('/:url(api|auth|components|app|bower_components|assets)/*')
   .get(errors[404]);

  app.route('/login')
    .get(auth.login)
    .post(auth.loginUser);
  app.route('/logout')
    .get(auth.logout);
  // All other routes should redirect to the index.html
  app.all('*', ensureLoggedIn('/login'));
  app.route('/*')
    .get(function(req, res) {
      res.sendfile(app.get('appPath') + '/index.html');
    });
};

I've also tried this with the routes:

  app.route('/*')
    .get(function(req, res) {
      res.sendfile(app.get('appPath') + '/index.html');
    });

Which seems to have the same behavior as placing ensureLoggedIn in app.all.

Here's a snippet of my routing on the Angular side, which uses ui-router:

.config ($stateProvider, $urlRouterProvider, $locationProvider) ->
  $httpProvider.interceptors.push('httpInterceptor')

  $urlRouterProvider
  .otherwise '/'

  $stateProvider
  .state 'main',
    url: '/'
    templateUrl: 'app/views/main/main.html'
    controller: 'MainCtrl'
  .state 'users',
    url: '/users'
    templateUrl: 'app/views/users/index.html'
    controller: 'UsersController'

As I said, the redirect works fine on '/users'. I'm not sure if this is a routing issue or auth issue. Auth should be fine, since clicking logout does take you to login screen and restricts access, but doesn't restrict access to the '/' route.

For the views, the login.jade is actually on the server side and the form is processed there. Except for a 404.jade, all other views are on the client-side and served using ui-router.

I feel like I'm overlooking something basic. Or just don't fully understand how this is working together. Any help would be great.

EDIT:

One thing I tried was changing the routing before app.route('login'):

  app.route('/')
    .get(function(req, res) {
      res.render('login');
    });

And changing ui-router url for main from '/' to '/main'.

This still grabbed index.html from angular and logged me in, so it didn't work. I also tried res.redirect to login in routes.js and it didn't redirect.

This is the code I use for handling authentication. It is a hack but I didn't find a better way when I needed to code it. Also the routes defined in the system varied user to user so I couldn't define them in the normal config stage. This may help with your issue though.

 $routeProvider
    .when("/login", { templateUrl: "/view/account/login.html", controller: Login })
    .when("/forgottenpassword", { templateUrl: "/view/account/forgottenpassword.html", controller: ForgottenPassword })
    .otherwise({ redirectTo: "login" });

This basically only allows access to 2 views. Once someone authenticates successfully I rebuild the routing table with the new valid views. Any invalid navigation goes to the login view.

I do this through a hack though so it might not be the best implementation angularjs wise. I do this by keeping a reference to $routeProvider on the window object then use $routeProvider as normal when you have a successful logon.

The original $routeProvider provided in angular also needs a public method to clear the existing routes before adding new ones.

After

  var routes = {};

Add

  this.ClearRoutes = function ()
  {
      routes = {};
  }

Example usage after successful logon

$routeProvider.ClearRoutes();

$routeProvider
    .when("/home", { templateUrl: "/view/home.html", controller: Home })
    .when("/logoff", { templateUrl: "/view/account/logoff.html", controller: Logoff })
    .otherwise({ redirectTo: "home" });