Well, I come from an ngRoute background, and I just started to learn uiRouter.
I used to write two different angular app one for login (login.html), and one for the main app (index.html). In he back-end, I just check if the user is authenticated then redirect them to index.html, else to login.html.
Now I want to encapsulate login into the main app. I have a lot of states requiring user to be logged in. In a lot of them some parts like header and footer are fixed. I'm looking for a concrete solution to handle it with these considerations:
I don't want to use resolve property for all of the states which need authentication.
I don't want my routes to be like /some-prefix-for-authed-paths/... for states that require authentication.
If somebody navigates to a route which requires authentication, I want to redirect them to login page and after login I want to redirect them back into where ever they were.
for 4xx errors, I want to display a custom error page(s) (probably route-dependent). For example if someone navigates to /users/3, and there is no user 3, (which will be determined by the resolve property of the state which will be rejected in case of not-existing user) I want to show them error page, but I DON'T want to change route to something like /error or something, because it actually doesn't make sense at all! Plus, I don't want a state push in the browser history.
You could try to implement an httpInterceptor that catches all non 2xx requests and redirect to some sort of error/login page.
Also to check for authentication you could use uiRouter onStateChange event by adding an access level to your states
.state('someState', {
url: '/someUrl',
isPrivate: true
})
$rootScope.$on('$stateChangeStart',
function(event, toState, toParams, fromState, fromParams){
event.preventDefault();
// Check if state is private
if(toState.isPrivate) {
// Do something
}
});
Note! This is just somewhat pseudo code
Related
I am trying to determine what is the best approach to adopt when a user is already logged in and type /login in the browser URL.
Should I prevent the event to be fired by using $routeChangeStart or $locationChangeStart (which one to chose)
Should I instead redirect to /home ?
Where should I implement the event capture ? In the controller or the app.js
I think you have somewhere a service that tells you if the user is logged in or not. My approach is to use a "resolve" clause/guard in the route/state you want to protect. In this case (login page) you could say something like: are you already logged in? Redirect to /home. Are you not logged in? I leave you load the page and log in. The choice to redirect to /home or elsewhere it depends on your application. You could even redirect to the /my-profile page if you have it.
So I have a login scheme that only needs user login behind specific pages. Currently, I have the page checking if the user exists via the $onInit function, then firing the login (which redirects to a login page via Code Flow in OAuth 2). Then when I've logged in, the page redirects back to the previous page, instead of the page I fired the login even from.
I think this is due to ui-router not registering history with the browser before it redirects, thus the $state object in the OAuth 2 parameters, when decoded shows me the path: "redirectUrl":"http://localhost:3000/reservations" when it should be "redirectUrl":"http://localhost:3000/reservations/purchase".
So my question is two fold.
Is $onInit an appropriate place to be doing this, or should I have some global routechange listener or use resolve? If I use either of those two methods, I think I will still have the route problem.
If I keep using $onInit, is there some way to make sure the url is registered correctly so that it will redirect to the correct location post-login?
$onInit function:
function init() {
var token = Meteor.call('getAccessToken');
if (!token) {
Meteor.loginWithOidc();
}
}
I am testing my Angular application that uses ui.router and authentication token. There is a partial page that is available only to authorized users. Here is the steps to reproduce:
1) Login
2) Navigate to partial user profile page
3) Logout
it deletes authentication header like this:
delete $http.defaults.headers.common['x-access-token'];
then navigates to a different page
4) In the browser address bar manually navigate back to the profile page.
browser sends http request with the token I just removed! I can see it in Chrome Network view.
5) Subsequent requests to the profile page are issued without the token and gets rejected
This problem does not happen in Safari.
This is actually a known problem with Angular JS caching templates. Sadly there seems to be no solution for it. '$templateCache.removeAll()' does not work for me. There are numerous posts about it:
AngularJS disable partial caching on dev machine
Remove Template cache on logout Angular.js
How to refresh / invalidate $resource cache in AngularJS
P.S. Actually, there is a solution that worked:
http://opensourcesoftwareandme.blogspot.com/2014/02/safely-prevent-template-caching-in-angularjs.html
Slightly modified version looks like this:
$rootScope.$on('$stateChangeStart',
function(event, toState, toParams, fromState, fromParams) {
if (typeof(fromState.templateUrl) !== 'undefined'){
$templateCache.remove(fromState.templateUrl);
}
});
After some more research I found even better solution. '$templateCache.removeAll()' actually does work, except that templates are still kept somewhere and to make sure they are updated I need to reload current state:
$templateCache.removeAll();
$state.transitionTo($state.current, $stateParams, {
reload: true,
inherit: false,
notify: true
});
This way I do not need to turn off the cache completely, but can just wipe it out whenever a user is logged in or out.
We have two diferent apps in our environment, one that handles login and the main portal. When anyone try to access a portal url and its not logged, the "otherwise" function will save this url and redirect to login.
$urlRouterProvider.otherwise(function($injector, $location){
console.log($location.absUrl());
//save attempted url
//redirect to login,
var loginUrl=$location.absUrl() + '#/login';
var $window=$injector.get('$window');
console.log(loginUrl);
$window.location.href=loginUrl;
});
This work fine in firefox, but in chrome, the 'login' state are never resolved, so it fall back to the default function every time, causing a loop.
You can see this issue in http://plnkr.co/edit/l51Wqn0EB1ySDjbScSvY
Could anyone help me with a solution?
EDIT : I need to perform the redirect with $window.location.href, because this app sometimes get loaded on /portal and need to redirect to /#/login.
When the app initially loads, it is hitting the "" path before going to the path in the URL (in your case, #/login). Each time you do a $window.location, you tell the app to reload. On each reload it looks for a state mapping to "" and cannot find it so it hits the otherwise... thus looping.
You can handle the empty route with a "when"
.when('', '/login')
You can also default it to your home page, assuming you would have logic for your home page to kick the user back to login if they are not logged in.
Not too sure if there is a need to use $window.location, but if you are only trying to store the current location and then direct to one of your states you can inject $state and use $state.go(...)
$urlRouterProvider.otherwise(function($injector, $location){
console.log($location.absUrl());
//save attempted url
//redirect to login,
var $state=$injector.get('$state');
$state.go('login');
});
Updated plunker: http://plnkr.co/edit/aMsjxQ9WQIXqrQ7e9MJq?p=preview
I have an angularjs app and would like the homepage to depend on the logged in status of the user.
Im looking for something like the following to happen:
When I goto mypage.com use 'resolve' to perform an HTTP GET with a server to check if logged in.
If the user is not logged in resolve the promise from 1) and show some generic splash screen. For example, templateUrl: splash.html
If the user is logged in then fetch additional data from the server. Once the data has been returned then resolve the promise from 1) and show a page specific for this user. For example, templateUrl: loggedin.html
In either case, the URL should not change. ie: I do no want to redirect to another route. The URL should always be mypage.com.
How can I have a dynamic page like this without using any redirects?
Have a look # https://github.com/angular-app/angular-app, its the best example app I've found so far for dealing with the user authorization rabbit hole.
For a quick start with getting your head around how user auth works in angular-app, have a look at these 3 files:
https://github.com/angular-app/angular-app/blob/master/client/src/app/app.js
https://github.com/angular-app/angular-app/blob/master/client/src/common/security/security.js
https://github.com/angular-app/angular-app/blob/master/client/src/common/security/authorization.js