cognitoUser.signOut() is not signing user out - javascript

I am using Cognito User Pools, currently in their Beta stage, to handle my user accounts on my website.
In javascript i have the line:
cognitoUser.signOut();
Which should sign my user out, and I think set cognitoUser to null. After the above line I have the following code:
if (cognitoUser != null) {
alert(cognitoUser.getUsername());
}
Much to my annoyance, this line is working, and popping up an alert with the should be signed out user's user name.
Why is the cognitoUser.signOut() line not working?? Do I have to sign the user out on the same page I signed them in on or something?
Thanks

cognitoUser.signOut();
This function clears any locally cached tokens for that user. This function will not set cognitoUser to null. You would still be able to call all functions on this object, however those (such as getUser, changePassword etc) functions which require valid tokens will fail because the user has signed-out (i.e. no valid tokens).

Related

Login function : Error [ERR_HTTP_HEADERS_SENT]

enter image description here
I'm trying to login as a user on my website and it's giving me this error, I can register the user and search for it in the login as many times as I want if the password and username are correct, however when there is an error in the user name or password it happens this in postman and even putting the right password and username it does not show the information and stays like this. Only returning to normal when I restart the server.
enter image description here
I'm a beginner and I'm learning, so I don't know what's wrong, thank you in advance for your attention.
The error generally means that you can not send HTTP headers multiple times.
It occurs when username does not match because in line 24 you send status(401) but then code flow continues, trying to send another status() later.
It occurs when password does not match because in line 32 you send status(401), code flow continues and then in the next line (34) you try to send status(201). The last call might send you to the exception handler (catch ..) in which you also try to send another status(500)
to avoid this, you might try:
if (!user) {
return res.status(401).json('wrong credentials');
}
//... get password hash from DB...
if (password !== req.body.password) {
return res.status(401).json('wrong credentials');
}
return res.status(201).json(user);
Important sidenote:You should NEVER encrypt/decrypt passwords.
Store password hashes instead and only ever compare hashes. Use bcrypt!

Issue when using firebase.auth().onAuthStateChanged accross two pages

I am having a small problem with two html pages which used to work in the past.
First, I have a login page (login.html) containing this kind of code:
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
// User is signed in.
window.open("main.html",'_self');
} else {
// No user is signed in.
prompt for login ....
......
}
});
Second, I also have this page (main.html) containing this sort of code:
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
// User is signed in.
do serious work ....
......
} else {
// Let us double check:
let theUser = firebase.auth().currentUser;
if (!theUser) {
// No user is signed in.
window.open("login.html",'_self');
}
// We need some patience :)
......
}
});
Once a user is logged in, things are supposed to go to main.html; and this is how it used to work. But now, for some unknown reason; when I log in, even though I have provided my credentials, the flow goes to main.html (as it should) but immediately bounces back to login.html.
I have been able to check that the block (in the main.html):
firebase.auth().onAuthStateChanged(function(user) {...}
does not detect me as logged in. Why is that?
P.S.
I have verified that my domain is added in the list of Authorized domains in the Firebase console.
I am encountering the same issue. I narrowed it down to Chrome. It seems to think that Chrome isn't allowed to save cookies when it is (I checked the settings). Might be an issue with a recent Chrome update? Just as a sanity check, try it on Firefox and let me know if that works. It did for me.
UPDATE: This is what I get back when I console log the error
{code: "auth/web-storage-unsupported", message: "This browser is not
supported or 3rd party cookies and data may be disabled.", a: null}
UPDATE 2: Strangely enough, I disabled cookies, tried and it failed. Re-allowed cookies (just as it was initially) and it works now. I would suggest capturing this error and prompting users to ensure they check their browser settings. I'm going to do this in my app.

Firebase initialize app lose Current User

I have a web application using html-js-css and a flask server.
My web app is a multi-pages app which apparently means that I have to Initialize firebase for each page in which i want to use it -.-
The problem is that every time I initialize firebase app, I lose the current user so while in my main page, after log-in, if I write:
const USER = firebase.auth().currentUser;
console.log(USER.uid);
I get my user ID, as soon as I move to another page and repeat the above code, I get the error:
TypeError: USER is null
Is there a way to either:
avoid Initializing the firebase-app at avery page
keep the CurrentUser (even storing it securely somewhere)
Thank you
Workaround:
I got this workaround working before Frank answer which is probably the best way to proceed. Instead I just stored the user id in an encrypted variable accessible to all pages.
Since the main.html page is always loaded, I store/removed the variable in a onAuthStateChanged listener there so as soon as the user is logged out, that variable is removed:
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
cached_uid = JSON.stringify(user.uid);
cached_uid = btoa(cached_uid);
localStorage.setItem('_uid',cached_uid);
} else {
localStorage.removeItem('_uid');
}
});
then on the other pages:
function loadUID(){
var uid = localStorage.getItem('_uid');
if (!uid) return false;
uid = atob(uid);
uid = JSON.parse(uid);
return uid
}
I followed this to find this solution:
How to send variables from one file to another in Javascript?
You will need to initialize the Firebase app on each page, but that is supposed to be a fairly cheap operation.
To pick up the user on the new page, Firebase runs a check against the server to ensure the user token is still valid. Since this code calls a server, its result likely isn't available yet when your firebase.auth().currentUser runs.
To solve this, run the code that requires a user in a so-called auth state change listener:
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
// User is signed in.
} else {
// No user is signed in.
}
});
Also see the Firebase documentation on getting the currently signed in user.

Check if Firebase Facebook user exists without creating a user starting from anonymous user

In Firebase I need to check if a Facebook user exists without creating the user. Initially the user is anonymous, and they try to login with Facebook. I want this to fail if the Facebook account is not already linked to a user in my system. It won't be linked to the current user because they are anonymous,
If I use Auth.signInAndRetrieveDataWithCredential I expected a "auth/user-not-found" error, but instead the user is simply created. Is this a bug or expected?
let credential = firebase.auth.FacebookAuthProvider.credential(
event.authResponse.accessToken)
firebase.auth().signInAndRetrieveDataWithCredential(credential).then( (userCredential) => {
let user = userCredential.user
app.debug("DEBUG: Existing user signed in:"+user.uid)
this.loginSuccess(user)
}).catch( (err) => {
app.error("ERROR re-signing in:"+err.code)
$("#login_status_msg").text(err)
})
If I use User.reauthenticateAndRetrieveDataWithCredential instead I get the error "auth/user-mismatch" which makes sense because user is currently anonymous. However, I was expecting "auth/user-not-found" may be thrown instead if the credential doesn't exist, but that doesn't happen.
I don't see a way to take my anonymous user, have them login with Facebook and then see if another user is already linked to that Facebook credential without creating the user if it doesn't exist.
If you're wondering why? My scenario is:
The system allows anonymous users
A user logs in, then converts to a logged in user by registering with Facebook.
App uninstall
App reinstall
User starts up the app and is initially anonymous.
They try and login with Facebook again. At this point I want to stop them from creating a user if they don't have one already. If they have a user ID already, the code works fine and changes their anonymous account ID to the original user ID which is good.
I found a solution! It wasn't too hard to implement, but it does seem hacky.
So we know that when using signInAndRetrieveDataWithCredential(cred) for facebook login, the account is created even if it does not exist yet. To solve this, we need to make sure that we handle the following three things:
Detect if the account is new
Delete the current account that was created by firebase
Throw an error to get out of the current flow and return to wherever you were before.
I just implemented and tested this solution, and it seems to work great:
// ... do your stuff to do fb login, get credential, etc:
const userInfo = await firebase.auth().signInAndRetrieveDataWithCredential(credential)
// userInfo includes a property to check if the account is new:
const isNewUser = _.get(userInfo, 'additionalUserInfo.isNewUser', true)
// FIRST, delete the account we just made.
// SECOND, throw an error (or otherwise escape the current context.
if (isNewUser) {
firebase.auth().currentUser.delete()
throw new Error('Couldn\'t find an existing account.')
}
// If the user already exists, just handle normal login
return userInfo.user
The reason I did this was to ensure that users had to go through the "create account flow" in my app. Your case would be really easy to implement as well, something like the following:
let credential = firebase.auth.FacebookAuthProvider.credential(event.authResponse.accessToken)
firebase.auth().signInAndRetrieveDataWithCredential(credential)
.then(userCredential => {
const isNewUser = userCredential.additionalUserInfo.isNewUser
if (isNewUser) {
firebase.auth().currentUser.delete()
// The following error will be handled in your catch statement
throw new Error("Couldn't find an existing account.")
}
// Otherwise, handle login normally:
const user = userCredential.user
app.debug("DEBUG: Existing user signed in:"+user.uid)
this.loginSuccess(user)
}).catch( (err) => {
app.error("ERROR re-signing in:"+err.code)
$("#login_status_msg").text(err)
})
You can use linkAndRetrieveDataWithCredential:
let credential = firebase.auth.FacebookAuthProvider.credential(
event.authResponse.accessToken);
anonymousUser.linkAndRetrieveDataWithCredential(credential).then( (userCredential) => {
// Firebase Auth only allows linking a credential if it is not
// already linked to another account.
// Now the anonymous account is upgraded to a permanent Facebook account.
}).catch( (err) => {
// Check for code: auth/credential-already-in-use
// When this error is returned, it means the credential is already
// used by another account.
})
You can use the method fetchSignInMethodsForEmail to check if an specific email is already associated to an specific provider or not. Doing this you will be able to check if one if the SighInMethods of the email associated to your user contains Facebook.com or not.
I show you below an example about how I manage this cases on my application. I'm using an RxJavaWrapper on my code, but you will understand the point of how to manage it:
RxFirebaseAuth.fetchSignInMethodsForEmail(authInstance, email)
.flatMap { providerResult ->
if (!providerResult.signInMethods!!.contains(credential.provider)) {
return#flatMap Maybe.error<AuthResult>(ProviderNotLinkedException(credential.provider))
} else {
return#flatMap RxFirebaseAuth.signInWithCredential(authInstance, credential)
}
}
.subscribe({ authResult ->
//Manage success
}, { error ->
//Manage error
})
First I check the providers associated to the email of the user(You can retrieve it from the provider)
If the list of SignInMethods contains my credential provider, I throw an error, if not, I call my signInWithCredential method to create the user.
Continue your workflow.
What I did to solve this problem without relying on the call to linkAndRetrieveDataWithCredential to fail and using the catch block to sign in the already existing user is to save the userID field that getCurrentAccessToken returns.
const { userID } = data;
this.props.setFacebookId(userID); // saves the userID on the server
I can later check if this userID already exists next time the user signs up with facebook.

Meteor.js - Fetch/Get Enrollment token (from Accounts.sendEnrollmentEmail)

I can't figure out how to get the enrollment token from the Accounts.sendEnrollmentEmail function.
I know this function sends a direct mail towards the user which in the end looks something like this:
http://localhost:3000/#/enroll-account/FCXzBbqHInZgBlLaOpu8Iv11jP9DJEG-e1auAHDsh6S
However, I would need to somehow get only to the token part FCXzBbqHInZgBlLaOpu8Iv11jP9DJEG-e1auAHDsh6S as I want to send enrollment mail trough a different service (e.g Postmark)
How to do this?
The Accounts.sendEnrollmentEmail(userId, email) function generates a random token and saves it in the user's services.password.reset.token field.
The code that generates the token is:
var token = Random.secret();
var when = new Date();
var tokenRecord = {
token: token,
email: email,
when: when
};
Meteor.users.update(userId, {$set: {
"services.password.reset": tokenRecord
}});
(You can view the function's source code here).
It then sends an email to the user using the Email package. If you want to use a different service to send the email, you basically have 2 options:
Use the same convention yourself (i.e, create the same record and use your own email service in your own function).
Use the existing function, allow the mail delivery to fail silently and then query the user's document for the token and send the email yourself.
Neither is a particularly good option, but both will work for the time being. I wish they had refactored this part into its own function.
Note that the accounts packages are expected to undergo some changes towards the release of the next Meteor versions.
BTW, this function is very similar to Accounts.sendResetPasswordEmail, which you may also wish to override or create your own version.

Categories