Implementing authentication with AngularJS and ui-router - angularjs

I am trying to deal with authentication with ui-router and angularJS, i would like to achieve this normal scenario where:
if (user is not authenticated && state requires authentication) then
go to 'login' state
else if (user is authenticated && state does not require authentication) then
go to 'main' state
else go to state requested by the user.
i have implemented it in a way i am not sure is correct and i need some validation here from you guys.
I have a resolve function that retrieving the information whether user is authenticated from the server, if it rejected, then I send the user to 'login' state in $stateChangeError. if it resolved I handle the other logic in $stateChangeSuccess.
But as I understand that the information would only be available in $stateChangeSuccess and not in $stateChangeStart , therefore if I implement the logic i wrote above in $stateChangeSucceess then some of the pages will flicker before the transition ends.
Please advise. Is there another way or a way to improve this solution ?. I thought of making a resolve function for each state , check if the user is authenticated and make the routing there.
Thanks.

This is a problem with single page apps. One of the solutions that I use in my angular pages is to cache the userid and the fact that the user is authenticated in local storage. You can add an expiry to this as well. Here's a link to the local storage module: https://github.com/grevory/angular-local-storage (there are others too).
Then in each state you can check to see if the user has already been authenticated rather than going back to the server each time. Also, this will help the back/fwd buttons work better. Typically in your controller you'd have code to check if the user is authenticated (which would probably go and check local storage for a flag and get the userinfo which was initially retrieved from the server).
I can see some security issues with this though if the browser is being shared by multiple users.
This article seems to list a better/newer way to do this: https://auth0.com/blog/2014/01/07/angularjs-authentication-with-cookies-vs-token/

using runfunction in your module combining with a service that knows if the user it's authenticated:
angular
.module('myApp')
.run(runFn);
runFn.$inject = ['$rootScope', 'MyAuthService', '$state'];
function runFn($rootScope, MyAuthService, $state){
$rootScope.$on('$stateChangeSuccess', function(e, toState, toParams, fromState, fromParams) {
var userAuthenticated = MyAuthService.isLogged();
//* check if the user it's present on cookies
//if(!userAuthenticated && $cookieStore.get('user_token'))
// If the token is in the cookies i get the user from server
//else
// $state.go('login')
//your logic here
/*if (user is not authenticated && state requires authentication) then $state.go('login')*/
});
}
This function will be executed always that a state change.

Related

Prevent session sharing between browser tabs

I have a nasty bug: open two tabs with login pages and log in with different users in each one. All requests from first tab that logged in return with 'unauthorized' error.
Frontend uses SESSION cookie and it looks like that cookie is overwritten by second successful login of the second tab and it tries to use this new cookie when browsing in the first tab.
Using Spring Boot 1.5.8, Spring-session 2, AngularJS 1.7.2
Configuration is very standard, so I don't think these boilerplates would be useful.
Until now I tried to set up a filter on backend that works before authentication, to somehow filter out requests that have known cookie, but I failed at this.
UPD:
Some way of preventing that situation when a user is logged in but with incorrect session is what I seek. Either blocking second login attempt in this browser, or kicking already logged in user when another one logs in in the same broser - all will do.
You can log out user from other tabs if you set some sort of token on local storage like the code below(in sucessfull login response from server )
localStorage.setItem('logout', 'logout-' + Math.random());
and have this function as a run block in your main app module:
function logoutFromOtherTabs(authService, $timeout) {
'ngInject';
window.addEventListener('storage', function (event) {
if (event.key === 'logout') {
$timeout(function () {
authService.logout();
}, 1000);
}
});

create generic function which will be call at all routes

In that function, I can get current controller and route value and according to current angular controller and route will call API which will return if user has access to that particular method or not.
I already did at server side using authorization attribute but need at client side.
Since you have not provided any code example. I assume you have routing in the client side. And you want authentication/authorization during the routing process.
To do this you can observe the route before route started changing and can check the authenticity of the user there and permit or refuse the user accordingly.
Sample code:
var app = angular.module('app', []);
app.run(function($rootScope) {
$rootScope.$on("$locationChangeStart", function(event, next, current) {
// check here the user's authenticity and let the user go or redirect to another page
});
});

Authentication Logic - Server vs Client

I'm trying to get my head around where authentication logic should live in my application, the approach I am trying to take is to have any auth responsibility handled by the server, redirecting to a login page that's separate from the main client side app - which I think is sensible?
I have an angularjs application which uses ui-router and makes api requests which are routed via the server.
I am using an Express server which is configured to use a couple of directories like so:
app.use(express.static('./dist/client'));
app.use(express.static('public'));
I then have middleware that performs an auth check (Im using express-session as well) and redirecting to login when required.
//A request to '/login' will serve the login page
app.use('/login', function(req, res){
res.sendFile(path.join(__dirname+'/public/login.html'))
});
//This will listen for all requests
app.use(function(req, res, next) {
if (req.url!== '/auth/login' && !req.session.accessToken) {
res.redirect('/login');
return;
}
next();
});
On initial page load, when no session cookie exists, express immediately redirects to the login view as expected.
After logon, and the main application loads, if I then manually delete the cookie in the browser and perform a state change that requires an api request (in a state resolve), the server returns the login view but this gets rendered inside the ui-view component being used by ui-router, rather than a full redirect to /login by the server.
Also, if I navigate to a page (after deleting cookie) that does not perform an api request, that page is served back, I guess as its not covered by my app.use middleware that does the redirect.
I feel I'm missing something obvious here, can someone help me please?
One way to handle this, there are others:
Make the API server return a 401 (unauthorized) error if the user is not authenticated, rather than redirecting them to the login page.
Then, in a run block, add a $stateChangeError event handler to the $rootScope. This way, if an API request is made from an unauthenticated user, it will trigger the event handler. From there you can redirect the user to your login page:
angular.module('myApp').run(function($rootScope, $window) {
$rootScope.$on('$stateChangeError', function() {
$window.location.href = '/login';
});
});
I'm not sure if it makes sense to worry about the other scenario where you delete the cookie and navigate to a page that does not make any API requests. What is such a user going to gain? In this hypothetical scenario, they are already looking at a page in your app (that might have sensitive data or not). How did they get there to begin with?
You could use a similar event handler for the $stateChangeStart event that checks for the presence of the cookie and redirect if it's missing. But, you don't want to put code in your client that validates the cookie, b/c then any curious visitor could read that code and learn how to create a cookie to fool your server.

Integrating Laravel, Facebook and Angular with token-based-authentication

My use case:
User is already logged in automatically server side using Facebook with laravel and Socialite.
I check if the user exists in the db and if not create it and log them into laravel.
Then I need to start an Angular app and make sure they are logged in with Laravel / Facebook.
After reading this article, it looks like this token based approach is what I should take.
In the tutorial you serve a login form with Angular, and then pass the email and password to an authenticate controller (Laravel), which returns a token (create by JWT-Auth).
Once the token is in Angular all is well, my problem is that I want to get the token directly into Angular without a login form since the user is already logged in as mention above.
I'm wondering if I could just output the token in markup somewhere and then pick it up from Angular or would that somehow be a security risk? I guess people will only be able to view source and see their own token?
If this is the wrong way to do this, then how should I do it? Do I need to authenticate with Facebook with Javascript, and then create a new laravel user with ajax?
Thanks so much!
One approach you could take is to add something to your .config block that checks for the presence of a JWT in local storage and if there isn't one there, makes a request to the API to see if the user is logged in on the Laravel side. If the user is logged in, a JWT is returned which can be picked up and saved in local storage. Since the .config block is run when the Angular app loads, this will only happen once, which is probably what you're looking for.
Here's what that might look like. First the Laravel side:
// AuthenticateController.php
...
// Simulates a user being logged in on the Laravel side through something
// other than credentials sent from the front-end. Obviously you would use
// Socialite
public function authenticate()
{
$user = User::find(1);
$token = JWTAuth::fromUser(1);
return response()->json(compact('token'));
}
...
Then the Angular:
// app.js
...
.run(function($rootScope, $state, $auth) {
if(!$auth.isAuthenticated()) {
// No credentials provided because the user is already logged in on the server
$auth.login().then(function() {
$state.go('users');
}
}
$rootScope.$on('$stateChangeStart', function(event, toState) {
...
This would just run when the Angular app loads, which will be after your user has logged in with Socialite. It's essentially just checking whether there is a user logged in on the back end and if so, the token is retrieved. It's a bit hacky and the interface isn't obvious, so it might not be the best solution.
You would need to arrange your authenticate controller on the Laravel side such that it returns the token if the user has logged in via socialite or require credentials if he/she hasn't (which is the traditional login).
You could also look at this approach to see if it works for you: https://github.com/barooney/jot-bot/tree/socialite
Let me know if that works out!

How should i recognize if user is logged in or not in angular/node application?

A scenario:
A user tries to login into the application, the credentials than passed to node to check if he exists, if so, some auth credential is placed to true, and the account of the user is than returned to the site, and with angular i have a service that does the following:
angular.module(myapp).service('accountService',function($http){
var account= null;
return {
authUser = function(credentials){
....
};
setaccount = function(accountObj){
account = accountObj;
...
}
}
Now it's ok if no refresh is going on, but when a refresh does happen, what is the best way to determine if a user is still logged in or not?
For example: i took a look into mean stack to see their approch, and what they do is like make the userObject exposed so i can do window.loggedinUser (That comes from a session storage of some kind?)
There are other examples i assume that deals with the run method doing something, but my question is this:
every time a user clicks refresh should the program do something in the run to determine if the user is still logged in or not (meaning dealing with server request in every refresh)?
What is the better solution to do so, a simple example will be appreciated....
For most AngularJS applications the user credentials are fetched from the server via an asynchronous HTTP request. This creates a timing problem of when AngularJS starts up and when the application knows if there is a user session.
You'll have to set the user flag to false at startup, and when the asynchronous call finishes you'll have to change it's state (if the user is logged in).
This becomes a problem if the current route is restricted to signed in users only.
The best recommended approach is to use route promises. Your accountService should provide a method that will return a promise that fetches the current user's session. If it already has it, then great use that one, otherwise it has to perform a HTTP request to get. Either way, the result should always be a promise.
This is how you should make the user session availible in your application. Via a promise on the route.

Resources