Am trying to route user to specific state when angular start, but it doesn't go to login when if it is true, instead of it goes to default routState
.run(function ($ionicPlatform, $rootScope, $state) {
var authToken = window.localStorage.getItem('authToken')
var shouldChangePassword=window.localStorage.getItem('shouldChangePassword')
if (authToken == 'undefined' || shouldChangePassword == 'true') {
event.preventDefault();
$state.go('login');
return;
}
.config(
//my routes
.state('login', {
url: '/login',
templateUrl: 'templates/account_login.html',
controller: 'loginCtrl',
})
$urlRouterProvider.otherwise('/tab/category');
)
how can i redirect to login and stop the app from going to default route
You could try hooking to UI-router's $stateChangeStart state change event:
.run(function ($ionicPlatform, $rootScope, $state) {
$rootScope.$on('$stateChangeStart', function (event) {
var authToken = window.localStorage.getItem('authToken');
var shouldChangePassword=window.localStorage.getItem('shouldChangePassword');
if (!authToken || shouldChangePassword) {
event.preventDefault();
$state.go('login');
}
});
But you might consider moving the authToken and shouldChangePassword to a separate service and read/set the value from/to that service instead of reading from the localstorage for every state change
Related
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
What I am trying to do is verify user authentication and respond accordingly. Either populate the $rootScope with session data or redirect to the login page. I want the session data to be accessible to other controllers and views.
The Session response from the server is a JSON object:
{"user_id":"17","name":"Administrator","user_email":"admin#example.com"}
My app.js looks like this:
var app = angular.module('app', ['ui.router'])
.config(function($stateProvider, $urlRouterProvider, $locationProvider){
$stateProvider
.state('login', {
url: '/login',
templateUrl: 'app/views/login.html'
})
.state('logout', {
url:'/logout',
templateUrl: 'app/views/login.html'
})
.state('dashboard', {
url:'/dashboard',
templateUrl:'app/views/dashboard.html',
role: 0
});
})
.run(function($rootScope, $location, Data){
$rootScope.$on('$StateChangeStart', function (event, toState, fromState) {
$rootScope.authenticated = false;
Data.get('session').then(function (results) { // Sends request to server the above mention JSON object is returned
if (results.user_id) {
$rootScope.authenticated = true;
$rootScope.user_id = results.user_id;
$rootScope.name = results.name;
$rootScope.user_email = results.user_email;
} else {
$location.path("/login");
}
});
});
Then in my controller:
app.controller('AuthCtrl', ['$scope','$rootScope', '$location', '$http', 'Data', function ($scope, $rootScope, $location, $http, Data) {
$scope.login = {};
$scope.doLogin = function (user) {
Data.post('login', { // Sends login information to server
user: user
}).then(function (results) {
if (results.status == "success") {
$location.path('dashboard');
}
});
};
$scope.logout = function () {
Data.get('logout').then(function (results) { // sends logout request to server
$location.path('login');
});
}
}]);
The login function and response works as it should. The logout function works and destroys the session. The API on the server responds the way it should, but the session data is not stored or accessible in the $rootScope and I can not use it in my views.
The dashboard view looks like this:
<div ng-controller="AuthCtrl">
<h4>User Authenticated</h4>
User ID: {{user_id}}
<br/>NAME: {{name}}
<br/>E-MAIL: {{user_email}}
<br/>
<a ng-click="logout();">Logout</a>
</div>
I'm not sure if I am using $rootScope.$on('$StateChangeStart', function(...) correctly. Any suggestions?
It should be $stateChangeStart. Note the first letter is small and not caps.
I'm using Devise for authenticatication in my Rails and Angular app. I'm trying to do a conditional statement on one of the states based on whether a user is authenticated.
I'm using the onEnter callback function to determine if a user is authenticated.
routes:
// Dashboard state
.state('dashboard', {
url: '/dashboard',
templateUrl: 'dashboard.html',
controller: 'MainCtrl',
})
// Login state
.state('login', {
url: '/login',
templateUrl: '_login.html',
controller: 'AuthCtrl',
onEnter: ['$state', 'Auth', function($state, Auth) {
Auth.currentUser().then(function (){
$state.go('dashboard');
})
}]
})
// Register state
.state('register', {
url: '/register',
templateUrl: '_register.html',
controller: 'AuthCtrl',
onEnter: ['$state', 'Auth', function($state, Auth) {
Auth.currentUser().then(function (){
$state.go('dashboard');
})
}]
})
$urlRouterProvider.otherwise('dashboard');
Nav controller
// NAV controller
// ------------------------------
.controller('NavCtrl', ['$scope', 'Auth',
// Main scope (used in views)
function($scope, Auth) {
$scope.signedIn = Auth.isAuthenticated;
$scope.logout = Auth.logout;
Auth.currentUser().then(function (user){
$scope.user = user;
});
$scope.$on('devise:new-registration', function (e, user){
$scope.user = user;
});
$scope.$on('devise:login', function (e, user){
$scope.user = user;
});
$scope.$on('devise:logout', function (e, user){
$scope.user = {};
});
}
])
Authentification controller
// Authentification controller
// ------------------------------
.controller('AuthCtrl', ['$scope', '$state', 'Auth',
// Main scope (used in views)
function($scope, $state, Auth) {
$scope.login = function() {
Auth.login($scope.user).then(function(){
$state.go('home');
});
};
$scope.register = function() {
Auth.register($scope.user).then(function(){
$state.go('home');
});
};
}
]);
How can I determine a non-authenticated user on the dashboard state, and redirect them to login?
You have have a few options to go down for this sort of thing:
1) In MainCtrl add an init() function which checks if(!Auth.isAuthenticated) $state.go('login'). Downside is if they lose auth after the controller is instantiated this does not redirect them.
2) I presume you're making some kind of back-end calls which require Authentication. If so, if they're not authed you can catch the 401 response via an http interceptor and redirect them to login there. This is the method I always use.
3) Attach a $watch to Auth.isAuthenticated do the redirect from there
I would like to have two home pages, the first would be for users who have not logged in and the second for users that are logged in.
This is my current set up:
.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) {
if (next.authenticate && !Auth.isLoggedIn()) {
$location.path('/login');
}
});
});
.config(function ($stateProvider) {
$stateProvider
.state('main', {
url: '/',
templateUrl: 'app/main/main.html',
controller: 'MainCtrl',
title: 'Home',
mainClass: 'home',
headerSearch: true
});
});
How could I reconfigure this so I could do something like the following:
.config(function ($stateProvider) {
$stateProvider
.state('welcome', {
url: '/',
templateUrl: 'app/welcome/welcome.html',
controller: 'WelcomeCtrl',
title: 'Welcome',
mainClass: 'welcome',
isLoggedIn: false
});
$stateProvider
.state('main', {
url: '/',
templateUrl: 'app/main/main.html',
controller: 'MainCtrl',
title: 'Home',
mainClass: 'home',
isLoggedIn: true
});
});
Just wanted to show, how we can manage authentication driven access to states. Based on this answer and its plunker, we can enrich each state (which should be accessible only for authenticated users) with a data setting, explained here: Attach Custom Data to State Objects (cite:)
You can attach custom data to the state object (we recommend using a data property to avoid conflicts)...
So let's have some states with public access:
// SEE no explicit data defined
.state('public',{
url : '/public',
template : '<div>public</div>',
})
// the log-on screen
.state('login',{
url : '/login',
templateUrl : 'tpl.login.html',
controller : 'UserCtrl',
})
...
And some with private access:
// DATA is used - saying I need authentication
.state('some',{
url : '/some',
template : '<div>some</div>',
data : {requiresLogin : true }, // HERE
})
.state('other',{
url : '/other',
template : '<div>other</div>',
data : {requiresLogin : true }, // HERE
})
And this could be hooked on on the state change:
.run(['$rootScope', '$state', 'User', function($rootScope, $state, User)
{
$rootScope.$on('$stateChangeStart'
, function(event, toState, toParams, fromState, fromParams) {
var isAuthenticationRequired = toState.data
&& toState.data.requiresLogin
&& !User.isLoggedIn
;
if(isAuthenticationRequired)
{
event.preventDefault();
$state.go('login');
}
});
}])
See all that in action here
There is similar Q & A were I try to show the concept of redirecting Authenticated and Not Authenticated user:
Angular UI Router: nested states for home to differentiate logged in and logged out
maybe that could help to get some idea, how we can use ui-router, and its event '$stateChangeStart' to hook on our decision manager - and its forced redirecting...
the code should be something like this
$rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState) { //, fromParams
console.log(toState.name + "," + fromState.name);
if(fromState.name === "") {
if (Auth.isLoggedIn()) {
$state.go('welcome');
event.preventDefault();
} else {
$state.go('home');
event.preventDefault();
} else {
if (toState.authenticate && !Auth.isLoggedIn()) {
$toState.go('login');
event.preventDefault();
}
}
}
so if user entering the application, then if he is logged in take him to welcome else take him to home.
once he is inside, then if he hits some route which needs auth.. then redirect him to login page..
sorry if i did not understood you requirement fully...
.config(function ($stateProvider,$rootScope) {
$stateProvider
.state('welcome', {
url: '/',
templateUrl: 'app/welcome/welcome.html',
controller: 'WelcomeCtrl',
onEnter: function() {
if (userIsLoggedIn()) {
$stateProvider.go('home');
}
});
});
I had problem like this and I solved it like this
.run(function ($rootScope, $location, AuthService) {
// start showing PRELOADER because we doing async call and we dont know when it will be resolved/rej
AuthService.checkLoginStatus().then(
(resp) => {
// fire logged in user event
$rootScope.$broadcast('userLogged',resp);
$location.path(YourHomePageUrl);
},
(err)=> {
// Check that the user is not authorized redirect to login page
$location.path(loginURL);
}
}).finally(
()=> {
// finnaly Set a watch on the $routeChangeStart
/// Hide PRELOADER
$rootScope.$on('$routeChangeStart',
function (evt, next, curr) {
if (!AuthService.isLoggedIn()) {
$location.path(art7.API.loginURL);
}
});
}
)
});
and im not using interceptor for handling 401 not authorized errors, thats my solution
Using the $rootScope.$on('$routeChangeStart', function(event, next, current), I'm redirecting to the signin page if the route requires authentication. That works perfectly.
How do I redirect back to the intended route though after signing in?
This is a simplified version of what's working for me:
app = angular.module('ngApp', []).config(function ($routeProvider) {
$routeProvider
.when('/dashboard', {
templateUrl: 'dashboard.html',
controller: 'dashboardController',
loginRequired: true //
})
.when('/login', {
templateUrl: 'login.html',
controller: 'loginController'
})
.otherwise({redirectTo: '/login'})
});
Then in the application's run block:
app.run(function ($location, $rootScope) {
var postLogInRoute;
$rootScope.$on('$routeChangeStart', function (event, nextRoute, currentRoute) {
//if login required and you're logged out, capture the current path
if (nextRoute.loginRequired && Account.loggedOut()) {
postLogInRoute = $location.path();
$location.path('/login').replace();
} else if (postLogInRoute && Account.loggedIn()) {
//once logged in, redirect to the last route and reset it
$location.path(postLogInRoute).replace();
postLogInRoute = null;
}
});
});
Here is an example of how I am doing it, hope it helps:
On the route provider, first set up the public access somehow:
// Just a demo on how the routes were set up to determine public access
angular.module('ngApp', []).config(function ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'views/main.html',
controller : 'MainController',
})
.when('/login', {
templateUrl : 'views/login.html',
controller : 'LoginController',
publicAccess : true // This is used in $routeChangeStart later
});
});
});
Then:
$rootScope.$on('$routeChangeStart', function(event, next, current) {
var publicAccess = next.publicAccess || false;
// This is just a service I made, this is how I check logged in status
// AuthenticationService.check() returns a promise
AuthenticationService.check().then(function() {
// As this is a promise, this block signals that the user is logged in
// If the page is marked as public access, then redirect to private area
if (publicAccess)
$location.path('/').replace();
}, function() {
// Since this segment of the promise signals that the user is not
// logged in, if the page is not publicly accessible, redirect to login
if (!publicAccess)
$location.path('/login').replace();
});
});
The following answer is from the OP.
Here's how I solved it:
I added this event broadcast into the $routeChangeStart
$rootScope.$on('$routeChangeStart', function(event, next, current) {
if (next.authRequired) {
var deferred = $q.defer(),
_token = mainConfig.csrfToken;
security.getCurrentUser(true, _token).success(function (data, status, headers, config) {
if(status == 200) {
// set the local scope variables
next.scope.isAuthenticated = true;
next.scope.user = security.currentUser;
// Broadcast out to each of the listeners
$rootScope.$broadcast('currentUserAuthenticated');
// Any other response requires a signin. Redirect.
} else {
next.scope.isAuthenticated = false;
next.scope.user = null;
$rootScope.$broadcast('authenticationRequired', $location.url());
$location.path('/signin');
}
});
}
});
Then in my security factory, I listened for the event and stored the location, like so:
$rootScope.$on('authenticationRequired', function(event, callingRoute) {
redirectRoute = callingRoute;
});