How to perform routeGuard in angularJS - angularjs

Im building an app in angularJS where a user can Login to the the admin Panel.
Router config
app.config(["$routeProvider","$locationProvider",($routeProvider,$locationProvider)=>{
$locationProvider.hashPrefix("");
$routeProvider
.when("/",{
templateUrl: "app/views/enterticket.html"
})
.when("/adminlogin",{
templateUrl: "app/views/adminlogin.html",
controller: "adminlogin"
})
//panel is the admin section
.when("/panel",{
controller: "panel",
templateUrl: "app/views/panel.html",
})
}])
There's a service that authenticates the user to grant access to the admin panel,
auth service
app.service("auth",["$http","$location",function($http){
let api = (window.location.origin)+"/auth";
this.auth = function () {$http.get(api).then((rez)=>{
//is session is not set on server
if(!rez.data.state){
window.location.href = "#/adminlogin"
})}
}])
And finally theres the admin component
app.controller("panel",["$scope","$http","auth",function($scope,$http,auth){
auth.auth();
$scope.addTicket = ()=>{
let bearer = $scope.bearer;
let api = (window.location.origin)+"/addticket";
let postfields = JSON.stringify({"bearer":bearer});
$http.post(api,postfields).then((rez)=>{
console.log(rez);
})
}
}])
This setup is working well and there is restriction for the admin panel, however, when someone attempts to visit the admin panel without logging in, the browser flashes the admin panel template and then quickly reverts back to the admin login. Can someone suggest a way of preventing that awkward flash or even tell me a better way of implementing angularJS routeGuards, thank so much

You need to use resolve in when.
I forked another project and you need to tweak the code as per your requirement. (http://plnkr.co/edit/PAYTAr1sOoCmkP8q)
angular.module('app', ['ngRoute']);
angular.module('app')
.factory("authService", function($q, $timeout){
return {
verify: function(){
var deferred = $q.defer();
$timeout(function(){
// deferred.resolve(true); // if the user is allowed to access admin panel
deferred.reject(false); // if the user is not logged in
},1000);
return deferred.promise;
}
};
});
angular.module('app')
.controller('mainCtrl', function($scope) {
$scope.message = "Message in the main controller";
});
angular.module('app')
.controller('templateCtrl', function($scope) {
$scope.hello = " World";
});
angular.module('app')
.config(function($routeProvider, $locationProvider) {
$routeProvider.
when('/option1', {
templateUrl: 'template.html',
controller: 'templateCtrl',
resolve: {
greeting: function(authService, $location){
return authService.verify().then(function(result){
return true; // if the user is logged in, it will allow the user to access login panel
}, function(result){
$location.path('/login');// if not, it will redirect to login page
return false;
})
}
}
})
.when('/login', {
template: '<div>Login Message</div>'
})
});

Hey Alaksandar Gene i got your answer to work however it was still flashing the template. I guess the problem was from the resolve not awaiting the http call. So i decided to use cookies instead.
app.service("mainauth",function($http,$q,$timeout,$cookies){
this.mainauth = ()=>{
var defer = $q.defer();
let api = (window.location.origin)+"/auth";
if($cookies.get("ticket_admin") == null){
defer.resolve(false);
}
return defer.promise;
}
})
Then for the routing:
app.config(["$routeProvider","$locationProvider",($routeProvider,$locationProvider)=>{
$locationProvider.hashPrefix("");
$routeProvider
.when("/",{
templateUrl: "app/views/enterticket.html"
})
.when("/adminlogin",{
templateUrl: "app/views/adminlogin.html",
controller: "adminlogin"
})
.when("/panel",{
controller: "panel",
templateUrl: "app/views/panel.html",
resolve: {
mainauth: function(mainauth,$location){
mainauth.mainauth().then((rez)=>{
if(!rez){
window.location.href = "#/adminlogin"
}
})
}
}
})
}])
I suppose theres a risk of somebody imitating the cookies, and gaining access so ill put up another service to verify the session incase that happens
app.service("auth",["$http","$cookies",function($http,$cookies){
this.forceAuth = function(){
let url = (window.location.origin)+"/auth";
$http.get(url).then((rez)=>{
if(!rez.data.state){
window.location.href = "#/adminlogin"
}
})
}
}])
I can then inject the above service in the controller for added security.
PS some might wonder why i didnt inject a service that verifies the cookies directly into the controller, thats because it was still causing a flash

Related

Routing in angularjs - conditional routing in otherwise syntax

i have the following code in my route provider:
project.config(['$routeProvider','$locationProvider',
function ($routeProvider,$locationProvider){
$routeProvider.
when('/start',{
templateUrl: 'start.html',
controller: 'startController'
}).
when('/event',{
templateUrl: 'event.html',
controller: 'eventController'
}).
when('/report',{
templateUrl: 'report.html',
controller: 'reportController'
}).
otherwise({
redirectTo: '/'
});
I have a rootscope variable admin with value of either 1 or 0
My current code sends the user to the start page if an unknown value is given to the route provider. I want the route provider to redirect the user to the event page if the user is an admin or to the start page if the user isnt an admin when an unknown value is given to the route provider.
I was thinking something like this might work, but it didnt.
otherwise({
redirectTo: defaultRedirect
});
$rootScope.admin == 1 ? $rootScope.defaultRedirect = "/event" : $rootScope.defaultRedirect = "/start"
It throws me an error.
Help is appreciated. Thanks.
No it will not work like that. Instead what you can do is have resolve in otherwise block, where you can check if loggedin user is admin or not (by checking $rootScope variable or calling authentication service). If service resolves user being admin then you can use $location service to change route using $location.path("/event") or $location.path("/start"). The code look like:
$routeProvider.otherwise({
resolve: {
load: [
'$rootScope', '$q', 'authentication', '$location', function ($rootScope, $q, authentication, $location) {
var defer = $q.defer();
function chooseRoute() {
if (authentication.identity === undefined) {
$location.path('/logon');
} else {
$location.path('/somewhere');
}
defer.reject();
}
if (authentication.identityResolved)
chooseRoute();
else {
var unbind = $rootScope.$watch(function () {
return authentication.identityResolved;
}, function (resolved) {
if (!resolved)
return;
chooseRoute();
unbind();
});
}
return defer.promise;
}
]
}
});
Another way is, you can check event $routeChangeStart in config/run block & then accordingly checking if user is admin you can redirect user to specific routes using $location service.

How to restrict the page from redirect after login/ logout in Angularjs?

I need to restrict the user from redirect and need to login only with authentication.
I tried but I can redirect to login page using back button and again come to same page using forward button. Even I can go to the required page using URL without login.
My code :
config.$inject = ['$routeProvider', '$locationProvider'];
function config($routeProvider, $locationProvider ) {
$routeProvider
.when('/login', {
controller: 'LoginController',
templateUrl: 'view/login.view.html',
controllerAs: 'vm'
})
.when('/profileData', {
controller: 'profileDataController',
templateUrl: 'view/profiledata.view.html',
controllerAs :'vm'
})
.when('/questionBank', {
controller: 'questionbankController',
templateUrl: 'view/questionbank.view.html',
controllerAs: 'vm'
})
.when('/dashboard', {
// controller: 'PersonalInfoController',
templateUrl: 'view/dashboard.view.html',
controllerAs:'vm'
})
.otherwise({ redirectTo: '/login' });
}
run.$inject = ['$rootScope', '$location', '$cookieStore', '$http'];
function run($rootScope, $location, $cookieStore, $http) {
// keep user logged in after page refresh
$rootScope.globals = $cookieStore.get('globals') || {};
if ($rootScope.globals.currentUser) {
$http.defaults.headers.common['Authorization'] = 'Basic ' + $rootScope.globals.currentUser.authdata; // jshint ignore:line
}
$rootScope.$on('$locationChangeStart', function (event, next, current) {
//redirect to login page if not logged in and trying to access a restricted page
var restrictedPage = $.inArray($location.path(), ['/dashboard','/questionBank', '/profileData']) === -1;
/* var a = $location.$$absUrl.split('#')[1];
var patt = new RegExp(a);
var res = patt.test(restrictedPage); */
var loggedIn = $rootScope.globals.currentUser;
if (restrictedPage && !loggedIn) {
$location.path('/login');
}
});
}
use this :based on response from server
.when('/login', {
controller: 'LoginController',
templateUrl: 'view/login.view.html',
resolve:{
logincheck: checklogedin
})
/ resolve function for user....
var checklogedin = function($q ,$http,$location)
{
var deferred =$q.defer();
$http.get('/loggedin').success(function(user){
if (user.staus==true)
{
//goo
deferred.resolve();
}
else
{
deferred.reject();
$location.url('/login');
}
});
return deferred.promise
};
Based on the code that you have provided, I can't tell 100% what is going on in your code. But... you could always try to use the resolve property on each route that you don't want to allow access without authentication. Here is what that would look like for questionBank:
.when('/questionBank', {
controller: 'questionbankController',
templateUrl: 'view/questionbank.view.html',
controllerAs: 'vm',
resolve: {
auth: function(AuthService, $q){
if(AuthService.isAuthenticated()) return $q.resolve();
return $q.reject();
}
}
})
Each property of the resolve object should return a promise, and if that resolves... the route change works. If it rejects... the route change is not allowed. If the promise never resolves, you are screwed, so make sure it resolves or it will never do the route.
This isn't the only way to try what you are saying. It is A way of trying it.
You can also add event listener on your $scope and prevent moving in case of unauthenticated user.
$scope.$on('$locationChangeStart', function (event, next, current) {
if (!is_logged_in) {
event.preventDefault();
}
});
In my code I have two main controllers LoginCtrl and AppCtrl, and all other controllers are nested within AppCtrl. Then in AppCtrl I have this code, which will check for logged user.
if (localStorageService.get('authToken') === null) {
$state.go('login', {locale: CONFIG.defaultLang});
} else if (!userService.isLoggedIn()) {
tokenStorage.setAuthToken(localStorageService.get('authToken'));
userService.setIdentity(JSON.parse(localStorageService.get('user')));
}
As you can see I store auth token from server in local storage. When page loades this code will be executed and if you are not logged in you will be redirected. And because all other application controllers are nested within AppCtrl this code will be executed every time.
For more info about nested controllers try for example this article - https://rclayton.silvrback.com/parent-child-controller-communication

Check login status in routeProvider

I'm working on an Angular project but I'm having a little problem and I'd appreciate any help because I'm really lost. Basically I need to check if a user is logged in, and if he is, he should not be allowed to access a certain view/route, here's the code I'm using for this:
'use strict';
angular.module('testApp')
.config(function ($routeProvider) {
$routeProvider
.when('/registroVisitante', {
template: '<registro-visitante></registro-visitante>',
resolve: {
"check": function(Auth, $location) {
console.log(Auth.isLoggedIn());
if (!Auth.isLoggedIn()) {
alert("Access allowed");
} else {
$location.path('/'); //redirect user to home.
alert("Access denied");
}
}
}
});
});
The problem itself is that, this is actually working, but only when I try to access the route via an anchor click or ng-click or whatever, however, when I type the route in the address bar it lets me access but it shouldn't, anyone has any idea why?
Try this
angular.module('testApp')
.config(function ($routeProvider) {
$routeProvider
.when('/registroVisitante', {
template: '<registro-visitante></registro-visitante>',
resolve: {
"check": function (Auth, $location) {
Auth.isLoggedIn(function (response) {
console.log(response);
if (response) {
$location.path('/'); //redirect user to home.
}
});
}
}
});
});
The problem was that it was an asynchronous request and maybe when it got to the if it was undefined.

AngularJS routing pulling main page content even when user not logged in

I am new to AngularJS. I made a simple app that have a login function using AngularJS. I used routing and on resolve i put some logic to check if user is logged in and then only proceed accordingly. I have everything working fine, the problem is, when i am not logged in, if i browse to /home it doesn't load the main.html page(that's how it's supposed to be) but a GET request gets called and that returns content of main.html in console.My code looks like this:
app.config(function($routeProvider){
$routeProvider.when('/', {
templateUrl: 'partials/login.html',
controller: 'LoginCtrl',
resolve:{
test: function($http, $q,$location){
var defer = $q.defer();
//checks if user is logged and returns boolean
$http.post('login/getLoggedUser', {}, {}).success(function(data){
if(!data.logged){
defer.resolve(data);
$location.url('/');
}
else{
defer.resolve(data);
$location.url('/home')
}
});
return defer.promise;
}
}
})
.when('/home',{
templateUrl: 'partials/main.html',
controller: 'MainCtrl',
resolve:{
test: function($http, $q,$location){
var defer = $q.defer();
$http.post('login/getLoggedUser', {}, {}).success(function(data){
if(data.logged){
defer.resolve(data);
$location.url('/home');
}
else{
defer.resolve(data);
$location.url('/')
}
});
return defer.promise;
}
},
})
.otherwise({ redirectTo: '/' });
});
When i direct to /home, GET http:/localhost:8080/an-grails/partials/main.html is called in console which contains the content of main page. How do i disable this call? Is there any other method to do this? I read documentation on AngularJS official page and also watched few videos of Egghead.io about resolve and got idea that controller and template gets loaded only after resolve is processed, So what am i doing wrong?
The simplest way to manage rights in your different routes is to catch the $routeChangeStart which is fired by the $route service everytime the route is changed.
With this, you can access the actual route and the next one. This object is the same that you register with $routeProvider.when(). You just have to add a boolean and compare this boolean with the actual user status.
$rootScope.$on("$routeChangeStart", function(event, next, current) {
if (next.loggedOnly && !user.isLogged()) {
// You should implement a isLogged method to test if the user is logged
$location.replace();
// This prevent a redirect loop when going back in the browser
return $location.path("/");
}
}
And inside your route declaration use :
$routeProvider.when('/home', {
templateUrl: 'partials/main.html',
controller: 'MainCtrl',
loggedOnly: true
});

Redirect index page if user is logged in AngularJS

I am trying to vary the page a user sees when they go to my website. If they are anonymous they should see the register page. If they have logged in they should see their dashboard.
I have a service which checks to see if the user is logged in (e.g. check cookies) which triggers when the Angular services load. I have tried to use the $routeProvider to redirect but the service has not been triggered when the $routeProvider is being initialized so it always thinks that the user is not logged in.
I can redirect easily once the initial page has been loaded but I am struggling to redirect the first page loaded. Can anyone give advice on how to do this?
Make sure to read comment under the answer. When I answered this question I didn't thought about unit tests and design. I was just demonstrating that what can be one of many ways to achieve the desired result
I think the best way to do it under controller or your app.config.run.
In your case you should create another module to check for user login status. Inject user login status checking module to your app module.
Here is the link to the sample followed by the app.js code
http://plnkr.co/edit/dCdCEgLjLeGf82o1MttS
var login = angular.module('myLoginCheck', [])
.factory('$logincheck', function () {
return function (userid) {
// Perform logical user logging. Check either
// by looking at cookies or make a call to server.
if (userid > 0) return true;
return false;
};
});
var app = angular.module('myApp', ['myLoginCheck']);
app.config(function ($routeProvider, $locationProvider) {
$routeProvider
.when('/publicurl', {})
.when('/loginurl', {})
.when('/unauthorize', {})
.otherwise({redirectTo: '/'});
})
.run(function ($logincheck, $location) {
//console.log("Into run mode");
console.log("Userid 5 is logged in: ", $logincheck(5));
console.log("Userid 0 logged in: ", $logincheck(0));
//now redirect to appropriate path based on login status
if ($logincheck(0)) {
//$location.path('/loginurl'); or
}
else {
//$location.path('/publicurl'); or
}
});
app.controller('MainCtrl', function ($scope) {
$scope.name = 'World';
});
I just did this, by making a dummy template and small controller for the / path which redirects as appropriate.
controllers.controller('loginController',
['$scope', '$location', '$cookies',
function($scope, $location, $cookies) {
if (!!$cookies.user) {
console.log("already logged in!");
$location.path('/shows');
} else {
console.log("need to login!");
$location.path('/users');
}
}]);
var app = angular.module('app', ['ngRoute', 'ngCookies', 'controllers', 'services']);
app.config(['$routeProvider',
function($routeProvider) {
$routeProvider.when('/users', {
templateUrl: "partial/users.html",
controller: 'userController'
});
$routeProvider.when('/shows', {
templateUrl: "partial/shows.html",
controller: 'showController'
});
$routeProvider.when('/', {
template: '',
controller: 'loginController'
});
$routeProvider.otherwise({
redirectTo: '/'
});
}]);

Resources