AngularJS: Access connected user info from controller - angularjs

I'm struggling with AngularJS and access to connected user info here. My goal is to save the info of the connected user in a Service, so that is available throughout the app. So this is how I do it:
The login function is exposed through a Login service, and its code is the following:
(function(){
var $loginService = angular.module("RockMyTask.LoginService", [
"satellizer",
"RockMyTask.ApiService"
]);
/**
* Login service
*
* function login(identifier, password, after)
* Logs the given user. Global data about the user are registered in the root scope.
* #param identifier The user's identifier (either email or password)
* #param password The user's password
* #param success (optional) A callback to execute when the user is successfully logged in. This callback is passed
* the response object.
* #param failure (optional) A callback to execute when the log in fails. This callback is passed the response
* object.
*
* function restore()
* If the user was already logged in, restore the user data in the rootScope.
*
* function connectedUser()
* Return the data of the currently connected user. If the user is not connected an empty object is returned.
* If the user data hasn't be fetched yet, then an empty object is returned and this object will be filled with
* the user data as soon as it is received.
*/
$loginService.factory("Login", ["$auth", "$rootScope", "User",
function($auth, $rootScope, User) {
var obj = {};
var connectedUser = {};
obj.logout = function() {
$auth.logout();
//Authorization.clear();
connectedUser = null;
};
obj.login = function(identifier, password, success, failure) {
var credentials = {
user_identifier: identifier,
password: password
};
$auth.login(credentials).then(function(auth_response) {
// fetch user data (with both spectator and rockstar flags set to true)
User.me(true, true).then(function(response) {
connectedUser = response.data;
//TODO add check for DEBUG_MODE
console.log("Received connected user data and stored it in connectedUser var");
}, function() {
connectedUser = {};
});
// execute provided success callback
if (_.isFunction(success)) {
success(auth_response);
}
}, function(response) {
connectedUser = null;
// execute provided failure callback
if (_.isFunction(failure)) {
failure(response);
}
});
};
obj.restore = function() {
if ($auth.isAuthenticated()) {
User.me(true, true).then(function(response) {
connectedUser = response.data;
}, function() {
connectedUser = null;
});
} else {
connectedUser = null;
}
};
obj.getConnectedUser = function(){
if($auth.isAuthenticated && !connectedUser){
User.me(true, true).then(function(response) {
connectedUser = response.data;
}, function() {
connectedUser = null;
});
}
return connectedUser;
};
obj.updateUserInfo = function(user){
connectedUser = user;
};
obj.isConnectedUserRockstar = function(){
return connectedUser.rocker != null;
};
obj.isConnectedUserSpectator = function(){
return connectedUser.spectator != null;
};
return obj;
}]);
})();
As you may observe in the code, upon successful login, I launch an HTTP request (User.me(true, ture)) which then stores the returned data (representing info about connected user) in an ad hoc variable of the service, connectedUser.
To access the connected user info from any point of the application, I've implemented a function in the Login service called getConnectedUser, whose code is reported here:
obj.getConnectedUser = function(){
if($auth.isAuthenticated && !connectedUser){
User.me(true, true).then(function(response) {
connectedUser = response.data;
}, function() {
connectedUser = null;
});
}
return connectedUser;
};
This function checks if the user is indeed authenticated and if the variable connectedUser has been already correctly populated and if not it triggers again the HTTP function to retrieve the data (otherwise it simply returns the connectedUser var).
Now, my problem is the following. The page I redirect the user to, after successful login, uses a controller, to which I've passed the Login service. The controller tries to retrieve the connected user info but the result is a null pointer. What am I doing wrong??
The code in the controller (just in case even if very simple):
$scope.user = Login.getConnectedUser();
EDIT 1
Looking at the console in the browser I see that the HTTP request has success and the returned data is:
{"id":3,"email":"iCartwright#hotmail.com","name":"Michele","surname":"Imperiali","locale":"it_IT","picture":"https:\/\/storage.rockmytask.it\/users\/profile_image\/3.jpeg","birthday":"1982-06-23","gender":"M","country":"IT","province":"Lombardia","city":"Milano","zip":"20100","street":"190 Grover Expressway","mobile_phone":"(437)924-8282","home_phone":"239-250-3166x783","username":"spectator","validated":true,"timezone":"Europe\/Rome","created_at":"2016-04-01T12:50:53+0000","rocker":null,"spectator":{"description":"I need help with my garden !"}}
As previously stated, when I try to access on the variables of $scope.user in the controller I get a null pointer exception.

I think the issue has to do with the
, function() {
connectedUser = null;
}
Is there a reason you are setting connectedUser to null? If the reason is because you want to set it to null if you get an error from the backend or a bad return then doing :
obj.getConnectedUser = function(){
if($auth.isAuthenticated && !connectedUser){
User.me(true, true).then(function(response) {
connectedUser = response.data;
}, function(error) {
connectedUser = null;
});
}
return connectedUser;
};
This then would only set connectUser to null if you get a bad response, As of right now it looks like the connectedUser is always getting set to null and explaining your null pointer.

Related

How to use cookies outside controller?

I've setup cookies inside a Login Controller. Whenever I'm trying to get the values of cookies outside of controller it's throwing an error. please check this out what I'm missing.
Controller After i get success response I'm setting cookies
app.controller('AngularLoginController', ['$scope','$http','$cookies','$rootScope', function($scope, $http,
$cookies,$rootScope) {
$scope.loginForm = function() {
$http.post("login.php", {
'email' :$scope.inputData.email,
'password':$scope.inputData.password
}).success(function(data) {
console.log(data);
if ( data != 'wrong') {
var loggedIn = $cookies.get('loggedIn');
// Putting cookies
$cookies['myCookieArray']= {'loggedIn':true,'username':data};
getmycookiesback = $cookies['myCookieArray'];
window.location.href = '#/userlist';
$rootScope.display = true;
$rootScope.username = getmycookiesback.username;
}
else {
$scope.errorMsg = "Invalid Email and Password";
}
})
}
}]);
After I login it calls AngularLoginController and set Cookies values after success Response.
Outside Controller if I'm comparing value of cookies it's throwing error getmycookiesback is not defined
var onlyLoggedIn = function ($location,$q,$cookies,$rootScope) {
var deferred = $q.defer();
var url = $location.absUrl();
if (getmycookiesback.loggedIn === "undefined") { // Error on This line
deferred.reject();
window.location.href = '#/login';
}
else{
deferred.resolve();
$rootScope.display = true;
$rootScope.username = getmycookiesback.username;
window.location.href = url;
alert(getmycookiesback.username);
//$cookies.remove('loggedIn');
return true;
}
return deferred.promise;
};
Why it's undefined if i have defined it in Controller.
Is there another way i can get value of cookies outside controller ?
Of course it is undefined; it appears to be a local variable in that function scope.
You may want to take a look at Angular Services for things you want to access globally - you may want to implement a function on a service to do this, or store that information locally within the service and provide a function to access it - take a look here.

Apparent race condition getting firebase.User from controller in Firebase 3.x

I'm using Firebase 3.x and the auth state change event to assign the current user in my service -
.service('FireService', function ($q, $http,$state) {
var accessToken;
var user;
firebase.auth().onAuthStateChanged(function (usr) {
if (usr) {
// User signed in!
user = usr;
} else {
user = null;
$state.go('login');
}
});
return {
currentUser: function () {
return user;
}
}
But I need the current user to a reference to my child objects from the database in the controller
var getLog = function () {
var logRange = currentDate.format('MMMYYYY').toUpperCase();
var userId = FireService.currentUser().uid;
var ref = LogService.logs(userid,logRange);
// download the data into a local object
ref.once('value').then(
function (result) {
logObj = result;
})
};
When I set breakpoints, I see that the currentUser() is always empty because the state change hasn't fired yet. I've also tried to assign my userid directly from firebase.User but this is also not yet initialized by the time my controllers are ready. How do I get the currently logged in user so that it's available before I start routing around in my app?
I would recommend using the observer directly within the getLog method, so it waits until the current user is set:
var getLog = function () {
var unsubscribe = firebase.auth().onAuthStateChanged(function(user) {
if (user) {
var logRange = currentDate.format('MMMYYYY').toUpperCase();
var userId = user.uid;
var ref = LogService.logs(userid,logRange);
// download the data into a local object
ref.once('value').then(
function (result) {
logObj = result;
});
}
unsubscribe(); // So this observer is only triggered once.
});
};
As the ref.once('value') resolves asynchronously anyway, it will just add a bit of latency to the method but won't change its behavior.
Using this "one off" observer pattern (an observer that calls its unsubscribe function the first time it is called) allows you to make sure Firebase Auth is fully initialized before executing the code. If Firebase Auth is already initialized by the time you create the observer, then its resolution will be triggered immediately. Remember that observers are always called asynchronously.
If you end up using this pattern often, you could add a method on FireService:
...
ready: function() {
return $q(function(resolve, reject) {
var unsubscribe = firebase.auth().onAuthStateChanged(function(user) {
unsubscribe();
resolve(user);
});
});
}
...
And use it within your getLog method:
var getLog = function () {
FireService.ready().then(function(user) {
if (!user) { return; }
var logRange = currentDate.format('MMMYYYY').toUpperCase();
var userId = user.uid;
var ref = LogService.logs(userid,logRange);
// download the data into a local object
ref.once('value').then(
function (result) {
logObj = result;
});
});
};

MobileFirst: Adapter Authentication calls Submit success repeatedly in Angular App

Environment:
MFPF v7.0
Eclipse: Luna SR.2 (4.4.2)
Windows 7
I face an strange issue. I am using adapter based authentication in one of my Angular based project.
The app authenticates well, but it repeatedly calls the submitSuccess.
I guess, it has something with the way Angular works, either I should use Challenge Handler as a Service or Controller. Because the way MobileFirst detects & handle instances of a/any handler objects. And that cause reference mis-match to dispose off or execute the relevant functions at appropriate time.
Currently I use it as a service.
Below is the challenge handler that I use.
define(['angular'], function(angular){
var loginChallengeHandler = angular.module('webApp.loginChallengeHandler', [])
.service('loginChallengeHandler', function(){
var _this = this;
_this.AuthRealmChallengeHandler = WL.Client.createChallengeHandler("AdapterAuthRealm");
_this.AuthRealmChallengeHandler.isCustomResponse = function(response) {
console.error("AuthRealmChallengeHandler.isCustomResponse:: " , response);
if (!response || !response.responseJSON || response.responseText === null) {
return false;
}
if (typeof(response.responseJSON.authRequired) !== 'undefined' || response.responseJSON.authRequired == true){
return true;
} else {
return false;
}
};
_this.AuthRealmChallengeHandler.handleChallenge = function(response){
console.error("AuthRealmChallengeHandler.handleChallenge:: " , response);
var authRequired = response.responseJSON.authRequired;
if (authRequired == true){
console.error("------Auth Required----- ");
_authenticationFailed(response);
} else if (authRequired == false){
console.error("------Auth PASSED ----- ");
//Now tell WL Authentication that user has been verified successfully so that it finishes the authentication process
_this.AuthRealmChallengeHandler.submitSuccess();
console.error("------ submitSuccess ----- ");
}
};
_this.AuthRealmChallengeHandler.userLogin = function(dataObjRef) {
var loginStatePromise = $q.defer();
_this.AuthRealmChallengeHandler.submitAdapterAuthentication(options,{
onFailure: function (error) {
loginStatePromise.resolve({ state:false , val: "" });
console.log("submitAdapterAuthentication Failed called ", error);
},
onSuccess: function(response) {
console.log("-> submitAdapterAuthentication onSuccess called " , response);
loginStatePromise.resolve({ state: _state , val: _msg });
},
timeout: 30000
});
return loginStatePromise.promise;
};
_this.AuthRealmChallengeHandler.logout = function (){
var userLogoutPromise = $q.defer();
WL.Client.logout("AdapterAuthRealm",{
onSuccess: function(){
console.log("onSuccess");
userLogoutPromise.resolve(true);
},
onFailure: function(){
console.log("onFailure");
userLogoutPromise.resolve(false);
},
timeout: 30000
});
return userLogoutPromise.promise;
};
var _authenticationFailed = function(response){
console.error("_authenticationFailed:: " , response);
//register failure request
_this.AuthRealmChallengeHandler.submitFailure();
};
});
return loginChallengeHandler;
});
I have also tried to bind the handler object with window object, so that it can access the handler's instance methods correctly.
Like:
window.AuthRealmChallengeHandler = WL.Client.createChallengeHandler("AdapterAuthRealm");
window.AuthRealmChallengeHandler.isCustomResponse = function(response) {
.
.
But still same issue.
I solved this issue and here is my solution for anyone facing similar issue in future.
Solution Description (few words)
As per my understanding, the IBM MobileFirst is expecting only one challenge-handler instance (the object that is create via createChallengeHandler function) to exists in the app. So most probably it assumes that the instance would be hooked into the window object.
Now based on this knowledge, we can see that above code is not working even we made instance through service ( i.e. singleton per angular app). Why ? Because, now the handler object becomes accessible via another reference, and this caused issues in resolving the handler references within the WL APIs.
So I just changed a bit of code (hooked it into window) so that WL APIs could reach the correct handler instance and clean-up the requests poll before marking the call successful and dispose off all the cached requests.
One more thing I would suggest.
Create only one handler instance in your client code
Create it as a service or factory - both are singletons in angularjs
Avoid using controllers, because there can be many controller instances within the angular app and it would lead to multiple handler references
And importantly trust IBM MobileFirst :)
Working Challenge Handler as Service
define(['angular'], function(angular){
'use strict';
var loginChallengeHandler = angular.module('webApp.loginChallengeHandler', [])
.service('loginChallengeHandler', function(){
//NOTE:- Below Must be bind with Window Object, otherwise will NOT work as per challenge handlers default behavior
window.AuthRealmChallengeHandler = WL.Client.createChallengeHandler("AdapterAuthRealm");
AuthRealmChallengeHandler.isCustomResponse = function(response) {
if (response && response.responseJSON && typeof (response.responseJSON.authStatus) === "string"){
return true;
} else {
return false;
}
};
AuthRealmChallengeHandler.handleChallenge = function(response){
var authStatus = response.responseJSON.authStatus;
if (authStatus === "failed"){
console.error("------Auth Required----- ");
_authenticationFailed(response);
} else if (authStatus === "passed"){
console.error("------Auth PASSED ----- ");
//do something here like change page etc.
//Now tell WL Authentication that user has been verified successfully so that it finishes the authentication process
AuthRealmChallengeHandler.submitSuccess();
}
};
AuthRealmChallengeHandler.userLogin = function(dataObjRef) {
var loginStatePromise = $q.defer();
AuthRealmChallengeHandler.submitAdapterAuthentication(options,{
onFailure: function (error) {
loginStatePromise.resolve({ state:false , val: "" });
},
onSuccess: function(response) {
loginStatePromise.resolve({ state: _state , val: _msg });
},
timeout: 30000
});
return loginStatePromise.promise;
};
AuthRealmChallengeHandler.logout = function (){
var userLogoutPromise = $q.defer();
WL.Client.logout("AdapterAuthRealm",{
onSuccess: function(){
//$state.go("home.login");
userLogoutPromise.resolve(true);
},
onFailure: function(){
userLogoutPromise.resolve(false);
},
timeout: 30000
});
return userLogoutPromise.promise;
};
var _authenticationFailed = function(response){
//register failure request
AuthRealmChallengeHandler.submitFailure();
};
});//end of service
return loginChallengeHandler;
});
Adapter
function onAuthRequired(headers, errorMessage){
errorMessage = errorMessage ? errorMessage : null;
return {
authStatus: "failed",
errorMessage: errorMessage
};
}
function Login(request){
if(request){
/* IF user credentials are Verified Correctly
* and user is authenticated then create User Identity
* and return success message if it is required by client app.
*/
userIdentity = {
userId: "abc",
displayName: "ABc",
attributes: {}
};
WL.Server.setActiveUser("AdapterAuthRealm", userIdentity);
WL.Logger.error("Auth Successful:");
return {
authStatus: "passed",
submitResponse: "send a Success message in case is required on client-side"
};
}else{
return onAuthRequired(null, "send an error message if required on client side");
}
}
I faced the same issue with adapter based authentication but I was using pure javascript, so no angular. From that I can tell you it's a MobileFirst issue and nothing related to angular.
This might sound contradictory to the documentations but don't call the submitSuccess function, just call your code on successful authentication. It will work fine and authenticate properly.
Also, make sure that you only have the security test set on the specific functions that you use after auth and not on the auth function itself.
Your code seems fine to me but I'm not that good in angular.

Angularfire current logged in user's data

I am using Angularfire for my login system and trying to get current logged in user's username for purpose of using in controller. Firts getting uid from $getAuth and sync it with data in database. But what I am trying below is not working.
.factory("Auth", function($firebaseAuth) {
var ref = new Firebase("URL");
return $firebaseAuth(ref);
})
.factory("Konusma", function($firebaseObject) { //When you give user's uid to this factory its gives you user data in database.
return function(xm) {
var ref = new Firebase("URL");
var userdata = ref.child('users').child(xm);
return $firebaseObject(userdata);
}})
.controller("chatCtrl", function(Auth, Konusma) {
var userx = Auth.$getAuth(); //Current logged in user's uid and email
var chatci = function() { //Trying to get current logged in user's username and other data according to uid which is comes from Auth.$getAuth.
Konusma(userx.uid).$loaded().then(function(data){
return data.username;
});
};
As mentioned in comments -
You are trying to get the data before user is authenticated.
So in your controller , instead of :
var userx = Auth.$getAuth();
you can use this :
var userx = {};
Auth.$onAuth(function(authData) {
var userx = authData;
// or bind to scope -
// $scope.authData = authData;
});
this will also update your authData variable every time auth status is change.
Anyway - you still need to call: currentUser = chatci() only after user is loggedIn ,
and not evrey time that auth is change.
The best way is to use it with router, see here : https://www.firebase.com/docs/web/libraries/angular/guide/user-auth.html#section-routers
So the controller is loaded only after auth is success.
But for your example - you can add this to your controller:
Auth.$waitForAuth().then(function(authData){
if (authData){
currentUser = chatci()
}
});

Nested Service promise not resolved in then()

I got a service which first need to retrieve an accesstoken and then a User object. When this is done, further UI actions needs to be taken. The problem I have is that login().then() is invoked with a valid Session.accesstoken but a non-resolved promise for the Session.user. So I can't do any logic based on the user's info (e.g. role, etc.)
The following code I have:
In controllers.js (login() is invoked upon form credentials button click)
.controller('LoginController',
function($scope, SessionService) {
$scope.credentials = {
username : '',
password : ''
};
$scope.login =
function() {
SessionService.login($scope.credentials).then(function() {
console.info("succesfully logged in as user " + JSON.stringify(Session));
// further UI actions..
});
};
})
In services.js
.factory('SessionService',
function(User, Session, AuthorizationService) {
return {
login : function(credentials) {
return AuthorizationService.requestToken(credentials)
.then(function(token) {
console.info("Got token: " + token);
if (!!token) {
Session.create(token);
console.info("Fetching user...");
return User.get({
id : 'me'
});
} else {
throw (new Error("Could not log in"));
}
}).then(function(user) {
console.info("Got user: " + user);
if (!!user) {
Session.user = user;
} else {
throw (new Error("Could not fetch user"));
}
});
},
};
})
Here is the output of the console:
Got token: cf9c0eba82d872508f7dcc0b234f0d52 services.js:120
Fetching user...
services.js:124 Got user: [object Object]
services.js:132 succesfully logged in as user
{"token":"cf9c0eba82d772508f7dcc0b234f0d52","user":{"$promise":{},"$resolved":false}}
The User object is created by $resource and is not a promise; it is the actual object that will eventually be populated with the user info, but at this point of the code it contains nothing.
The solution is simple: from the first then do:
return User.get({...}).$promise;
The next then will be invoked with the user object as expected, but only when the data is actually fetched.

Resources