AngularJS local storage service wrong return - angularjs

I have this service in my app. Service to set and get user in localstorage. But the getUser function was returning the JSON.parse function not my object.
Anybody know how to resolve this insue?
The code is here:
.service('UserService', function() {
// For the purpose of this example I will store user data on ionic local storage but you should save it on a database
var setUser = function(user_data) {
window.localStorage.extract = JSON.stringify(user_data);
};
var getUser = function(){
console.log(JSON.parse(window.localStorage.user || '{}'));
return JSON.parse(window.localStorage.user || '{}');
};
var clearUser = function(){
delete window.localStorage.user;
};
return {
getUser: getUser,
setUser: setUser,
clearUser: clearUser
};
})
Thanks

I use the localStorage.get/set item methods like below. So your service would look like
.service('UserService', function() {
var getUser = function(){
return JSON.parse(window.localStorage.getItem('user'));
};
var setUser = function(user){
JSON.stringify(window.localStorage.setItem('user', user));
};
return {
getUser: getUser,
setUser: setUser
};
})

Related

How to set state when using AWS Cognito userhandler function

I am working on the React project which require authorization via AWS Cognito. By following the documentation, I can get the ID token successfully. However I don't know how to update the state in the onsuccess callback, here is my code. Appreciate your help.
class A extends Component{
constructor(){
super();
this.state = {
auth: "";
token: "";
login:false;
}
}
}
hanleSignIn = ()=>{
let auth = this.initCognitoSDK();
let curUrl = window.location.href;
auth.parseCognitoWebResponse(curUrl);
auth.getSession();
}
initCognitoSDK=()=>{
let authData = {
ClientId: // client id
AppWebDomain: // "https://" part.
TokenScopesArray: // like ['openid','email','phone']...
RedirectUriSignIn: 'http://localhost:3000',
RedirectUriSignOut: 'http://localhost:3000',
IdentityProvider: **,
UserPoolId: **,
AdvancedSecurityDataCollectionFlag: false
};
let auth = new CognitoAuth(authData);
auth.userhandler = {
onSuccess: function (result) {
if(result){
let idToken = result.getIdToken().getJwtToken();
//here I want to update the following state once get token successfully
// However I can't reach this.setState in the callback function
this.setState({
auth: auth,
token: idToken,
login: true
})
}
},
onFailure: function (err) {
console.log("Error!" + err);
}
};
auth.useCodeGrantFlow();
return auth;
}
You are using a regular function for your callback which means when the callback is called, the context of this is auth.userhandler which does not have a setState property. As with other parts of your code, you should use the arrow function notation so that it uses the lexical this which is your component instance:
auth.userhandler = {
onSuccess: (result) => {
...
this.setState({ ... });
}
};

React Redux Firebase Keep user logged in function

I added my login and logout functions and its working properly, but I don't know how to keep users logged in? Any quick advices? Thanks. This is my login logout actions:
export function logout_action() {
return dispatch => {
firebase.auth().signOut()
.then(function () {
const logged_value = null;
dispatch(login({
...logged_value
}));
}).catch(function (error) {
// An error happened.
});
}
}
export function login_action() {
return dispatch => {
var provider = new firebase.auth.GoogleAuthProvider();
firebase.auth().signInWithPopup(provider).then(function (result) {
const logged_value = result.user;
dispatch(login({
...logged_value
}));
}).catch(function (error) {
var errorCode = error.code;
});
}
}
You can save the result either on the redux store or save it in window.localstorage when the user logins in and when they logout you can delete the result from window.localstorage or the redux store.

Receiving Resource object instead of value using Node and Angular

I am trying to get the value returned through an authorization getCurrentUser function. When calling the function, I get this resource holding a Promise. I can see the _id value in there! How do I access it?
Thank you in advance!
Console Output
Here's a bit of the Auth service where getCurrentUser is defined:
(function config() {
function AuthService($location, $http, $cookies, $q, appConfig, Util, User) {
const safeCb = Util.safeCb;
let currentUser = {};
let Auth = {};
const LOCAL_HOST = 'localhost';
if ($cookies.get('token') && $location.path() !== '/logout') {
currentUser = User.get();
}
Auth = {
getCurrentUser(callback) {
if (arguments.length === 0) {
return currentUser;
}
const value = currentUser.hasOwnProperty('$promise') ? currentUser.$promise : currentUser;
return $q.when(value).then(user => {
safeCb(callback)(user);
return user;
}, () => {
safeCb(callback)({});
return {};
});
}
...
Here is where I call it:
angular.module('toroApp').directive('csvreaderDirective', ['$http', function ($http, Auth) {
return {
controller(Auth) {
const currentUser = Auth.getCurrentUser();
console.log(currentUser);
},
compile(element, Auth) {
...
Auth.getCurrentUser() is returning a $q promise so you need to use the promise API to get the result.
controller(Auth) {
Auth.getCurrentUser().then(function (user) {
const currentUser = user;
console.log(currentUser);
});
}

firebase auth delayed on refresh

Hard refreshes on my SPA React/Firebase application does not maintain auth state on immediate execution of a function. I have a workaround, but it's sketchy.
My react routes utilize the onEnter function to determine whether or not the user is authenticated or not. For instance
<Route path="/secure" component={Dashboard} onEnter={requireAuth}/>
Furthermore, my requireAuth function looks like this:
function (nextState, replace) {
console.log('requireAuth', firebase.auth().currentUser);
if (!firebase.auth().currentUser) {
console.log('attempting to access a secure route. please login first.');
replace({
pathname: '/login',
state: { nextPathname: nextState.location.pathname }
});
}
};
However, on a hard refresh there is a slight delay on firebase.auth().currentUser. It's null at first, then executes a POST to firebase servers in order to determine auth state. When it returns the currentUser object is populated. This delay causes issues though.
My hacky solution is the following: update: this doesn't actually work...
function (nextState, replace) {
setTimeout(function () {
console.log('requireAuth', firebase.auth().currentUser);
if (!firebase.auth().currentUser) {
console.log('attempting to access a secure route. please login first.');
replace({
pathname: '/login',
state: { nextPathname: nextState.location.pathname }
});
}
}, 50);
};
Simply wrap it in a timeout. However, I really don't like this... any thoughts?
Update:
I have also tried to wrap it within a onAuthStateChanged listener, which should be more accurate than a setTimeout with a definitive time delay. Code as follows:
function (nextState, replace) {
var unsubscribe = firebase.auth().onAuthStateChanged(function (user) {
if (!user) {
console.log('attempting to access a secure route');
replace({
pathname: '/login',
state: { nextPathname: nextState.location.pathname }
})
console.log('should have called replace');
}
unsubscribe();
});
// setTimeout(function () {
// console.log('requireAuth', firebase.auth().currentUser);
// if (!firebase.auth().currentUser) {
// console.log('attempting to access a secure route. please login first.');
// replace({
// pathname: '/login',
// state: { nextPathname: nextState.location.pathname }
// });
// }
// }, 50);
};
The two log statements are executed, but react-router replace does not seem to be executed correctly. Perhaps that's a different question for the react-router experts.
update 2:
It was late at night when I was working on this. Apparently setTimeout doesn't actually work either.
Okay. So, I was able to solve this by utilizing the localStorage variable that firebase provides to store the user information.
function (nextState, replace) {
if (!firebase.auth().currentUser) {
let hasLocalStorageUser = false;
for (let key in localStorage) {
if (key.startsWith("firebase:authUser:")) {
hasLocalStorageUser = true;
}
}
if (!hasLocalStorageUser) {
console.log('Attempting to access a secure route. Please authenticate first.');
replace({
pathname: '/login',
state: { nextPathname: nextState.location.pathname }
});
}
}
};
While this is a post related to ReactJS, I recently came across the same problem when writing my own authentication/authorisation service for AngularJS. On page refresh the onAuthStateChanged passes a user that is null because firebase is still initializing (asynchronously).
The only solution that worked for me was storing the users uid in localStorage after the user has logged in and deleting the value after the user has logged out.
Since i'm using a authService and userService seperately I registered a listener in the authService that is fired once the user is logged in/out.
Code sample authService (not the full authService):
var loginListeners = [];
var logoutListeners = [];
function addLoginListener(func) {
loginListeners.push(func);
}
function addLogoutListener(func) {
logoutListeners.push(func);
}
function login(email, password) {
return firebase.auth().signInWithEmailAndPassword(email, password).then(function(user) {
for(var i = 0; i < loginListeners.length; i++) {
loginListeners[i](user); // call registered listeners for login
}
});
}
function logout() {
return firebase.auth().signOut().then(function() {
for(var i = 0; i < logoutListeners.length; i++) {
logoutListeners[i](); // call registered listeners for logout
}
});
}
Code sample userService (not the full userService):
.provider('userService', ['authServiceProvider',
function UserService(authServiceProvider) {
var usersRefUrl = '/users';
var userInfo = null;
var userDetails = null;
// refreshHack auto-executed when this provider creates the service
var storageId = 'firebase:uid'; // storing uid local because onAuthStateChanged gives null (when async initializing firebase)
(function addRefreshHackListeners() {
authServiceProvider.addLoginListener(function(user) {
userInfo = user;
localStorage.setItem(storageId, user.uid); // store the users uid after login so on refresh we have uid to retreive userDetails
});
authServiceProvider.addLogoutListener(function() {
userInfo = null;
localStorage.removeItem(storageId);
});
firebase.auth().onAuthStateChanged(function(user) {
if(user) { // when not using refreshHack user is null until async initializing is done (and no uid is available).
localStorage.setItem(storageId, user.uid);
userInfo = user;
resolveUserDetails();
} else {
localStorage.removeItem(storageId);
userInfo = null;
userDetails = null;
}
});
})();
function isLoggedIn() {
return userInfo ? userInfo.uid : localStorage.getItem(storageId); // check localStorage for refreshHack
}
function resolveUserDetails() {
var p = null;
var uid = isLoggedIn();
if(uid)
p = firebase.database().ref(usersRefUrl + '/' + uid).once('value').then(function(snapshot) {
userDetails = snapshot.val();
return userDetails;
}).catch(function(error) {
userDetails = null;
});
return p; // resolve by returning a promise or null
}
}]);
And in a run-block you can globally register a user and resolve the user-info/details every route change (makes it more secure):
.run(['$rootScope', 'userService', 'authService',
function($rootScope, userService, authService) {
// make user available to $root in every view
$rootScope.user = userService.getUser();
$rootScope.$on('$routeChangeStart',
function(event, next, current) {
// make sure we can add resolvers for the next route
if(next.$$route) {
if(next.$$route.resolve == null)
next.$$route.resolve = {};
// resolve the current userDetails for every view
var user = userService.resolveUserDetails();
next.$$route.resolve.userDetails = function() {
return user;
}
}
});
}]);
Maybe this can help someone who is struggling the same issue. Besides that feel free to optimize and discuss the code samples.
Works by managing localStorage. Here is example how I do it.
constructor(props) {
super(props);
let authUser = null;
// setting auth from localstorage
for (let key in localStorage) {
if (key === storageId) {
authUser = {};
break;
}
}
this.state = {authUser};
}
componentDidMount() {
firebase
.auth
.onAuthStateChanged(authUser => {
if (authUser) {
localStorage.setItem(storageId, authUser.uid);
} else {
localStorage.removeItem(storageId);
}
// change state depending on listener
authUser
? this.setState({authUser})
: this.setState({authUser: null});
});
}
This worked for me try to wrap your main app in an if else statement depending on the state of firebase.auth .
constructor() {
super();
this.state={
user:{},
stateChanged:false
};
}
componentDidMount(){
fire.auth().onAuthStateChanged((user)=>{
this.setState({stateChanged:true})
});
}
render() {
if(this.state.stateChanged==false){
return(
<div>
Loading
</div>
)
}
else{
return(<div>your code goes here...</div> )
}
}

Mocha -Parameter in mocked data

I'm mocking a function called isLoggedIn():
auth = {
isLoggedIn: function () {
return true;
}
};
and apply is in a beforeEach loop
beforeEach(function () {
angular.mock.module(function ($provide) {
$provide.value('Auth', auth);
});
});
At the moment isLoggedIn() will always return true. This function needs to be able to return false for some particular tests.
Is there a way to pass a variable into the mock from my tests.
E.g. something like
var loggedIn = ;
auth = {
isLoggedIn: function () {
return loggedIn;
}
};
Yes, you can for instance put it inside your test:
var loggedIn;
beforeEach(function () {
auth = {
isLoggedIn: function () {
return loggedIn;
}
};
angular.mock.module(function ($provide) {
$provide.value('Auth', auth);
});
});
It is also possible to have it as a separate file, but for this example it seems overkill.

Resources