AngularJS Routing with Authorization Process - javascript

I'm trying to implement basic authentication routing in AngularJS. I have a model that has a authorize method that returns a promise. I want the routing to wait until that authorize function has returned true or false to continue, once that has completed it should resume the path or redirect the user to the login page.
I think essentially i need to stop routing, call that method and then resume or redirect to login. Below is the code I have so far but i'm not sure how to accomplish the pause/resume. Any ideas?
return angular.module('d', ['ngCookies', 'ngRoute'])
.config(['$routeProvider', '$locationProvider', '$httpProvider',
function ($routeProvider, $locationProvider, $httpProvider) {
$routeProvider.when('/',
{
templateUrl: 'views/home.html',
controller: 'HomeCtrl'
});
$routeProvider.when('/login',
{
templateUrl: 'views/login.html',
controller: 'LoginCtrl'
});
$routeProvider.otherwise({ redirectTo: '/404' });
$locationProvider.html5Mode(true);
// handle unauthorized requests by redirecting to login page
$httpProvider.responseInterceptors.push(
['$location', '$q', function ($location, $q) {
function success(response) {
return response;
}
function error(response) {
if (response.status === 401) {
$location.path('/login');
return $q.reject(response);
}
else {
return $q.reject(response);
}
}
return function (promise) {
return promise.then(success, error);
}
}]);
}])
.run(['$rootScope', '$location', 'Auth', function ($rootScope, $location, Auth) {
$rootScope.$on("$routeChangeStart", function (event, next, current) {
$rootScope.error = null;
Auth.authorize().then(function(){
$location.path('/');
},function(){
$location.path('/login');
});
});
}]);

Your solution is very similar to a prototype I wrote a while back.
The idea is that whenever you "touch" the server and get an authentication error, a modal window pops-up asking for a login without changing the URL (you let it change to the new URL and stay there).
My implementation was also based on an interceptor checking for 401. It has a dependency on $rootScope and sets a property "needsLogin" to true. The page template has the login modal window visible when needsLogin === true and hides the ng-view (this is important since the new route has been loaded but it misses its data). Finally, upon successfull login, the login controller does the $route.reload() and then sets $rootScope.needsLogin = false.
Small snippets:
<div id="main" role="main" ng-show="needsLogin === false">
<div ng-view></div>
</div>
<div ng-show="needsLogin === true" ng-include="'views/login.html'" class="overlay"></div>
The login controller can be something like:
function LoginCtrl($scope, $rootScope, $route, $http) {
$scope.login = function () {
$http.post( /* Somehow do your login */ )
.success(function () {
var deregister = $rootScope.$on('$routeChangeSuccess', function () {
// hide login / show ng-view after route has been reloaded
$rootScope.needsLogin = false;
deregister();
});
$route.reload();
})
.error(function () {
/* handle errors */
});
};
}
$route.reload() is not a full page refresh, it merely re-initializes the route (controller/view etc). Hopefully, the call that was rejected before will run again and the page will be fine.

Related

AngularJS - disable some routes

As title already suggests, I'm trying to disable some routes. I'm using angular seed project, that already has a nice structure.
I'm using JWT and I'm trying to set up a structure where if a certain route requires user to be logged in, and the user is not logged in, it redirects him to the some other page.
On my angular.module I've added the following code:
.run(['$rootScope', 'userService', '$location', function($rootScope, userService, $location) {
userService.init();
$rootScope.$on('$routeChangeStart', function(event, next, current) {
$rootScope.isPrivate = next['authenticate'];
if ($rootScope.isPrivate) {
if (!userService.get())
$location.path('/');
}
});
}]);
And this is a protected route:
angular.module('myApp.view2', ['ngRoute', 'ngCookies'])
.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/admin/vnos-stevilke', {
templateUrl: 'view2/view2.html',
controller: 'View2Ctrl',
authenticate: true
}).when('/admin/vnos-stevilke/:id', {
templateUrl: 'view2/view2.html',
controller: 'View2Ctrl',
authenticate: true
});
}])
.controller('View2Ctrl', ['$scope', 'webServices', '$location', '$routeParams', function($scope, webServices, $location, $routeParams) {
if ($routeParams.id)
webServices.getBranchById($routeParams.id, function(err, data) {
$scope.branch = data;
});
webServices.getCompanies(function(err, data) {
console.log(data);
console.log('no access!');
if (!err)
$scope.companies = data;
});
}]);
now at first it appears to be working OK: if I'm not logged in, the route is not displayed and I get redirected back to the root. But at a closer look I've noticed that console.log('no access!'); is still displayed in the console. So it appears that controller gets initialized.
It seems like the whole route is loaded and then gets redirected if user is not logged in. That is not the behaviour I'm looking for. I'm trying to HOLD the loading of the route until I'm sure the user is logged in.
Any ideas?
UPDATE:
I changed the code accordingly with the suggestion below, but it doesn't seem to work. Where have I gone wrong?
userService method that checks if user is logged in:
this.isLogged = function() {
var deferred = $q.defer();
if (current === null) return deferred.reject();
else return deferred.resolve(current);
};
Run method:
.run(['$rootScope', 'userService', '$location', function($rootScope, userService, $location) {
userService.init();
$rootScope.$on('$routeChangeError', function() {
$location.path('/');
});
}]);
Restricted page:
.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/admin/vnos-stevilke', {
templateUrl: 'view2/view2.html',
controller: 'View2Ctrl',
resolve: function(userService) {
console.log('test');
return userService.isLogged();
}
});
}])
Here, the "test" never displays in console.
You need to just decide if you are letting user into restricted area with resolve route parameter.
If in one of resolve function resolves with a promise object that is rejected it stops entering requested route.
I would write something like:
$routeProvider.when('/restrictedURL',{
...some params,
resolve: function(userService){
return userService.get();
}
}
...and make userService.get return a Promise object that is resolved if session is active and rejected otherwise.
Now.. if promise is rejected a route won't be launched and $routeChangeError event is raised, so you need something like:
angular.module('yourapp').run(function($rootScope){
$rootScope.$on("$routeChangeError",function(){
$location.path('/');
});
});
read more about resolve parameter # https://docs.angularjs.org/api/ngRoute/provider/$routeProvider

AngularJS: Preventing user from accessing a certain state

I'm building an app using sails.js backend and angular in frontend. I'm trying to prevent the user from accessing the admin control page if he's not authorized. I've run into couple of answers already, but none of them seem to fully work.
At the moment in my app.js, I have
$stateProvider
.state('home', {
url: "/home",
templateUrl: "home/homeTemplate.html",
controller: 'homeController'
})
.state('adminPage', {
url: "/adminPage",
templateUrl: "adminPage/adminTemplate.html",
controller: 'adminPageController',
resolve: {
validate: function($q, $sails, $location) {
var defer = $q.defer();
$sails.get("/user/getCurrentUser")
.success(function(response) {
if (response.user.accessAdminPage) {
defer.resolve();
}
else {
defer.reject("Access blocked");
$location.path('/');
}
return defer.promise;
})
}
}
})
The current code is partially working; The problem at the moment is, that when the unauthorized user first logs in and lands on the home page, and then accesses localhost:1337/#/adminPage, he actually reaches the page. The url in the address bar changes to localhost:1337/#/home but the user isn't redirected. Now the weird part is, when accessing the home page afterwards through the navbar and trying to access the admin page again, the user IS redirected to the home page as intended (although there's an annoying 'flash' while the page is reloaded).
For other people asking, this kind of handling has worked, and I'm wondering what I may have missed and generally any reasons for why my current solution isn't working.
You are returning promise from success function, this will never work.
You should return defered.promise (promise object) from outside success function.
CODE
$stateProvider
.state('home', {
url: "/home",
templateUrl: "home/homeTemplate.html",
controller: 'homeController'
})
.state('adminPage', {
url: "/adminPage",
templateUrl: "adminPage/adminTemplate.html",
controller: 'adminPageController',
resolve: {
validate: function($q, $sails, $location) {
var defer = $q.defer();
$sails.get("/user/getCurrentUser")
.success(function(response) {
if (response.user.accessAdminPage) {
defer.resolve();
} else {
defer.reject("Access blocked");
$location.path('/');
}
});
return defer.promise;
}
}
});
Hopefully this could help you, Thanks.
With the solution given by pankajparkar, the issue is that you will have to reply the logic in each state declaration. I recommend you to check the user's authorization in the onStateChangeStart event
angular.module('myApp', ['ui.router'])
.run(function($rootScope, AuthService){
$rootScope.$on('$stateChangeStart',
function(event, next, nextParams, prev, prevParams) {
AuthService.isNotAutorized()
.then(function() {
event.preventDefault();
$state.go('defaultState');
});
});
});

Angular, delay of initial load

I am using a generator here - https://github.com/DaftMonk/generator-angular-fullstack, and I am having a small problem when it initially loads.
There is a second or 2 when it loads when you can see the index.html template without partial css on it, and then it loads the user to the login page. I would like to get rid of this, so when you go to the root, you would go straight to /login. I'm not sure where to begin with this, as it could be many things. I should not I have alot of bower'd in scripts, maybe they are slowing this down?
My first thought was it was the authentication, but maybe it has nothing to do with it. But here is the app js minus the dependancies
.config(function ($routeProvider, $locationProvider, $httpProvider) {
$routeProvider
.otherwise({
redirectTo: '/'
});
$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('$routeChangeStart', function (event, next) {
Auth.isLoggedInAsync(function(loggedIn) {
if (next.authenticate && !loggedIn) {
$location.path('/login');
}
});
});
});
It could entirely not be this, but I think it might be. I don't care if the page is blank for second, but it's showing the items in the top like the header menu that is in the base index.html with a little css styling on it, but it looks terrible. Any direction in where to look to fix this would be MUCH appreciated, as I have no idea where to even start.
Thanks for reading!

angularjs view is loaded before resolve checks access

I'm setting up an access control system in angular. This is how it looks so far.
It's doing the ajax to return the current user's role, then checking that role with the access array to see if the user has permission. If not it redirects.
That all works fine, but the view is still being shown for a split second before the redirect.
It may also be important to note that the ajax request is necessary because the user auth is being handled with Laravel, so I made an API for Angular to talk to get information about the user's session.
var app = angular.module('application', ['ngResource']);
app.config(function($routeProvider){
$routeProvider
.when('/admin', {
controller: 'showAdmin',
templateUrl: 'admin.html',
access: ['Admin', 'Manager'],
resolve: AppCtrl.resolve
});
});
function AppCtrl ($scope, getUser, $location, $rootScope) {
}
AppCtrl.resolve = {
getUser : function($q, $http, $location, $rootScope) {
return $http({
method: 'GET',
url: '/api/getUser'
})
.success(function(data, status) {
$rootScope.user = data;
if($rootScope.access.indexOf(data.permissions[0].role_name) < 0) $location.path('/');
});
}
};
app.run(function ($rootScope, sessionFactory, $location){
$rootScope.$on('$routeChangeStart', function (event, next) {
$rootScope.access = next.access;
});
});
Use an ng-cloak directive on the containing element to eliminate the flicker. See this page for an example along with some CSS/browser-specific gotchas.

AngularJS- Login and Authentication in each route and controller

I have an AngularJS application created by using yeoman, grunt and bower.
I have a login page that has a controller that checks for authentication. If the credentials are correct I reroute to home page.
app.js
'use strict';
//Define Routing for app
angular.module('myApp', []).config(['$routeProvider', '$locationProvider',
function($routeProvider,$locationProvider) {
$routeProvider
.when('/login', {
templateUrl: 'login.html',
controller: 'LoginController'
})
.when('/register', {
templateUrl: 'register.html',
controller: 'RegisterController'
})
.when('/forgotPassword', {
templateUrl: 'forgotpassword.html',
controller: 'forgotController'
})
.when('/home', {
templateUrl: 'views/home.html',
controller: 'homeController'
})
.otherwise({
redirectTo: '/login'
});
// $locationProvider.html5Mode(true); //Remove the '#' from URL.
}]);
angular.module('myApp').factory("page", function($rootScope){
var page={};
var user={};
page.setPage=function(title,bodyClass){
$rootScope.pageTitle = title;
$rootScope.bodylayout=bodyClass;
};
page.setUser=function(user){
$rootScope.user=user;
}
return page;
});
LoginControler.js
'use strict';
angular.module('myApp').controller('LoginController', function($scope, $location, $window,page) {
page.setPage("Login","login-layout");
$scope.user = {};
$scope.loginUser=function()
{
var username=$scope.user.name;
var password=$scope.user.password;
if(username=="admin" && password=="admin123")
{
page.setUser($scope.user);
$location.path( "/home" );
}
else
{
$scope.message="Error";
$scope.messagecolor="alert alert-danger";
}
}
});
On the home page I have
<span class="user-info">
<small>Welcome,</small>
{{user.name}}
</span>
<span class="logout">Logout</span>
In the loginController, I check the login info and if it's successful, I set the user object in the service factory. I don't know whether this is correct or not.
What I need is, When the user is logged in, It sets some value in the user object so that all other pages can get that value.
Whenever any route changes happen, the controller should check if the user is logged in or not. If not, it should reroute to the login page. Also, if the user is already logged in and come back to the page, it should go to home page. The controller should also check the credentials on all of the routes.
I have heard about ng-cookies, but I don't know how to use them.
Many of the examples I saw were not very clear and they use some kind of access roles or something. I don't want that. I only want a login filter.
Can someone give me some ideas?
My solution breaks down in 3 parts: the state of the user is stored in a service, in the run method you watch when the route changes and you check if the user is allowed to access the requested page, in your main controller you watch if the state of the user change.
app.run(['$rootScope', '$location', 'Auth', function ($rootScope, $location, Auth) {
$rootScope.$on('$routeChangeStart', function (event) {
if (!Auth.isLoggedIn()) {
console.log('DENY');
event.preventDefault();
$location.path('/login');
}
else {
console.log('ALLOW');
$location.path('/home');
}
});
}]);
You should create a service (I will name it Auth) which will handle the user object and have a method to know if the user is logged or not.
service:
.factory('Auth', function(){
var user;
return{
setUser : function(aUser){
user = aUser;
},
isLoggedIn : function(){
return(user)? user : false;
}
}
})
From your app.run, you should listen the $routeChangeStart event. When the route will change, it will check if the user is logged (the isLoggedIn method should handle it). It won't load the requested route if the user is not logged and it will redirect the user to the right page (in your case login).
The loginController should be used in your login page to handle login. It should just interract with the Auth service and set the user as logged or not.
loginController:
.controller('loginCtrl', [ '$scope', 'Auth', function ($scope, Auth) {
//submit
$scope.login = function () {
// Ask to the server, do your job and THEN set the user
Auth.setUser(user); //Update the state of the user in the app
};
}])
From your main controller, you could listen if the user state change and react with a redirection.
.controller('mainCtrl', ['$scope', 'Auth', '$location', function ($scope, Auth, $location) {
$scope.$watch(Auth.isLoggedIn, function (value, oldValue) {
if(!value && oldValue) {
console.log("Disconnect");
$location.path('/login');
}
if(value) {
console.log("Connect");
//Do something when the user is connected
}
}, true);
Here is another possible solution, using the resolve attribute of the $stateProvider or the $routeProvider. Example with $stateProvider:
.config(["$stateProvider", function ($stateProvider) {
$stateProvider
.state("forbidden", {
/* ... */
})
.state("signIn", {
/* ... */
resolve: {
access: ["Access", function (Access) { return Access.isAnonymous(); }],
}
})
.state("home", {
/* ... */
resolve: {
access: ["Access", function (Access) { return Access.isAuthenticated(); }],
}
})
.state("admin", {
/* ... */
resolve: {
access: ["Access", function (Access) { return Access.hasRole("ROLE_ADMIN"); }],
}
});
}])
Access resolves or rejects a promise depending on the current user rights:
.factory("Access", ["$q", "UserProfile", function ($q, UserProfile) {
var Access = {
OK: 200,
// "we don't know who you are, so we can't say if you're authorized to access
// this resource or not yet, please sign in first"
UNAUTHORIZED: 401,
// "we know who you are, and your profile does not allow you to access this resource"
FORBIDDEN: 403,
hasRole: function (role) {
return UserProfile.then(function (userProfile) {
if (userProfile.$hasRole(role)) {
return Access.OK;
} else if (userProfile.$isAnonymous()) {
return $q.reject(Access.UNAUTHORIZED);
} else {
return $q.reject(Access.FORBIDDEN);
}
});
},
hasAnyRole: function (roles) {
return UserProfile.then(function (userProfile) {
if (userProfile.$hasAnyRole(roles)) {
return Access.OK;
} else if (userProfile.$isAnonymous()) {
return $q.reject(Access.UNAUTHORIZED);
} else {
return $q.reject(Access.FORBIDDEN);
}
});
},
isAnonymous: function () {
return UserProfile.then(function (userProfile) {
if (userProfile.$isAnonymous()) {
return Access.OK;
} else {
return $q.reject(Access.FORBIDDEN);
}
});
},
isAuthenticated: function () {
return UserProfile.then(function (userProfile) {
if (userProfile.$isAuthenticated()) {
return Access.OK;
} else {
return $q.reject(Access.UNAUTHORIZED);
}
});
}
};
return Access;
}])
UserProfile copies the current user properties, and implement the $hasRole, $hasAnyRole, $isAnonymous and $isAuthenticated methods logic (plus a $refresh method, explained later):
.factory("UserProfile", ["Auth", function (Auth) {
var userProfile = {};
var clearUserProfile = function () {
for (var prop in userProfile) {
if (userProfile.hasOwnProperty(prop)) {
delete userProfile[prop];
}
}
};
var fetchUserProfile = function () {
return Auth.getProfile().then(function (response) {
clearUserProfile();
return angular.extend(userProfile, response.data, {
$refresh: fetchUserProfile,
$hasRole: function (role) {
return userProfile.roles.indexOf(role) >= 0;
},
$hasAnyRole: function (roles) {
return !!userProfile.roles.filter(function (role) {
return roles.indexOf(role) >= 0;
}).length;
},
$isAnonymous: function () {
return userProfile.anonymous;
},
$isAuthenticated: function () {
return !userProfile.anonymous;
}
});
});
};
return fetchUserProfile();
}])
Auth is in charge of requesting the server, to know the user profile (linked to an access token attached to the request for example):
.service("Auth", ["$http", function ($http) {
this.getProfile = function () {
return $http.get("api/auth");
};
}])
The server is expected to return such a JSON object when requesting GET api/auth:
{
"name": "John Doe", // plus any other user information
"roles": ["ROLE_ADMIN", "ROLE_USER"], // or any other role (or no role at all, i.e. an empty array)
"anonymous": false // or true
}
Finally, when Access rejects a promise, if using ui.router, the $stateChangeError event will be fired:
.run(["$rootScope", "Access", "$state", "$log", function ($rootScope, Access, $state, $log) {
$rootScope.$on("$stateChangeError", function (event, toState, toParams, fromState, fromParams, error) {
switch (error) {
case Access.UNAUTHORIZED:
$state.go("signIn");
break;
case Access.FORBIDDEN:
$state.go("forbidden");
break;
default:
$log.warn("$stateChangeError event catched");
break;
}
});
}])
If using ngRoute, the $routeChangeError event will be fired:
.run(["$rootScope", "Access", "$location", "$log", function ($rootScope, Access, $location, $log) {
$rootScope.$on("$routeChangeError", function (event, current, previous, rejection) {
switch (rejection) {
case Access.UNAUTHORIZED:
$location.path("/signin");
break;
case Access.FORBIDDEN:
$location.path("/forbidden");
break;
default:
$log.warn("$stateChangeError event catched");
break;
}
});
}])
The user profile can also be accessed in the controllers:
.state("home", {
/* ... */
controller: "HomeController",
resolve: {
userProfile: "UserProfile"
}
})
UserProfile then contains the properties returned by the server when requesting GET api/auth:
.controller("HomeController", ["$scope", "userProfile", function ($scope, userProfile) {
$scope.title = "Hello " + userProfile.name; // "Hello John Doe" in the example
}])
UserProfile needs to be refreshed when a user signs in or out, so that Access can handle the routes with the new user profile. You can either reload the whole page, or call UserProfile.$refresh(). Example when signing in:
.service("Auth", ["$http", function ($http) {
/* ... */
this.signIn = function (credentials) {
return $http.post("api/auth", credentials).then(function (response) {
// authentication succeeded, store the response access token somewhere (if any)
});
};
}])
.state("signIn", {
/* ... */
controller: "SignInController",
resolve: {
/* ... */
userProfile: "UserProfile"
}
})
.controller("SignInController", ["$scope", "$state", "Auth", "userProfile", function ($scope, $state, Auth, userProfile) {
$scope.signIn = function () {
Auth.signIn($scope.credentials).then(function () {
// user successfully authenticated, refresh UserProfile
return userProfile.$refresh();
}).then(function () {
// UserProfile is refreshed, redirect user somewhere
$state.go("home");
});
};
}])
The most straightforward manner of defining custom behavior for individual routes would be pretty easy:
1) routes.js: create a new property (like requireAuth) for any desired route
angular.module('yourApp').config(function($routeProvider) {
$routeProvider
.when('/home', {
templateUrl: 'templates/home.html',
requireAuth: true // our custom property
})
.when('/login', {
templateUrl: 'templates/login.html',
})
.otherwise({
redirectTo: '/home'
});
})
2) In a top-tier controller that isn't bound to an element inside the ng-view (to avoid conflict with angular $routeProvider ), check if the newUrl has the requireAuth property and act accordingly
angular.module('YourApp').controller('YourController', function ($scope, $location, session) {
// intercept the route change event
$scope.$on('$routeChangeStart', function (angularEvent, newUrl) {
// check if the custom property exist
if (newUrl.requireAuth && !session.user) {
// user isn’t authenticated
$location.path("/login");
}
});
});
I wrote a post a few months back on how to set up user registration and login functionality with Angular, you can check it out at http://jasonwatmore.com/post/2015/03/10/AngularJS-User-Registration-and-Login-Example.aspx
I check if the user is logged in the $locationChangeStart event, here is my main app.js showing this:
(function () {
    'use strict';
 
    angular
        .module('app', ['ngRoute', 'ngCookies'])
        .config(config)
        .run(run);
 
    config.$inject = ['$routeProvider', '$locationProvider'];
    function config($routeProvider, $locationProvider) {
        $routeProvider
            .when('/', {
                controller: 'HomeController',
                templateUrl: 'home/home.view.html',
                controllerAs: 'vm'
            })
 
            .when('/login', {
                controller: 'LoginController',
                templateUrl: 'login/login.view.html',
                controllerAs: 'vm'
            })
 
            .when('/register', {
                controller: 'RegisterController',
                templateUrl: 'register/register.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(), ['/login', '/register']) === -1;
            var loggedIn = $rootScope.globals.currentUser;
            if (restrictedPage && !loggedIn) {
                $location.path('/login');
            }
        });
    }
 
})();
I feel like this way is easiest, but perhaps it's just personal preference.
When you specify your login route (and any other anonymous routes; ex: /register, /logout, /refreshToken, etc.), add:
allowAnonymous: true
So, something like this:
$stateProvider.state('login', {
url: '/login',
allowAnonymous: true, //if you move this, don't forget to update
//variable path in the force-page check.
views: {
root: {
templateUrl: "app/auth/login/login.html",
controller: 'LoginCtrl'
}
}
//Any other config
}
You don't ever need to specify "allowAnonymous: false", if not present, it is assumed false, in the check. In an app where most URLs are force authenticated, this is less work. And safer; if you forget to add it to a new URL, the worst that can happen is an anonymous URL is protected. If you do it the other way, specifying "requireAuthentication: true", and you forget to add it to a URL, you are leaking a sensitive page to the public.
Then run this wherever you feel fits your code design best.
//I put it right after the main app module config. I.e. This thing:
angular.module('app', [ /* your dependencies*/ ])
.config(function (/* you injections */) { /* your config */ })
//Make sure there's no ';' ending the previous line. We're chaining. (or just use a variable)
//
//Then force the logon page
.run(function ($rootScope, $state, $location, User /* My custom session obj */) {
$rootScope.$on('$stateChangeStart', function(event, newState) {
if (!User.authenticated && newState.allowAnonymous != true) {
//Don't use: $state.go('login');
//Apparently you can't set the $state while in a $state event.
//It doesn't work properly. So we use the other way.
$location.path("/login");
}
});
});
app.js
'use strict';
// Declare app level module which depends on filters, and services
var app= angular.module('myApp', ['ngRoute','angularUtils.directives.dirPagination','ngLoadingSpinner']);
app.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/login', {templateUrl: 'partials/login.html', controller: 'loginCtrl'});
$routeProvider.when('/home', {templateUrl: 'partials/home.html', controller: 'homeCtrl'});
$routeProvider.when('/salesnew', {templateUrl: 'partials/salesnew.html', controller: 'salesnewCtrl'});
$routeProvider.when('/salesview', {templateUrl: 'partials/salesview.html', controller: 'salesviewCtrl'});
$routeProvider.when('/users', {templateUrl: 'partials/users.html', controller: 'usersCtrl'});
$routeProvider.when('/forgot', {templateUrl: 'partials/forgot.html', controller: 'forgotCtrl'});
$routeProvider.otherwise({redirectTo: '/login'});
}]);
app.run(function($rootScope, $location, loginService){
var routespermission=['/home']; //route that require login
var salesnew=['/salesnew'];
var salesview=['/salesview'];
var users=['/users'];
$rootScope.$on('$routeChangeStart', function(){
if( routespermission.indexOf($location.path()) !=-1
|| salesview.indexOf($location.path()) !=-1
|| salesnew.indexOf($location.path()) !=-1
|| users.indexOf($location.path()) !=-1)
{
var connected=loginService.islogged();
connected.then(function(msg){
if(!msg.data)
{
$location.path('/login');
}
});
}
});
});
loginServices.js
'use strict';
app.factory('loginService',function($http, $location, sessionService){
return{
login:function(data,scope){
var $promise=$http.post('data/user.php',data); //send data to user.php
$promise.then(function(msg){
var uid=msg.data;
if(uid){
scope.msgtxt='Correct information';
sessionService.set('uid',uid);
$location.path('/home');
}
else {
scope.msgtxt='incorrect information';
$location.path('/login');
}
});
},
logout:function(){
sessionService.destroy('uid');
$location.path('/login');
},
islogged:function(){
var $checkSessionServer=$http.post('data/check_session.php');
return $checkSessionServer;
/*
if(sessionService.get('user')) return true;
else return false;
*/
}
}
});
sessionServices.js
'use strict';
app.factory('sessionService', ['$http', function($http){
return{
set:function(key,value){
return sessionStorage.setItem(key,value);
},
get:function(key){
return sessionStorage.getItem(key);
},
destroy:function(key){
$http.post('data/destroy_session.php');
return sessionStorage.removeItem(key);
}
};
}])
loginCtrl.js
'use strict';
app.controller('loginCtrl', ['$scope','loginService', function ($scope,loginService) {
$scope.msgtxt='';
$scope.login=function(data){
loginService.login(data,$scope); //call login service
};
}]);
You can use resolve:
angular.module('app',[])
.config(function($routeProvider)
{
$routeProvider
.when('/', {
templateUrl : 'app/views/login.html',
controller : 'YourController',
controllerAs : 'Your',
resolve: {
factory : checkLoginRedirect
}
})
}
And, the function of the resolve:
function checkLoginRedirect($location){
var user = firebase.auth().currentUser;
if (user) {
// User is signed in.
if ($location.path() == "/"){
$location.path('dash');
}
return true;
}else{
// No user is signed in.
$location.path('/');
return false;
}
}
Firebase also has a method that helps you install an observer, I advise installing it inside a .run:
.run(function(){
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
console.log('User is signed in.');
} else {
console.log('No user is signed in.');
}
});
}
For instance an application has two user called ap and auc. I am passing an extra property to each route and handling the routing based on the data i get in $routeChangeStart.
Try this:
angular.module("app").config(['$routeProvider',
function ($routeProvider) {
$routeProvider.
when('/ap', {
templateUrl: 'template1.html',
controller: 'template1',
isAp: 'ap',
}).
when('/auc', {
templateUrl: 'template2.html',
controller: 'template2',
isAp: 'common',
}).
when('/ic', {
templateUrl: 'template3.html',
controller: 'template3',
isAp: 'auc',
}).
when('/mup', {
templateUrl: 'template4.html',
controller: 'template4',
isAp: 'ap',
}).
when('/mnu', {
templateUrl: 'template5.html',
controller: 'template5',
isAp: 'common',
}).
otherwise({
redirectTo: '/ap',
});
}]);
app.js:
.run(['$rootScope', '$location', function ($rootScope, $location) {
$rootScope.$on("$routeChangeStart", function (event, next, current) {
if (next.$$route.isAp != 'common') {
if ($rootScope.userTypeGlobal == 1) {
if (next.$$route.isAp != 'ap') {
$location.path("/ap");
}
}
else {
if (next.$$route.isAp != 'auc') {
$location.path("/auc");
}
}
}
});
}]);
All have suggested big solution why you are worrying of session on client side.
I mean when state/url changes I suppose you are doing an ajax call to load the data for tempelate.
Note :- To Save user's data you may use `resolve` feature of `ui-router`.
Check cookie if it exist load template , if even cookies doesn't exist than
there is no chance of logged in , simply redirect to login template/page.
Now the ajax data is returned by server using any api. Now the point came into play , return standard return types using server according to logged in status of user. Check those return codes and process your request in controller.
Note:- For controller which doesn't require an ajax call natively , you can call a blank request to server like this server.location/api/checkSession.php and this is checkSession.php
<?php/ANY_LANGUAGE
session_start();//You may use your language specific function if required
if(isset($_SESSION["logged_in"])){
set_header("200 OK");//this is not right syntax , it is just to hint
}
else{
set_header("-1 NOT LOGGED_IN");//you may set any code but compare that same
//code on client side to check if user is logged in or not.
}
//thanks.....
On client side inside controller or through any service as shown in other answers
$http.get(dataUrl)
.success(function (data){
$scope.templateData = data;
})
.error(function (error, status){
$scope.data.error = { message: error, status: status};
console.log($scope.data.error.status);
if(status == CODE_CONFIGURED_ON_SERVER_SIDE_FOR_NON_LOGGED_IN){
//redirect to login
});
Note :- I will update more tomorrow or in future
You should check user authentication in two main sites.
When users change state, checking it using '$routeChangeStart' callback
When a $http request is sent from angular, using an interceptor.

Categories