How to restrict routes with $routeProvider - angularjs

In a MEAN app, I have an authService module with an Auth factory which contains an authFactory.isLoggedIn function:
// check if a user is logged in
// checks if there is a local token
authFactory.isLoggedIn = function() {
if (AuthToken.getToken())
return true;
else
return false;
};
So I thought I could use this with the resolve property of $routeProvider like this:
var MyModule = angular.module('app.routes', ['ngRoute']);
MyModule.config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) {
$routeProvider
// route for the home page
.when('/', {
templateUrl : 'app/views/pages/home.html'
})
// login page
.when('/login', {
templateUrl : 'app/views/pages/login.html',
controller : 'mainController',
controllerAs: 'login'
})
// register page
.when('/register', {
templateUrl: 'app/views/pages/register.html',
controller: 'userCreateController',
controllerAs: 'register'
})
// upload page
.when('/upload', {
templateUrl : 'app/views/pages/upload.html',
controller: 'uploadController',
controllerAs: 'userupload',
resolve: function($q, $location) {
var deferred = $q.defer();
deferred.resolve();
if (!Auth.isLoggedIn) {
$location.path('/login');
}
return deferred.promise;
}
})
//logout
.otherwise({redirectTo: '/'});
$locationProvider.html5Mode(true);
}]);
Unfortunately this doesn't work to stop unauthenticated users accessing the upload page and I don't see any errors being reported.
I have seen instances of simpler ways to do this eg:
.when('/upload', {
templateUrl : 'app/views/pages/upload.html',
controller: 'uploadController',
controllerAs: 'userupload',
isLoggedIn: true
})
But that doesn't work either, which is a shame as it's far simpler.

In the end I was determined to use the resolve property of $routeProvider so after experimenting with the solution on http://midgetontoes.com/blog/2014/08/31/angularjs-check-user-login
I came up with:
var MyModule = angular.module('app.routes', ['ngRoute']);
MyModule.config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) {
var onlyLoggedIn = function($location, $q, Auth) {
var deferred = $q.defer();
if (Auth.isLoggedIn()) {
deferred.resolve();
} else {
deferred.reject();
$location.url('/login');
}
return deferred.promise;
};
$routeProvider
// route for the home page
.when('/', {
templateUrl : 'app/views/pages/home.html'
})
// login page
.when('/login', {
templateUrl : 'app/views/pages/login.html',
controller : 'mainController',
controllerAs: 'login'
})
// register page
.when('/register', {
templateUrl: 'app/views/pages/register.html',
controller: 'userCreateController',
controllerAs: 'register'
})
// upload page
.when('/upload', {
templateUrl : 'app/views/pages/upload.html',
controller: 'uploadController',
controllerAs: 'userupload',
resolve:{loggedIn:onlyLoggedIn}
})
//logout
.otherwise({redirectTo: '/'});
$locationProvider.html5Mode(true);
}]);
I am sure this isn't as good as the custom http interceptor as posited by #Dimitiri Algazin or as simple as the solution from #Pasan Ratnayake but it does fulfil my quest to use resolve. Thanks to #Dimitri and #Pasan anyway.

Add custom http interceptor. This is not exact code, just algorithm, some syntax might missing:
.factory('myHttpInterceptor', function($q, $location, AuthToken) {
function isLoggedIn() {
return !!AuthToken.getToken();
}
function canRecover(response) {
var status = response.status;
var config = response.config;
var method = config.method;
var url = config.url;
console.log("--->>> ", method, status, url);
if (status == 401) {
alert("401");
} else if ( status == 403) {
alert("403");
} else if (status == 404) {
alert("404");
} else if (status == 405) {
alert("405");
} else if (status == 500) {
alert("500");
} else {
}
return response;
}
return {
// optional method
'request': function(config) {
if (isLoggedIn()) {
return config;
} else {
$location.path('login');
}
},
// optional method
'response': function(response) {
// do something on success
return response;
},
// optional method
'requestError': function(rejection) {
return $q.reject(canRecover(rejection));
},
// optional method
'responseError': function(rejection) {
return $q.reject(canRecover(rejection));
}
};
})
.config(['$httpProvider', function($httpProvider) {
$httpProvider.interceptors.push('myHttpInterceptor');
}])

There are multiple ways you could achieve this functionality.
Easiest would be to add a check similar to below code to each controller that you don't want your users to access.
// You could re-direct the user to a '401' state as well
if (!authFactory.isLoggedIn())
$state.go('login');

Related

URL path shows undefined - issue after redirect on $location in AngularJS

Here i'm working on a login app using AngularJS. After authentication, when I try to redirect to home page using $location, first the url will change to '/home' but suddenly the path change and shows '/undefined'. Following is my code:
var app = angular.module('app', ['ngRoute', 'ngCookies']);
var currentURL = location.protocol+'//'+location.hostname+':'+location.port;
app.constant("customConstants", {"value": "false","url": currentURL});
app.config(function($routeProvider) {
$routeProvider
.when('/', {
templateUrl : 'usrlib/html/login.html',
controller : 'loginController'
})
.when('/login', {
templateUrl : 'usrlib/html/login.html',
controller : 'loginController'
})
.when('/home', {
templateUrl : 'usrlib/html/home.html',
controller : 'homeController'
})
.otherwise({
redirectTo: '/'
});
});
app.controller('loginController', function($rootScope, $scope, $http, $route, $location, customConstants) {
console.log("Inside loginController");
$scope.authenticate = function () {
var userdetails = {};
userdetails["username"]=angular.element('#username').val();
userdetails["password"]=angular.element('#password').val();
var config_json = {
headers : {
'Content-Type': 'application/json'
}
}
$http.post(customConstants.url+"/login",userdetails, config_json)
.then(function successCallback(response) {
console.log(JSON.stringify(response.data, null, "\t"));
var resp = response.data;
if(resp=="success"){
alert("Success login")
$location.path('/home');
}
else{
console.log("Login Failed");
}
}, function errorCallback(response) {
console.log(response.error);
});
};
});
app.controller('homeController', function(customConstants, $scope, $http) {
alert("Inside homeController");
$http.get(customConstants.url+"/home")
.then(function successCallback(response) {
console.log("overall_info :: "+JSON.stringify(response.data, "\t"));
}, function errorCallback(response) {
});
});
In browser it shows:
Can't find the issue, please help out.
You do not need.
$location.path('/home'); // Wrong
Try
$location.path('home'); // ryt
Just try to remove the $location Parameter from your function definition. because unless you invoke the function there is a chance of showing undefined,,

How can I prevent access to an Angular page if the user IS logged in with AngularFire?

I feel like this is really easy but I'm not sure why I can't figure it out.
For example if we want to restrict access to a page if a user is not logged in we can do something like:
// == LISTEN FOR ROUTE ERRORS
app.run(['$rootScope', '$location', function($rootScope, $location) {
$rootScope.$on('$routeChangeError', function(event, next, previous, error) {
if (error === 'AUTH_REQUIRED') {
$location.path('/login');
}
});
}]);
// == RETURN AUTH SERVICE
app.factory('Authentication', ['$firebaseAuth', function($firebaseAuth) {
return $firebaseAuth();
}]);
// == APP ROUTER
app.config(['$routeProvider', '$location', function($routeProvider, $location) {
$routeProvider
.when('/account', {
controller: 'userController',
templateUrl: 'views/account.html',
resolve: {
"currentAuth": ['Authentication', function(Authentication) {
return Authentication.$requireSignIn(); // if rejected throws $routeChangeError
}]
}
})
}]);
Now what if I want to add a resolve to the '/login' route so that if the user is logged in I can just force them to the account page or a success page?
.when('/login', {
controller: 'userController',
templateUrl: 'views/login.html',
resolve: {
"currentAuth": [function() {
// reject if the user is already logged in
}]
}
});
.when('/login', {
controller: 'userController',
templateUrl: 'views/login.html',
resolve: {
"currentAuth": ['$q', function($q) {
var p = $q.defer();
if (Authentication.$getAuth()) {
p.reject({code: someErrorCode, message: 'Already logged in'});
} else {
p.resolve();
}
return p.promise;
}]
}
});
You should also handle the scenario when logged in, in $routeChangeError or $stateChangeError

AngularJS UI router: Block view

Right now i am making an AngularJS+UI router install application. But i have a problem, the problem is, that i want to disable access to the views, associated with the install application. I want to do it in resolve in the state config.
But the problem is i need to get the data from a RESTful API, whether the application is installed or not. I tried making the function, but it loaded the state before the $http.get request was finished.
Here was my code for the resolve function:
(function() {
var app = angular.module('states', []);
app.run(['$rootScope', '$http', function($rootScope, $http) {
$rootScope.$on('$stateChangeStart', function() {
$http.get('/api/v1/getSetupStatus').success(function(res) {
$rootScope.setupdb = res.db_setup;
$rootScope.setupuser = res.user_setup;
});
});
}]);
app.config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise("/404");
$stateProvider.state('db-install', {
url: "/install/db",
templateUrl: 'admin/js/partials/db-install.html',
controller: 'DBController',
resolve: {
data: function($q, $state, $timeout, $rootScope) {
var setupStatus = $rootScope.setupdb;
var deferred = $q.defer();
$timeout(function() {
if (setupStatus === true) {
$state.go('setup-done');
deferred.reject();
} else {
deferred.resolve();
}
});
return deferred.promise;
}
}
})
.state('user-registration', {
url: "/install/user-registration",
templateUrl: "admin/js/partials/user-registration.html",
controller: "RegisterController"
})
.state('setup-done', {
url: "/install/setup-done",
templateUrl: "admin/js/partials/setup-done.html"
})
.state('404', {
url: "/404",
templateUrl: "admin/js/partials/404.html"
});
}]);
})();
EDIT:
Here is what my ajax call returns:
Try this way:
$stateProvider.state('db-install', {
url: "/install/db",
templateUrl: 'admin/js/partials/db-install.html',
controller: 'DBController',
resolve: {
setupStatus: function($q, $state, $http) {
return $http.get('/api/v1/getSetupStatus').then(function(res) {
if (res.db_setup === true) {
$state.go('setup-done');
return $q.reject();
}
return res;
});
}
}
})
Then inject setupStatus in controller:
.state('setup-done', {
url: "/install/setup-done",
templateUrl: "admin/js/partials/setup-done.html",
controller: ['$scope', 'setupStatus', function ($scope, setupStatus) {
$scope.setupdb = setupStatus.db_setup;
$scope.setupuser = setupStatus.user_setup;
}]
})

Yeoman Angular Fullstack - how to secure routes?

I have created a new route 'Rooms' with the generator and have modelled my $stateProvider on the admin route
.state('admin', {
url: '/admin',
templateUrl: 'app/admin/admin.html',
controller: 'AdminController',
controllerAs: 'admin',
authenticate: 'admin'
});
Vs
$stateProvider
.state('rooms', {
url: '/rooms',
templateUrl: 'app/rooms/rooms.html',
controller: 'RoomsCtrl',
controllerAs: 'rooms',
authenticate: 'admin'
});
But my route still appears without authentication!
I guess I am missing a few things to make it secure, though I am unable to understand what!
Can anyone help?
Thanks
your controller should be like:
angular.module('auth8App').controller('RoomsCtrl', function ($scope,Auth,$location) {
//check if the user is logged-in
Auth.isLoggedInAsync(function(loggedIn) {
if (!loggedIn) {
//if the user is not logged Redirect to login
event.preventDefault();
$location.path('/login');
}
});
$scope.message = 'Hello';
});
check if you have an interceptor service factorie defined and called in your app.js lik this:
.config(function ($stateProvider, $urlRouterProvider, $locationProvider, $httpProvider) {
$urlRouterProvider
.otherwise('/');
$locationProvider.html5Mode(true);
$httpProvider.interceptors.push('authInterceptor');
})
.factory('authInterceptor', function ($rootScope, $q, $cookieStore, $location) {
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) {
$location.path('/login');
// remove any stale tokens
$cookieStore.remove('token');
return $q.reject(response);
}
else {
return $q.reject(response);
}
}
};
})
.run(function ($rootScope, $location, Auth) {
// 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) {
event.preventDefault();
$location.path('/login');
}
});
});
});

How to set my initial page to a Login html page

In my AngularJS SPA, I would like to force a sort of splash page with a login form. Once the user is authenticated, then I would like to load the full website.
Here is my app.config, which currently triggers a modal logon. However, this is not the right solution for my application. I do not want to load any nav bars UNTIL the user is fully logged in.
angular
.module('rage')
.config(config);
function config($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise("/dashboard");
$stateProvider
.state('corp', {
url: "/corp",
templateUrl: "app/views/corp.html",
data: { pageTitle: 'RAGE' }
})
.state('maint', {
url: "/maint",
templateUrl: "app/views/maint.html",
data: { pageTitle: 'RAGE' }
})
.state('main', {
url: "/dashboard",
templateUrl: "app/views/dashboard.html",
controller: 'MainCtrl',
data: { pageTitle: 'RAGE', requireLogin: true },
resolve: {
authUser: ['$rootScope', 'loginService', 'userService', function ($rootScope, loginService, userService, authUser) {
return loginService.loginModal().then(function (user) {
$rootScope.userID = user.userId;
initSession(user, $rootScope, loginService, userService);
return user;
})
}]
}
})
.state('login', {
url: "/login",
templateUrl: "app/components/login/login.html",
controller: 'LoginCtrl'
})
}
function initSession(user, $rootScope, loginService, userService) {
userService.getInitParams().then(function (envJson) {
$rootScope.rageSessionVars = envJson;
userService.initRazor(envJson).then(function (data) {
var response = data.status;
if (response.match(/SUCCESS/g)) {
userService.openUserSession(razorEnvJson).then(function (data) {
// ...
});
}
});
});
}
Should I handle this in my app.config ?
Any advice is appreciated.
thanks,
Bob
You can do one thing.Make a state like below.
state("context", {
url:"/context",
template: '',
controller : "contextCtrl"
}).
And in 'contextCtrl' you should check if the user is authenticated or not like below
app.controller("contextCtrl",function($scope, $rootScope, userLoginService,$state, SessionService){
if(SessionService.getSessionUser()==null){
$state.go("login");
}else{
if(SessionService.getSessionUser().authorities[0].authority=="ROLE_ADMIN"){
$state.go("dashboard");
}
}
});
SessionService in above code is your localStorageService which stores the logged in user. Now in dashBoardCtrl of dashboard state (MainCtrl in your case) you should check on every request whether the user is authenticated or not. Following service checks the response of every request.If the response's status is 403,page will be redirected to login page.
service.factory('APIInterceptor', function($q) {
return {
'response': function(response) {
// do something on error
if(response.status == 302 ){
window.location.href = contextPath;
return $q.reject(response);
}
return response;
},
'responseError': function (rejection) {
if(rejection.status === 0) {
location.reload();
}
if(rejection.status == 403 ){
window.location.href = contextPath+'/#/login';
return $q.reject(response);
}
return $q.reject(rejection);
}
};
})
With this,if the user is not authenticated he will be redirected to login page.Hope it helps you.

Resources