How to stop Angular App on 401 response - angularjs

Hey im building login system using angularjs. I have one thing which annoys me. For example i have page A with 2 rest request. The first request send some public data - you dont need auth for this, and some private - for current user.
My api check the token - for public data, becouse user always can delete cookies - error response send 401 and angular catch it, and check authience for api/me- similarly send 401. What's the problem? Or what annoying me?
In one page we have two request for api/me to see some private data (private buttons for example) and public data in one page, but when we have got unauth user (probably deleted a cookies) http interceptor catch 2x 401 response error, from api/me and api/public data. How can I solve this problem? Becouse it's doesn't matter how much 401 response we've got. The one 401 response is enough to state that we should logout user.
By the way I was interesting how fb check cookies, they doing it asynchronous becouse when we remove cookie names 'xs' server send request to ajax api then our fb is totally blocked. Where I can learn more about this technique ?

basically the states within ui.router (which is used by professional developers instead of angulars default routing) , can be given a property to determine whether auth is required ...
.state('login', {
url: '/login',
templateUrl: 'routes/login/login.html',
controller: 'LoginController',
controllerAs: 'loginCtrl',
authenticate: false
})
.state('home', {
url: '/home',
templateUrl: 'routes/home/home.html',
controller: 'HomeController',
controllerAs: 'homeCtrl',
authenticate: true
})
then you can use the authenticate property to determine if the state requires auth. on every state change you can make an http call to your isauthenticated api if the state requires auth. if that call returns 401 then call your logout function.
your logout function might clear auth cookies and send the user to the login state.
you can also watch for 401s in your http interceptor and deal with them how you think best.

Related

Redirect user after validating their ID token (GAE/Firebase)

I followed the diagram below to integrate Firebase Authentication to my GAE web app, but I am having trouble redirecting users to homepage after they log in.
Currently, I have a FirebaseUI widget on my login page that handles the authorization. I configured the widget such that it sends the ID token of the user to my backend server with an XHR as described here upon a successful login, so that I can verify the integrity and authenticity of the token. However, after I am done verifying, I am not able to redirect the user to homepage since apparently that's how XHR works.
As can be seen from my network logs below, my app does indeed request a redirection, but my browser does not respond to it.
I am not sure what the best way to proceed is as my experience in web development is nonexistent and Firebase documentation was not helpful. Any direction you can provide me would be appreciated!
I was able to implement this using a callback function that is executed on the frontend upon verification of the token. Something along the lines of:
jQuery.ajax({
type: 'POST',
url: '/login',
dataType: 'json',
data: 'idtoken=' + idToken,
success: function(response) {
window.location.replace(response.redirect);
},
error: function(xhr, textStatus, errorThrown) {
// Handle error
}
});

Methods of saving user info in AngularJS

I want to know about the ways of saving user info.
Many seniors have recommended using $cookieStore, or Authentication or etc.
But how about using $rootScope?
My idea is when user has logged in, saving his/her id and password into $rootScope.
(Naming like $rootScope.user_id = 'stupid';)
Is this dangerous way?
I don't know whether this question is duplicated or not, but I couldn't find one talking about using $rootScope.
.
.
UPDATE
My config is like below.
'root controller' can see every scopes, so even if I refreshed pages,
$rootScope value does not disappear.
$stateProvider
.state('root.login',{
url: '/login',
controller: 'LoginCtrl',
templateUrl: 'views/login.html'
})
.state('root.signup',{
url: '/signup',
controller: 'LoginCtrl',
templateUrl: 'views/signup.html'
})
.state('root.main',{
url: '/main',
controller: 'MainCtrl',
templateUrl: 'views/main.html',
})
Its very bad way to store raw user credential in rootScope or cookies. However you can archive this by using userToken or session given by server side.
Example for userToken
send user login req to backend server
server return response userToken
angularjs store userToken in cookies
everytime angularjs req to backend, must append with this userToken(usually put in header)
Example for session
send user login req to backend server
server return result (as backend server will create session on http in server itself)
angularjs can send req to backend normally ( backend will validate whether session is valid or not to accept the req)
so if user refresh the page or switch the page you can call backend server to validate the userToken or session.
$rootScope will be vulnerable if you are storing sensitive data,instead use localstorage for storing user credential with encryption using some encryption algorithm and key and as mentioned above , create service to get and set values.

how to handle passport-facebook callback in angular client?

I am developing a MEAN application. I am using passport for authentication- local, facebook and google strategies.
I am using angularjs client. All the routing is handled at client. I am only consuming server data apis.
When using passport-facebook strategy, I am using below code at node server as per passport docs.
app.get('/auth/facebook',passport.authenticate('facebook-auth', { scope : ['email'] }));
app.get('/auth/facebook/callback',passport.authenticate('facebook-auth', {
successRedirect : '/home',
failureRedirect : '/login',
scope:['email']
}));
Problem I am facing is when user click on "Sign in using Facebook" button
<i class="fa fa-facebook"></i> Sign in using Facebook
Client will access "/auth/facebook" route that will eventually redirect user to facebook page for validating user's credentials.
After successful validation, user will be redirected to route "/home" as defined in "successRedirect" value.
Now the thing is, I want to use custom callback function instead of defining redirects for success or failure. It will look like below:
app.get('/auth/facebook/callback',passport.authenticate('facebook-auth', function(err,user,info){
if(err){
throw err;
}
else if(user === 'userexists'){
res.json({
'state':false,
'message':'User with this e-mail already exists'
});
}
else{
req.logIn(user,function(loginErr){
if(loginErr){
throw loginErr;
}
res.json({
'state':true,
'message':'You logged in successfully!'
});
});
}
}));
The root problem I am facing here, I can not use above custom callback as Client is not calling the "auth/facebook/callback" route, it is called by facebook.
So, there is no success handler waiting to catch above callback's response at client side!!
I want some way to get response in json form at client to eliminate server side redirection and also way to pass message and username to client after successful authentication by facebook.
I am about to give up with passport. Hoping for any possible solution before removing a lot of code!
Thanks
This can be accomplished by redirecting to another endpoint inside the facebook callback handler. There is no need to do res.json() on the callback from facebook since they only make a request to that in order to let you know if auth failed or succeeded. From their docs:
// GET /auth/facebook/callback
// Use passport.authenticate() as route middleware to authenticate the
// request. If authentication fails, the user will be redirected back to the
// login page. Otherwise, the primary route function function will be called,
// which, in this example, will redirect the user to the home page.
So facebook returns control over request process back to you when they call /auth/fb/callback but it's up to you what to do next. Since once the user is successfully authenticated, you would have req.user available throughout the whole session. At this point, you can redirect to something like the have in the example /account and check if req.user with req.isAuthenticated() and complete the flow you desire.

UI Router and Satellizer force login

I know this question isn't very specific, but I have no idea how to solve this issue or where I should begin..
I'm using Satellizer for authentication with UI-Router. the problem is I have 2 routes /sign_in and /profile the /sign_in issue a request to the server returning the user info (when successful login)... this currentUser info is used all over the application...
the problem is when a user try to go to /profile before login, it renders the empty view... how do I enforce user to login first before accessing these "restricted" views ?!
again I know this isn't a specific question but I really don't know where to start
Have a look into handling the routechangestart event on UI router, I did something similar.
I think this will give you some clues http://arthur.gonigberg.com/2013/06/29/angularjs-role-based-auth/
The key is that you need to create some form of authentication service where you can store the logged in state of the user, and since a service is invoked once (singleton) it is shared across your app (controllers, other services, etc). Then in the routechangestart event handling you can prevent routing to the target page and redirect to login if user is not authenticated.
I found the answer in this Github Repo
the idea is to have role based authentication as discussed in the Repo readme.
so the code for forbidding the unauthorised access to /profile would be something like the following
$stateProvider.state("profile", {
url: "/profile",
templateUrl: "app/profile/profile.html",
controller: "ProfileCtrl",
controllerAs: "user",
data: {
permissions: {
except: ['guest'],
redirectTo: 'sign_in'
}
}
});
given that you defined the guest role
definePermissions = function(Permission, Identity) {
Permission.defineRole('guest', function(stateParams) {
return !Identity.currentUser;
});
};

Angular ui-router using resolve validate authentication

I'm trying to see what's the best way to validate if a user is authenticated into the application.
Right now I'm using the following:
The user log in into the application
On Server Side a Token is created and send it back to the browser
On Log In Success, AngularJS stores the Token: $http.defaults.headers.common['RequestVerificationToken'] = token || $cookies.token;
On every http call to the server the Token is sent and is verified server side, in case the token doesn't exist then a 401 response status is sent to client.
This is working pretty well, now Im using UI-Router to control application states (pages - real scenario):
I have the following state:
$stateProvider
.state('personinfo', {
url: "/personinfo",
controller: 'PersonController',
templateUrl: "app/partials/personinfo.html"
})
Inside my PersonContoller:
app.controller('PersonController', function ($scope,$sce, $location, PersonService) {
$scope.title = 'Person Page';
PersonService.getPersons().success(function (response) {
$scope.persons = response.success;
}).error(function () {
// If token doesn't exist, a 401 reponse status is sent by server
$location.url('/login');
});
});
I don't really like how it works because AngularJS will load the state and download the partial HTML file and then it will go into the controller and execute the get method and if the token is not valid then it will redirect to login state.
I would like to validate the token before the state is being loaded, so if the token is not valid then the partial HTML won't be downloaded or whatever.
I have read that UI-Router has a resolve property that can be used to get data before the view is loaded... can I use the resolve to validate the Token?
Hope someone can give me a guide or advice.
Thanks a lot.
Your build stack should compile production JS with template embedded, so there
is no round trip to worry about.
If you really want to intercept the initial page load, experiment with
$locationChangeSuccess, which is fired before the first $routeChangeStart.
If you want to hook into resolve, just attach a promise to it.

Resources