AngularFire Authentication with Route Limiting - javascript
I'm currently working on my first Angular application. Each hurdle I get over is really helping me with my understanding of how everything in Angular comes together but also programming in general.
Right now, I am experimenting with AngularFire and Firebase as the back end to the application. Further down the line, I will store other data in Firebase, but right now I am focused on getting the User Authentication part down.
Currently, the application is comprised of an index.html page, which loads the navigation HTML elements as a header, and then loads partials within an ng-view element. This keeps the navigation constant and the pages dynamic (much of the content is generated via Angular from various JSON objects). It also includes a login.html that is entirely separate and does not include the navigational elements from the main application, but does use the same controllers and modules as the rest of the application. I've managed to cobble together a working login, which interfaces successfully with Firebase. This can verify existing e-mail/password combinations or register new users. What I really want is for a successful login to redirect to index.html, and if someone tries to access index.html or any of the partials without having signed in, to be redirected to the login.html. I'm not entirely sure how to approach this, though I believe it will have something to do with the router. I'd also like to keep the authentication information attached to a persistent user object so it can be used in controlling the visibility and functionality of navigation options later on.
The Angular code that I am using:
var profileApp = angular.module('profileApp', ['ngRoute', 'firebase']);
profileApp.controller('LoginCtrl', ['$scope', '$rootScope', '$firebaseAuth', function($scope, $rootScope, $firebaseAuth) {
var ref = new Firebase('https://FIREBASEURL.firebaseio.com/');
$rootScope.auth = $firebaseAuth(ref);
$scope.signIn = function () {
$rootScope.auth.$login('password', {
email: $scope.email,
password: $scope.password
}).then(function(user) {
$rootScope.alert.message = '';
}, function(error) {
if (error = 'INVALID_EMAIL') {
console.log('email invalid or not signed up — trying to sign you up!');
$scope.signUp();
} else if (error = 'INVALID_PASSWORD') {
console.log('wrong password!');
} else {
console.log(error);
}
});
}
$scope.signUp = function() {
$rootScope.auth.$createUser($scope.email, $scope.password, function(error, user) {
if (!error) {
$rootScope.alert.message = '';
} else {
$rootScope.alert.class = 'danger';
$rootScope.alert.message = 'The username and password combination you entered is invalid.';
}
});
}
}
]);
profileApp.controller('AlertCtrl', [
'$scope', '$rootScope', function($scope, $rootScope) {
$rootScope.alert = {};
}
]);
profileApp.controller('HistoryCtrl', function ($scope, $http){
$http.get('patient.json').success(function(data) {
$scope.historyItems = data;
});
});
profileApp.controller('FaceCtrl', function ($scope, $http){
$http.get('patient.json').success(function(data) {
$scope.faceItems = data;
});
});
profileApp.controller('PhysicalCtrl', function ($scope, $http){
$http.get('patient.json').success(function(data) {
$scope.physicalItems = data;
});
});
profileApp.controller('MenuCtrl', function ($scope, $http){
$http.get('profile.json').success(function(data) {
$scope.profileItems = data;
});
});
profileApp.controller('ContentCtrl', function ($scope, $http){
$http.get('content.json').success(function(data) {
$scope.contentItems = data;
});
});
profileApp.controller('OrdersCtrl', function ($scope, $http){
$http.get('patient.json').success(function(data) {
$scope.ordersItems = data;
});
});
profileApp.controller('MedAdminCtrl', function ($scope, $http){
$http.get('patient.json').success(function(data) {
$scope.medadminItems = data;
});
});
profileApp.controller('LabsCtrl', function ($scope, $http){
$http.get('patient.json').success(function(data) {
$scope.labItems = data;
});
});
profileApp.controller('VitalsCtrl', function ($scope, $http){
$http.get('patient.json').success(function(data) {
$scope.vitalItems = data;
});
});
profileApp.controller('AssessmentCtrl', function ($scope, $http){
$http.get('patient.json').success(function(data) {
$scope.asessmentItems = data;
});
});
profileApp.controller('IoCtrl', function ($scope, $http){
$http.get('patient.json').success(function(data) {
$scope.ioItems = data;
});
});
profileApp.config(['$routeProvider', function ($routeProvider) {
__insp.push(["virtualPage"]);
$routeProvider
// Home
.when("/", {
templateUrl: "partials/face.html",
controller: "FaceCtrl"
})
.when("/face", {
templateUrl: "partials/face.html",
controller: "FaceCtrl"
})
// Pages
.when("/medicalHistory", {
templateUrl: "partials/medicalhistory.html",
controller: "HistoryCtrl"
})
.when("/physicalExam", {
templateUrl: "partials/physicalexam.html",
controller: "PhysicalCtrl"
})
.when("/orders", {
templateUrl: "partials/orders.html",
controller: "OrdersCtrl"
})
.when("/medAdmin", {
templateUrl: "partials/medadmin.html",
controller: "MedAdminCtrl"
})
.when("/labs", {
templateUrl: "partials/labs.html",
controller: "LabsCtrl"
})
.when("/vitals", {
templateUrl: "partials/vitals.html",
controller: "VitalsCtrl"
})
.when("/assessment", {
templateUrl: "partials/assessment.html",
controller: "AssessmentCtrl"
})
.when("/io", {
templateUrl: "partials/io.html",
controller: "IoCtrl"
})
//.when("/contact", {templateUrl: "partials/contact.html", controller: "PageCtrl"})
// else 404
.otherwise("/404", {templateUrl: "partials/404.html", controller: "PageCtrl"});
}]);
Here!, Iv changed the code.... Check this out and let me know if this solves the problem!
To Sign In and Sign Up:
profileApp.controller('LoginCtrl', ['$scope', '$location', '$firebase',
function($scope, $location, $firebase) {
$scope.message = "";
var username = "";
var password = "";
var ref = new Firebase("https://FIREBASEURL.firebaseio.com/");
$scope.signIn = function() {
username = $scope.username;
password = $scope.password;
ref.authWithPassword({
email: username,
password: password
}, function(error, authData) {
if (error) {
switch (error.code) {
case "INVALID_EMAIL":
alert("The specified user account email is invalid.");
break;
case "INVALID_PASSWORD":
alert("The specified user account password is incorrect.");
break;
case "INVALID_USER":
alert("The specified user account does not exist.");
break;
default:
alert("Error logging user in:", error)
}
$scope.message = "Wrong Username/Password;";
} else {
$location.path("/home");
}
}, {
remember: "sessionOnly"
});
};
};
$scope.signUp = function () {
$rootScope.auth.$createUser($scope.email, $scope.password, function (error, user) {
if (!error) {
$rootScope.alert.message = '';
} else {
$rootScope.alert.class = 'danger';
$rootScope.alert.message = 'The username and password combination you entered is invalid.';
}
});
};
]);
To Sign Out:
profileApp.controller('home', ['$scope', '$location', '$firebase',
function($scope, $location, $firebase) {
function authDataCallback(authData) {
if (authData) {
alert("Logged in...");
} else {
$location.path("/login");
alert("Log in to access this page");
}
};
var ref = new Firebase("https://FIREBASEURL.firebaseio.com/");
ref.onAuth(authDataCallback);
$scope.logoutUser = function() {
ref.unauth();
$location.path("/login");
};
// Authentication Code
One more thing! You click the submit button and you will not be navigated to another desired view on correct Username/Password. Here what you will do:
1- After clicking submit button wait for 5-10 seconds.
2- After that click the submit button again. Try this out for now.
Cheers!
Related
angularjs logged in user info to be available throughout the app
I want to make the user info available to all views after login. How can I modify the code to be able to access the pseudonym from the other view? Can you please give an example? Here is my login controller: app.controller("MyregisterCtrl", ["$scope", "$stateParams", "Auth", "$state", "$location", "$modal", "DatabaseRef", function ($scope, $stateParams, Auth, $state, $location, $modal, DatabaseRef) { $scope.user = {}; $scope.signIn = function () { if (!$scope.user.email && !$scope.user.password) { toastr.error("Add email and password"); } else { Auth.$signInWithEmailAndPassword($scope.user.email, $scope.user.password) .then(function(firebaseUser) { //=====user info================= var userId = firebase.auth().currentUser.uid; DatabaseRef.ref('/users/' + userId).once('value') .then(function(snapshot) { pseudonym = snapshot.val().pseudonym; console.log("pseudonym: ", pseudonym); return pseudonym; }); //====================== $state.go('app.dashboard'); if (!firebaseUser.emailVerified) { toastr.info('Your email is NOT verified.', 'Verify email!'); $state.go('login.signin'); } }) .catch(function(error) { toastr.error(error.message, error.reason, { timeOut: 10000 }); $scope.user = {}; }) } }; }]); this console.log("pseudonym: ", pseudonym); gives me what I want to access, but can't access it from other views, by just typing {{pseudonym}} for example.
Assign to a $scope variable, whenever you want to display on view , pseudonym = snapshot.val().pseudonym; $scope.pseudonym =pseudonym;
angularjs firebase login scope value is not available after refresh
I have my pseudonym in the $scope and I try to access it from other views after the user has logged in, using: however, when I refresh the page immediately after the user has successfully signed in, the $scope value reverts back into {{pseudonym}} and the parameter isn't available. How can I save this data persistently throughout the logged in session? Can you please provide the answer with an example? app.controller("MyregisterCtrl", ["$scope", "$stateParams", "Auth", "$state", "$location", "$modal", "DatabaseRef", "$rootScope", function ($scope, $stateParams, Auth, $state, $location, $modal, DatabaseRef, $rootScope) { $scope.user = {}; $scope.signIn = function () { if (!$scope.user.email && !$scope.user.password) { toastr.error("Add email and password"); } else { Auth.$signInWithEmailAndPassword($scope.user.email, $scope.user.password) .then(function(firebaseUser) { var userId = firebase.auth().currentUser.uid; DatabaseRef.ref('/users/' + userId).once('value') .then(function(snapshot) { pseudonym = snapshot.val().pseudonym; console.log("pseudonym: ", pseudonym); $scope.pseudonym = pseudonym; }); $state.go('app.dashboard'); if (!firebaseUser.emailVerified) { // firebaseUser.sendEmailVerification(); toastr.info('Your email is NOT verified.', 'Verify email!'); $state.go('login.signin'); } // $state.go('home'); }) .catch(function(error) { toastr.error(error.message, error.reason, { timeOut: 10000 }); $scope.user = {}; }) } };
You should use a Service to store the value and retrieve it whenever you need. var myApp = angular.module('myApp',[]); myApp.service('mySingleton', function() { var username= "test"; return { username : username }; }); function MyCtrl($scope, mySingleton) { $scope.username= mySingleton.username; } function MyCtrl2($scope, mySingleton) { $scope.username= mySingleton.username; }
Remove $modal effect
I want a simple HTML form showing user and password inputs, now form is showing up in modal box using bootstrap. I am good in java but my hands are tight on javascript and angularjs, bootstraps etc. Code1 $scope.modalShown = false; var showLoginDialog = function() { if(!$scope.modalShown){ $scope.modalShown = true; var modalInstance = $modal.open({ templateUrl : 'templates/login.html', controller : "LoginCtrl", backdrop : 'static', }); modalInstance.result.then(function() { $scope.modalShown = false; }); } }; other code from where the values are passing. code2 .controller('LoginCtrl', [ '$scope', '$state', '$modalInstance' , '$window', 'Auth', function($scope, $state, $modalInstance, $window, Auth ) { $scope.credentials = {}; $scope.loginForm = {}; $scope.error = false; //when the form is submitted $scope.submit = function() { $scope.submitted = true; if (!$scope.loginForm.$invalid) { $scope.login($scope.credentials); } else { $scope.error = true; return; } }; //Performs the login function, by sending a requeora to the server with the Auth service $scope.login = function(credentials) { $scope.error = false; Auth.login(credentials, function(user) { //success function $modalInstance.close(); $state.go('home'); }, function(err) { console.log("error"); $scope.error = true; }); code1 is written in the maincontroller of angular app where its triggering the modal box with user and password inputs getting credentials from login.js
define a redirect from dynamic URL in angular.js
I want to redirect the users, to their user profile page, after they successfully login (or their login is successfully authenticated). I take the users to their profile pages through the dynamic hyper link below: #/{{getAccountTypeName($parent.user.account_type)}}/{{$parent.user.handle}} And I've located where my users are authenticated at login. But How can I take a dynamically populated URL such as above, in angular JS and call it as a redirect in the below authentication statement. (I am new to angular) var email = $scope.loginForm.email; var password = $scope.loginForm.password; auth.login( email, password, function (data) { if (data.success == true) { $scope.initLogin(); $scope.loginCallback(data); $scope.showWelcome = true; addPointsLogin(); $("#welcomeModal").modal(); $scope.cancel = function () { $("#welcomeModal").modal("hide"); }; } else { alert(data.error_message); } }
app.controller('myCntrl', ['$http', '$scope', '$route', '$routeParams', '$location', function($http, $scope, $route, $routeParams, $location){ var email = $scope.loginForm.email; var password = $scope.loginForm.password; auth.login( email, password, function (data) { if (data.success == true) { $scope.initLogin(); $scope.loginCallback(data); $scope.showWelcome = true; addPointsLogin(); $location.path(path); // Put your path here console.log($routeParams); // Here you get your URL data $("#welcomeModal").modal(); $scope.cancel = function () { $("#welcomeModal").modal("hide"); }; } else { alert(data.error_message); } } }]);
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.