How to get logged user with Angular? - angularjs

Maybe I am missing something very trivial, but I can't find an answer.
I am implementing a webapp based on nodejs, express and angular for the client side.
I handle my signup and sessions through passport.js. Therefore, server-side, I can access the logged user through request.user.
Now, I have a logged user which goes on its profile page, displayed through an angular view.
Here is the issue: In order to show them user information now I am thinking to send an $http request to the server, which takes the user from request and sends it back to the client where it is displayed. However, this sounds weird to me.
So here is my question: Is there any way to access the user in the session thruogh angular? If so, what user information is actually stored in the client?
Thanks in advance, and apologies if this is something too trivial to be asked:/

You will need to create a service in Angular that requests the current user, and stores the result so you can retrieve it for use in your controllers. There is nothing built in to Angular for this; you will have to create it your self. However, it's fairly straight forward.
myapp // assume was previously defined using var myapp = angular.module('myapp', []);
.factory('principal', ['$http', '$q', function($http, $q) {
var _identity = undefined;
return {
// this function returns the current _identity if defined; otherwise, it retrieves it from the HTTP endpoint
identity: function(setIdentity) {
if (setIdentity) {
_identity = setIdentity;
return;
}
var deferred = $q.defer();
if (angular.isDefined(_identity)) {
deferred.resolve(_identity);
return deferred.promise;
}
$http.get('/path/to/endpoint')
.success(function(result) {
_identity = result;
deferred.resolve(_identity);
})
.error(function() {
_identity = undefined;
deferred.reject();
});
return deferred.promise;
}
};
}]);
This principal service has one method at the moment, identity(). This method returns a promise. If the identity has already been retrieved, it will resolve with that value immediately. If not, it will attempt to get it from an HTTP endpoint. If the HTTP call succeeds, it will memoize the result to _identity and resolve the promise. If the call fails, the promise will be rejected. identity has a secondary use. If you supply it with a single parameter, it will set that value as the identity and return nothing. This is useful if you already have the identity and want to set it right away, for example, after they successfully sign-in.
You could manage the sign-in page like this:
myapp.controller('SignInCtrl', ['$scope', 'principal', '$http', function($scope, principal, $http) {
// these two values will be bound to textboxes in the view
$scope.username = '';
$scope.password = '';
// this function will be called when the sign in form is submitted
$scope.submit = function() {
$http.post('/path/to/signin', {username: $scope.username, password: $scope.password })
.success(function(identity) {
// assumes /path/to/signin returns a JSON representation of the signed-in user upon successful authentication
// presumably the endpoint also sets a cookie representing an auth token or something of that nature. the browser will store this automatically for you
principal.identity(identity); // set the identity immediately
// do whatever else on successful login, like redirect to another route
});
}
}]);
And a controller somewhere else that needs the current identity could do something like this:
myapp.controller('MyCtrl', ['$scope', 'principal', function($scope, principal) {
// retrieve the identity. when it resolves, set user on the scope
principal.identity().then(function(identity) {
// after this, you can use user in your view or later in your controller
$scope.user = identity;
});
}]);
Now you have a way of storing the identity immediately after sign-in. I do make an assumption that your code that signs the user in sets a cookie to represent an auth token or whatever in your signin endpoint. The good thing about this is that if the user refreshes their browser, or the cookie is stored for a period of time, the user can just visit your app and the identity will resolve it using that token cookie automatically.
This plunk is a working demo of a more elaborate set up. Some of it may not apply to you (for example, it uses ui-router instead of regular routing), but it should be a reasonable reference point.

Related

How to set a value in responseError so that the request can use it for every request in AngularJS interceptor

I have a question on AngularJS interceptor.
I have 2 sets of users: legacy user and new user.
For every legacy user, to continue they have to reset their password first. That means they have to be authenticated first and this API sends a status code of 420 and then the user is redirected to Password reset page. And until the user resets his password, no other API calls are honored.
For this I'm using AngularJS interceptor.
In the responseError function I check if the status code I receive is 420. If it is, I set a variable in $rootScope so that all the subsequent requests can be suppressed based on this flag in the request function of the interceptor. But this value is always undefined after the redirect.
I also tried creating a new Service and injecting it in the interceptor and calling the service method to set a variable from responseError. Then, when I read the variable from request function it was always undefined after the redirect. So, no luck here either.
When the request method is executed the user would have been redirected to a new page. It shouldn't have reset the value in either $rootScope or in the Service, am I right?
What am I missing here? Can someone point me in the right direction here?
angular.module('app')
.factory('httpInterceptor', ["$window", "$q", "$rootScope", "$injector", function ($window, $q, $rootScope, $injector) {
return {
request: function (config) {
// Read the value from the $rootScope. If it's true, cancel all the subsequent requests
var redirected = $rootScope.app.redirected;
console.log("request: redirected: " + redirected); // The value is always 'undefined' here
var canceller = $q.defer();
if (redirected) {
// Canceling request
canceller.resolve();
}
return config;
},
responseError: function (response) {
var statusCode = response.status;
var newLocation = response.headers('location');
if (statusCode === 420 && newLocation) { // Status code is 420. Redirect and set a flag in the $rootScope
$rootScope.app.redirected = true;
$window.location.href = newLocation;
}
return response || $q.when(response);
}
}
}])
.config(['$httpProvider', function($httpProvider) {
$httpProvider.interceptors.push('httpInterceptor');
}]);
Finally, my efforts in solving this made me more knowledgeable today than I was yesterday. And, it feels so good to answer your own question :)
So, it looks like when used $window.location.href, it resets the $rootScope variable. As a fix, I replaced this line of code with $location.path(newLocation)
I switched back to using Service and I can see the value and it's not undefined

How can I get specific value from $http.get in AngularJS

I have a simple question. When I use $http.get in angular controller, how can I use a specific value from the results? Let's say that I am getting username and password and I want to compare each one individually.
app.controller("loginController", function ($scope, $http) {
$scope.submit = function () {
$http.get("../Views/userAuthentecation.aspx")
.then(function (response) {
$scope.members = response.data;
});
}
});
From the above controller, I am using the userAuthentecation.aspx to read from database in the behind code and just display the results as json format. So, the $scope.members is actually a json format data contains username and password.
Well generally we assign an entire response to a single object like you did
$scope.members = response.data;
but lets say if you have 3 objects in response.data and you want them all to be in the different objects than you can simply assign them to different objects like below
$scope.id = response.data.id;
$scope.username = response.data.username;
$scope.members = response.data.password;
once the data is assigned to $scope.objects than you can do with it whatever you like but Since you have authentication code in your controller i suppose you are trying to make an authentication system. well in that case once the login form is filled its been send to a function in controller for authentication for example
<form name="form" ng-submit="login()" role="form">
once the controller gets the request you can either process it in the same function or you can send it to authentication service which is usually a factory to perform a specific task in this case it will check the user crediantials.
$scope.login = function () {
$scope.dataLoading = true;
AuthenticationService.Login($scope.username, $scope.password, function(response) {
if(response.success) {
AuthenticationService.SetCredentials($scope.username, $scope.password);
$location.path('/');
} else {
$scope.error = response.message;
$scope.dataLoading = false;
}
});
};
the above function is calling another factory for authenticating the user credentials, the factory AuthenticationService will be called which will pass the parameters to Login() function. function within the factory can be called like this
AuthenticationService.login()
once the credentials are checked and verfied and response code is 200 which means ok then entered creditals will be passed to
AuthenticationService.SetCredentials($scope.username, $scope.password);
which will generated encrypted cookie
hope this will give a little understand about authentication and comparing the response data, the entire workig authentication example can be found here

Use of AngularJS Factory for Authentication

New to AngularJS.. I want to build a simple Logon screen using a logon page, utilising a controller (I call it 'AccessCtrl') and service.
I want the controller to make use of a service (I called it 'AuthService'), which will perform the REST call that verifies the username/password, and, will also contain information about the success or failure of that logon attempt.
I want to receive information about the last login attempt (as returned in by the REST call). For now - just a string which I want to show on the logon screen, (eg 'password invalid' or 'account expired', or 'welcome'). I'm making this a property of the AuthService service which I hope to display.
My view, has a form with username, password and a submit button that calls the controller's login() method. I'm not including it here for brevity. I don't think thats where the problem lies.
To start with, I want to capture when the server is down/unavailable, etc, and also report this, using the same service. To start with - all calls will fail,(because I have an invalid url).
The controller is defined like this:
angular.module('loginApp')
.controller('AccessCtrl', ['$scope','$http','AuthService',
function ($scope,$http,AuthService,Config) {
this.login =function() {
var user={user:this.username,password:this.password,otherCredentials:this.otherCredentials} ;
AuthService.login(user);
this.message=AuthService.message;
};
}]);
My factory service is defined as follows:
.factory('AuthService', ['$http', function ($http) {
var authService = {};
var apiRootUrl="http://somesite.com/urany";
authService.login = function (credentials) {
authService.message="";
$http.post(apiRootUrl+'/login', credentials)
.then(
function (response) {
// populate OK message
authService.message="Welcome !";
},
function (response) {
// Currently - all calls fail, as the URI is invalid
// I want to trap this.
authService.message = "Server unavailable with response status = " + response.status ;
return authService;
},
function(response) {
// this is the notify callback function.
//
});
};
authService.isAuthenticated = function () {
// rerurn true or false if previously authenticated
return true;
};
return authService;
}])
;
The problem is that my invalid message ('Server unavailable with response status...') does not appear in my view.
In stepping through the code, the correct authService function is called, but, this does not seem to be populated into the controller/view.
I have a feeling this is a timing issue, where the controller that is executing
this.message=AuthService.message;
before the call actually comes back from the post - but I'm not sure that is the case or how to fix that.
any help/clues welcome.
thanks.
S
Have you tried something like this?
Angular Service Code:
//call the service
AuthService.login(user).then(function(response){//begin call back
//store the response data message object property returned from the service
$scope.message = response.message;
}//end call back

AngularJs watch for change in variable inside a service

I have a service named loginManager which stores objects called is_logged_in & api_token along with few others. My various controllers make ajax calls using $http using the api_token.
If the api_token is reset/expired on server, response is sent as auth_error, at this point I set is_logged_in = false
What i want to achieve is, whenever is_logged_in is changed, the service redirects to /login using $location.path('/login'), i.e. to say, I want to watch the object inside the service, and invoke callback on change from service itself.
I just want the service to take care of login and corresponding routing, without any controller worrying about weather user is logged in or not.
I believe Pankaj Pakar's answer could work but you should use angular's interceptors for that. They intercept all messages. You could add hook for response or responseError and when you recieve auth_error you do any action you like. For example $location.path('/login'), display error to user, etc.
If you want to separate logic you could inject your service with all code inside and just call some method on it.
I'd suggest you to put that watcher in run phase on the angular application which will be there at a single place, by which you could check the value is_logged_in flag of service & if user is not login then redirect him/her to login page directily.
Code
app.run(function($rootScope, loginManager, $location){
$rootScope.$watch(function(){
return loginManager.is_logged_in;
}, function(newValue){
if(angular.isDefine(newValue) && !newValue)
$location.path('/login');
//$state.go('login'); //if you are using ui.router
})
})
Edit
Really curious part of your question is, from where you are changing is_logged_in flag of your service as #JBNizet asked? If any code is there is JavaScript then you should directly redirect to login page from there.
I feel the need to answer something more, Mior is quite right, but his answer needs more meat.
Here I show you how I managed to handle ALL server XHR requests with response 401 unauthorized.
First of all you need a service:
'use strict';
angular.module('theModule')
.factory('interceptorService', ['$q', '$location', function ($q, $location) {
return {
response: function (response) {
return response || $q.when(response);
},
responseError: function (rejection) {
var returnTo = $location.path().replace(/^\/|\/$/g, '');
if (returnTo === 'login') {
return;
}
if (rejection.status === 401) {
console.log('Unauthorized');
$location.path('/login').search('returnTo', returnTo);
}
return $q.reject(rejection);
}
};
}]);
This will be used to intercept all XHR calls and to change the location every time a 401 error is found.
I've also added an improvement that is the "returnTo" parameter, you will be able to use it after login to return to the previous page.
To bind it to each request you have to call the config method, this is my main javascript.
'use strict';
/**
* #author Gianmarco Laggia
*
* Main module of the application an configurations.
*/
angular
.module('theModule', [])
.config(['$httpProvider', function ($httpProvider) {
//Http Interceptor to check auth failures for xhr requests
$httpProvider.interceptors.push('interceptorService');
}]);
This is pretty much what you need to intercept every request, working on the rejection.status you can also intercept events such as server down (status is -1), internal server error (500+), success status (in the response part, status 200+) etc.

Change an existing $http request interceptor

In my angular app, I have a $http request interceptor called 'authInterceptor' that I create like this:
.factory('authInterceptor', function ($q, $window, EXT_API_BASE, $injector) {
return {
request: function (config) {
if (config.url.indexOf(EXT_API_BASE) !== -1){
var Auth = $injector.get('Auth');
if(Auth.user && Auth.user.token){
config.headers.Authorization = 'Web ' + Auth.user.token;
}
}
return config;
}
}});
It gets registered in a .config():
$httpProvider.interceptors.push('authInterceptor');
As you see, my Authorization headers are bound to a Auth.user.token value. This value is available when my user is signed in.
Then the headers are sent for any calls to my api.
The problem I am facing is... when the user signs out in my angular app, the Authorization headers are still being sent even though I deleted Auth.user.token already.
On a hard refresh of the page, the Authorization headers then get removed completely.
How can I make sure 'authInterceptor' registers the change in token value when my user signs out?
Answering my own question. Made a newbie mistake of changing Auth.user object like this on sigh out:
Auth.user = {}
That created a new object and the requestInterceptor was still referencing the old object.
The correct way to do it is:
delete Auth.user.token

Resources