I am using route resolver in angularjs,for user will be redirect to login if user is not logged in as follows,
$routeProvider
.when('/', {
templateUrl: 'app/components/main/dashboard.html',
controller: 'dashboardController',
resolve: {
login: function ($rootScope, $location) {
if (!$rootScope.currentUser) {
$location.path('/login');
}
}
}
})
Here I want use this login function in many other routes,So i can copy paste same resolve function to every where as follows,
.when('/items', {
templateUrl: 'app/components/item/list.html',
controller: 'itemController',
resolve: {
login: function ($rootScope, $location) {
if (!$rootScope.currentUser) {
$location.path('/login');
}
}
}
})
It is working fine,my question is,is there any way to avoid this duplication of codes or is there any other better method ?
I set up a github repository yesterday which is a starting point for a web app and contains this feature here
If you look in public/app/app-routes.js you will see I have added resolve functions as variables, then you can simply do this rather than writing a whole function each time:
Function
var checkLoggedIn = function($q, $timeout, $http, $window, $location, $rootScope) {
// Initialize a new promise
var deferred = $q.defer();
// Make an AJAX call to check if the user is logged in
$http.get('/loggedin').success(function(user) {
// Authenticated
if (user !== '0') {
$rootScope.loggedInUser = user;
$window.sessionStorage['loggedInUser'] = JSON.stringify(user);
deferred.resolve();
}
// Not Authenticated
else {
$window.sessionStorage['loggedInUser'] = null;
$rootScope.loggedInUser = null;
deferred.reject();
$location.url('/login');
}
});
return deferred.promise;
};
checkLoggedIn.$inject = ["$q", "$timeout", "$http", "$window", "$location", "$rootScope"];
Route
.when('/profile', {
title: 'Profile',
templateUrl: '/app/templates/profile.html',
controller: 'ProfileController',
resolve: {
loggedIn: checkLoggedIn
}
})
Should be easily adaptable for your app. Hope that helps!
Related
I am having a lot of trouble determining how to add permissions / access-control to our Angular app. Right now we have this:
app.config(['$stateProvider', function ($stateProvider) {
$stateProvider
.state('default', {
url: '',
templateUrl: 'pages/home/view/home.html',
controller: 'HomeCtrl'
})
.state('home', {
url: '/home',
templateUrl: 'pages/home/view/home.html',
controller: 'HomeCtrl',
permissions: { // ?
only: ['Admin','Moderator']
},
access: { // ?
roles: ['*']
},
resolve: { // ?
authenticate: function($state, $q, $timeout){
},
}
})
}]);
I having trouble determining which methodology to use to create access control to each page.
Right now, the logged in user is stored in an Angular value:
app.value('USER', JSON.parse('{{{user}}}'));
The USER value contains the information about which roles / permissions the user has.
But I cannot inject USER into app.config() callback, it says "unknown provider".
How can I do access-control based of the USER parameters?
the key to perform that is to add your access control on an event $stateChangeStart
For example if you have your routing like that :
.state('termsofuse', {
url: "/terms",
templateUrl: "termOfUse.html",
resolve: {
authorizedRoles: function() {
return [USER_ROLES['su'],
USER_ROLES['user'],
USER_ROLES['admin'],
USER_ROLES['skysu']
]
}
}
})
you may define your access controle like that
.run(
function($rootScope, $q, $location, $window, $timeout) {
$rootScope.$on(
'$stateChangeStart',
function(event, next, current) {
var authorizedRoles = next.resolve.authorizedRoles();
//this function controls if user has necessary roles
if (!isAuthorized(authorizedRoles)) {
event.preventDefault();
// and I broadcast the news $rootScope.$broadcast("AUTH_EVENTS.notAuthenticated");
} else {
$rootScope.$broadcast("AUTH_EVENTS.loginSuccess");
}
})
});
Then you just of to define your event's catcher to manager the desired behaviour (redirect / error message or whatever necessary)
You might want to check out ngAA, I use it alongside ui-router.
This is how I got it to work. I am not sure if it's the best way, but it does work.
You cannot inject services/values in the app.config(), but you can inject services/values into the resolve.authenticate function.
I decided to use the resolve.authenticate methodology.
.state('home', {
url: '/home',
templateUrl: 'pages/home/view/home.html',
controller: 'HomeCtrl',
resolve: {
authenticate: handlePageRequest['home'] // <<<<
}
})
and then we have a handler:
let handlePageRequest = {
'default': function ($q, USER, $state, $timeout, AuthService) {
// this page is always ok to access
return $q.when();
},
'home': function ($q, USER, $state, $timeout, AuthService) {
if(AuthService.isHomePageAccessible(USER)){
return $q.when();
}
else{
$timeout(function () {
$state.go('not-found'); // we can prevent user from accessing home page this way
}, 100);
return $q.reject()
}
},
};
I'm using the latest Angular + Firebase and trying to set up a login authorization system. I have home.html which contains login+signup links, going to login.html and adding credentials works just fine (logging correct UID when submittet) but it's supposed to route to dash.html but goes back to home.html.
I've figured out that it seem to be issues with my resolve functions because the problem disappears when I remove .otherwise. But I still want (need?) it there I think.
If I'm logged in (but redirected to home.html) I can still access dash.html through the URL and I cannot access it again if I use the logout function at dash.html and that's how it should be.
But I can't figure out why I'm redirected to home.html in the first place.
Here's some of the code, any help appreciated:
My .run, .config and routes.
app.run(['$rootScope', '$location',
function($rootScope, $location){
$rootScope.$on('$routeChangeError',
function(event, next, previous, error){
if(error === 'AUTH_REQUIRED'){
$location.path('/home');
}
});
}]);
app.config(['$routeProvider', '$locationProvider', function($routeProvider,
$locationProvider){
$routeProvider
.when('/home', {
templateUrl: '/home.html',
controller: 'homeController'
})
.when('/login', {
templateUrl: '/login.html',
controller: 'loginController',
resolve: {
'currentAuth': ['Auth', function(Auth){
return Auth.$waitForAuth();
}]
}
})
.when('/dash', {
templateUrl: '/dash.html',
controller: 'dashController',
resolve: {
'currentAuth': ['Auth', function(Auth){
return Auth.$requireAuth();
}]
}
})
.otherwise({ redirectTo: '/home' });
}]);
My login controller:
app.controller('loginController', ['currentAuth', '$scope', '$firebaseAuth',
'Auth', '$location', '$rootScope',
function(currentAuth, $scope, $firebaseAuth, Auth, $location, $rootScope){
var ref = new Firebase('https://url.firebaseio.com');
$scope.auth = $firebaseAuth(ref);
$scope.loginUser = function(){
$scope.auth = Auth;
$scope.auth.$authWithPassword({
email:$scope.email,
password:$scope.password
}, {
remember: 'sessionOnly'
}).then(function(authData) {
console.log('Logged in as: ', authData.uid);
$scope.auth.$onAuth(function(authData) {
$rootScope.auth = true;
$scope.auth = Auth;
$location.path('/dash.html');
})
}).catch(function(error) {
console.log('There was an error: ', error);
});
};
}]);
And my factory and module:
var app = angular.module('app', ['firebase', 'ngRoute']);
app.factory('Auth', ['$firebaseAuth',
function($firebaseAuth){
var ref = new Firebase('https://url.firebaseio.com');
return $firebaseAuth(ref);
}]);
it has with your resolve issue.
If you look at the documentation firebase doc
you will see that they use the
$waitForSignIn
or
$requireSignIn
functions. I know that because I have done the same thing. Try that instead and it should work
Why $location.url( "/messages" ); is not working? is there any restriction about $location inside a factory in AngularJS? any idea what is going on?
If I'm doing something wrong can you please tell my how should I redirect to another url "/messages"? I can't reload the page using so $window is not an option.
Thanks
My factory:
(function () {
'use strict';
angular
.module('app.conversation')
.factory('conversationFactory', conversation);
conversation.$inject = ['$rootScope', '$q', 'authFactory', '$location'];
function conversation($rootScope, $q, authFactory, $location) {
var service = {
changeLocation: changeLocation
};
return service;
function changeLocation() {
$location.url( "/messages" ); // recarga MIERDA!!!!!
}
}
})();
Route works fine:
(function () {
'use strict';
angular
.module('app.messages')
.config(configFunction);
configFunction.$inject = ['$routeProvider'];
function configFunction($routeProvider) {
$routeProvider
.when('/messages', {
templateUrl: 'app/messages/messages.html',
controller: 'messagesController',
controllerAs: 'meC',
resolve: {
user: function ($q, $timeout, authFactory) {
return authFactory.isLoggedInPromise();
}
}
}).when('/compose/:to?', {
templateUrl: 'app/messages/compose.html',
controller: 'composeController',
controllerAs: 'mcC',
resolve: {
user: function ($q, $timeout, authFactory) {
return authFactory.isLoggedInPromise();
}
}
});
}
})();
use $location.path( "/messages" );
I did:
$location.path( "/messages" ); // and then
$rootScope.$apply()
$location.path doesn't change in a factory with AngularJS
I am aware that the error which is in the title of this question is basically because in my controller I am injecting a Session service in my controller that has not been defined. I am currently looking at: Angular Devise for which I am rolling out in an application that is using rails but have angular and rails separate. My setup on the angular side is as followed:
main.js
angular.module('App.controllers', []);
angular.module('App.config', []);
angular.module('App.directives', [])
angular.module('App.resources', ['ngResource']);
angular.module('App.services', []);
var App = angular.module("App", [
"ngResource",
"ngCookies",
"$strap.directives",
"App.services",
"App.directives",
"App.resources",
"App.controllers",
"TotemApp.config"
], function ($routeProvider, $locationProvider, $httpProvider) {
var interceptor = ['$rootScope', '$q', function (scope, $q) {
function success(response) {
return response;
}
function error(response) {
var status = response.status;
if (status == 401) {
window.location = "/login";
return;
}
return $q.reject(response);
}
return function (promise) {
return promise.then(success, error);
}
}];
});
Session.js
App.service('Session',[ '$cookieStore', 'UserSession', 'UserRegistration', function($cookieStore, UserSession, UserRegistration) {
this.currentUser = function() {
return $cookieStore.get('_angular_devise_user');
}
this.signedIn = function() {
return !!this.currentUser();
}
this.signedOut = function() {
return !this.signedIn();
}
this.userSession = new UserSession( { email:"sample#email.com", password:"password", remember_me:true } );
this.userRegistration = new UserRegistration( { email:"sample#email.com", password:"password", password_confirmation:"password" } );
}]);
sessions_controller
App.controller('SessionsController', ['$scope', '$location', '$cookieStore', 'Session', function($scope, $location, $cookieStore, Session) {
$scope.session = Session.userSession;
$scope.create = function() {
if ( Session.signedOut ) {
$scope.session.$save().success(function(data, status, headers, config) {
$cookieStore.put('_angular_devise_user', Session.userSession.email);
$location.path('/todos');
});
}
};
$scope.destroy = function() {
$cookieStore.remove('_angular_devise_user');
$scope.session.$destroy();
};
}]);
routes.js
'use strict';
App.config(function($routeProvider, $httpProvider, $locationProvider) {
$routeProvider
.when('/', {
templateUrl: 'views/main/home.html',
controller: 'MainCtrl'
})
.when('/login', {
templateUrl: 'views/sessions/new.html',
controller: 'SessionsController'
})
.when('/sign_up', {
templateUrl: 'views/registrations/new.html',
controller: 'RegistrationsController'
})
.otherwise({
redirectTo: '/'
});
});
This error occurs when I try to access the login page or register page. If someone can shed some light that would be greatly appreciated. Not entirely sure how to resolve this Error: Unknown provider: $SessionProvider <- $Session error
At first glance this looks correct. I can only assume that perhaps you've not included Session.js in your index.html file?
Angular clearly doesn't know what 'Session' service is so there's something wrong there either file not loaded, not loaded correctly or something along those lines as far as I can see.
Edit: Does the error say 'SessionProvider <- Session' or '$SessionProvider -< $session' because if it's the latter, then something is named wrong somewhere in your app since your service is named 'Session' not '$Session'.
When you have syntax error in other javascript or typescript files. You will get the same error. Please make sure that you have no syntax error.
I'm trying to create an SPA where you have to be logged in to access almost everything. So naturally, the default screen you see is the login screen. However, after a user has logged in, no matter what the ui-sref is, ui-router redirects to the login page (even when the user is authenticated). Here is my ui-router code:
(function () {
'use strict';
angular
.module('app', ['ui.router', 'satellizer'])
.config(function ($stateProvider, $urlRouterProvider, $authProvider, $httpProvider, $provide) {
$httpProvider.interceptors.push(['$q', '$injector', function($q, $injector){
return {
responseError: function (rejection) {
var $state = $injector.get('$state');
var rejectionReasons = ['token_not_provided', 'token_expired', 'token_absent', 'token_invalid'];
angular.forEach(rejectionReasons, function (value, key) {
if (rejection.data.error === value) {
localStorage.removeItem('user');
$state.go('auth');
}
});
return $q.reject(rejection);
},
response: function(response) {
var authorization = response.headers('authorization');
if(authorization !== null) {
authorization = authorization.substr(7).trim();
//console.log(authorization);
var $auth = $injector.get('$auth');
$auth.setToken(authorization);
}
return response;
}
}
}]);
$authProvider.loginUrl = 'mingdaograder/api/authenticate';
$stateProvider
.state('users', {
url: '/users',
templateUrl: 'views/userView.html',
controller: 'UserController as user'
})
.state('subjects', {
url: '/users/:user_id/subjects',
templateUrl: 'views/subjectsView.html',
controller: 'SubjectsCtrl as subjectsCtrl'
})
.state('subject', {
url: '/users/:user_id/subjects/:subject_id',
templateUrl: 'views/subjectView.html',
controller: 'SubjectCtrl as subjectCtrl'
})
.state('auth', {
url: '/auth',
templateUrl: 'views/authView.html',
controller: 'AuthController as auth'
});
//.state('otherwise', {
// url: '*path',
// templateUrl: 'views/authView.html',
// controller: 'AuthController as auth'
//});
//$urlRouterProvider.otherwise('/auth');
$urlRouterProvider.otherwise(function($injector, $location) {
console.log("Could not find " + $location);
$location.path('/auth');
});
})
.run(function ($rootScope, $state, $log) {
$rootScope.$on('$stateChangeStart', function (event, toState) {
console.log(toState.name);
var user = JSON.parse(localStorage.getItem('user'));
if (user) {
$rootScope.authenticated = true;
$rootScope.currentUser = user;
}
}
);
}
);
})();
Anytime I try to use $state.go(any state name here) or even type the address into the address bar, I am always redirected to the auth state. On the console the message is "Could not find http://localhost/#/" for every single route. I can type in http://localhost/#/users/5/subjects and I get the same message.
Here is one of my controllers doing a redirect:
(function () {
'use strict';
angular
.module('app')
.controller('AuthController', AuthController);
function AuthController($auth, $state, $http, $rootScope, $log) {
var vm = this;
vm.loginError = false;
vm.loginErrorText;
vm.login = function () {
var credentials = {
username: vm.username,
password: vm.password
};
$auth.login(credentials).then(function () {
return $http.get('api/authenticate/user');
}, function (error) {
vm.loginError = true;
vm.loginErrorText = error.data.error;
}).then(function (response) {
var user = JSON.stringify(response.data.user);
localStorage.setItem('user', user);
$rootScope.authenticated = true;
$rootScope.currentUser = response.data.user;
//$log.info('From AuthCtrl: ' + $rootScope.currentUser.id);
$state.go('subjects', {user_id:$rootScope.currentUser.id});
});
}
}
})();
Any ideas what I'm doing wrong? Thanks a lot for your time.
Update: Ok, I haven't found a way to fix it but I think I may have found a possible cause. It seems to only happen for the routes with parameters. For example, if I go to the users state, whose path is /users, there is no redirect. However, if I go to the subjects state, whose path is /users/:user_id/subjects, it does redirect. It's like the Url matching service can't recognize that /users/5/subjects matches /users/:user_id/subjects, so redirects. Any ideas how to work around this?
I found I didn't have a '/' at the beginning of my initial state url. Every time I navigated to the state, the missing '/' seemed to push it into the stateProvider.otherwise.
state1: 'opportunity'
state1Url : '/opportunity/' <== added initial forward slash to make it work.
state2: 'opportunity.create'
state2Url : 'create/'
The first path to be recognised will be the selected as the current location. This means that the order of your route definitions is crucially important. In your case you only have a single catch-all otherwise route definition and since all routes match this then all routes are directed to your login page ignoring any other route definitions you may have, including all your stateProvider state definitions.
One way to fix this is to remove the urlRouterProvider route definition altogether and instead use the *path syntax provided by ui-router to create an alternative otherwise state (which must be defined last for the same reasons given above).
Therefore your code might look something like this:
$stateProvider
.state('auth', {
url: '/auth',
templateUrl: 'views/authView.html',
controller: 'AuthController as auth'
})
.state('users', {
url: '/users',
templateUrl: 'views/userView.html',
controller: 'UserController as user'
})
.state('subjects', {
url: '/users/:user_id/subjects',
templateUrl: 'views/subjectsView.html',
controller: 'SubjectsCtrl as subjectsCtrl'
})
.state('subject', {
url: '/users/:user_id/subjects/:subject_id',
templateUrl: 'views/subjectView.html',
controller: 'SubjectCtrl as subjectCtrl'
})
.state("otherwise", {
url: "*path",
templateUrl: 'views/authView.html',
controller: 'AuthController as auth'
});
From experience, this is either due to the / missing at either the beginning or the end of the url route property definition.
Make sure for parent routes to add the initial forward slash to your routes.
.state('checkers', {
url: '/checkers/',
templateUrl: 'checkers.html',
controller: 'CheckersController',
title: 'Checker',
})
(function () {
'use strict';
angular
.module('app', ['ui.router', 'satellizer'])
.config(function ($stateProvider, $urlRouterProvider, $authProvider, $httpProvider, $provide) {
$httpProvider.interceptors.push(['$q', '$injector', function($q, $injector){
return {
responseError: function (rejection) {
var $state = $injector.get('$state');
var rejectionReasons = ['token_not_provided', 'token_expired', 'token_absent', 'token_invalid'];
angular.forEach(rejectionReasons, function (value, key) {
if (rejection.data.error === value) {
localStorage.removeItem('user');
$state.go('auth');
}
});
return $q.reject(rejection);
},
response: function(response) {
var authorization = response.headers('authorization');
if(authorization !== null) {
authorization = authorization.substr(7).trim();
//console.log(authorization);
var $auth = $injector.get('$auth');
$auth.setToken(authorization);
}
return response;
}
}
}]);
$authProvider.loginUrl = 'mingdaograder/api/authenticate';
$stateProvider
.state('users', {
url: '/users',
templateUrl: 'views/userView.html',
controller: 'UserController as user'
})
.state('subjects', {
url: '/users/:user_id/subjects',
templateUrl: 'views/subjectsView.html',
controller: 'SubjectsCtrl as subjectsCtrl'
})
.state('subject', {
url: '/users/:user_id/subjects/:subject_id',
templateUrl: 'views/subjectView.html',
controller: 'SubjectCtrl as subjectCtrl'
})
.state('auth', {
url: '/auth',
templateUrl: 'views/authView.html',
controller: 'AuthController as auth'
});
//.state('otherwise', {
// url: '*path',
// templateUrl: 'views/authView.html',
// controller: 'AuthController as auth'
//});
//$urlRouterProvider.otherwise('/auth');
$urlRouterProvider.otherwise(function($injector, $location) {
console.log("Could not find " + $location);
$location.path('/auth');
});
})
.run(function ($rootScope, $state, $log) {
$rootScope.$on('$stateChangeStart', function (event, toState) {
console.log(toState.name);
var user = JSON.parse(localStorage.getItem('user'));
if (user) {
$rootScope.authenticated = true;
$rootScope.currentUser = user;
}
}
);
}
);
})();