Firebase user.getToken having stale value after signout - firebase

I am following Firebase guide for authentication for the web (https://github.com/firebase/FirebaseUI-Web), it works fine for most of the cases. However, if I log out of my Gmail account it still shows the data of the previously logged in user. The biggest challenge is user.getToken() is getting the old token. Has anyone faced similar issue? What would be the way to resolve it.
Here are the details ( please note it is not full-fledged application, I am exploring the authentication part before using in my web application)
In chrome browser, I log into my Gmail account
Go to my test web application/SPA, on the same browser
I see my details on the page (see the code below)
var displayName = user.displayName;
var email = user.email;
var emailVerified = user.emailVerified;
var photoURL = user.photoURL;
var uid = user.uid;
var providerData = user.providerData;
user.getToken().then(function(accessToken) {
document.getElementById('sign-in-status').textContent = 'Signed in';
document.getElementById('sign-in').textContent = 'Sign out';
document.getElementById('account-details').textContent = JSON.stringify({
displayName: displayName,
email: email,
emailVerified: emailVerified,
photoURL: photoURL,
uid: uid,
accessToken: accessToken,
providerData: providerData
}, null, ' ');
});
Now I log out of my Gmail account
Go to test web application/SPA, still seeing the details
Go to chrome debugger and type user.getToken(), not null value is returned.
Ideally, it should be null.
Appreciate any help.
Kind Regards

Related

What Sign in method to use best?

We are having a flutter app (ios, android, web), where users are signed in via username & password.
We are also using google firebase since its powerful and easy to integrate.
The username and password mainly belongs to the website where we are gathering data at. (As example - If they use the website without the app, and they change the password, after that he wont be able to login to the app)
Now the mentionned websites host is giving us API access, login via OpenId to get the access token for the API. Because we are a safety risk since we store the passwort of the users too!
For the API access we dont really need to store Username and password of the user, since they are redundant anyway. But if we want to add a feature (for example message sending or further data storage) we need to have the user signed in into firebase.
Upt to now we are using for (first) signin the following snippet:
firebaseAuth.createUserWithEmailAndPassword(
email: email, password: password);
and for already signed in users :
firebaseAuth.signInWithEmailAndPassword(
email: email, password: password);
Notice that similar credentials are also using to login on the API. (Since the user is there already registered)
How can we login on firebase with said information without asking twice for a password ond username (once for us, and once for the API) ?
We already tried :
await firebaseAuth.signInWithCustomToken(token)
with the jwl token from the OpenId, of course it did not work because the token did not contain the uid reference.
SOLUTION
Create a Firebase Cloud Function just like described in Firebase Cloud Functions.
Be aware that if you want to create a customtoken, the cloud functions need rights. On initializeApp(..)
admin.initializeApp({
serviceAccountId: '{App_Name}#appspot.gserviceaccount.com',
});
So the correct service account has to be selected, you also have to give him the rights to generate tokens. (See => Stackoverflow Question
The Cloud Function does then look the following way :
export const functionName= functions.https.onRequest(async (request, response) => {
const id = request.query.id;
const passcode = request.query.passcode; // not really needed
// add other passcodes for different authentications
if (passcode == "{COMPARE SOMETHING}") {
await admin.auth().createCustomToken(id).then((customToken) => {
response.status(200).send({
'id': id,
'customToken': customToken
});
}).catch((error) => {
response.status(500).send({
'ErrorMessage': "No token could be generated",
"Error": error
});
});
}
else {
response.status(500).send({
'ErrorMessage': "Passcode wrong"
});
}
});
On the other hand we have the code on the mobile app :
// Get JWT Token
Map<String, dynamic> jwtpayload =
Jwt.parseJwt(response_decoded['id_token']); // use import 'package:jwt_decode/jwt_decode.dart';
final queryParameters = {
'id': jwtpayload ['sub'],
'passcode': 'APassCode',
};
final uri = Uri.https('us-central1-{yourApp}.cloudfunctions.net',
'/{functionName}', queryParameters);
final cloud_function_api_call = await client.post(uri);
var decoded_cloud_function_api_call =
jsonDecode(cloud_function_api_call.body);
And at the end :
await firebaseAuth.signInWithCustomToken(
decoded_cloud_function_api_call['customToken']);
I hope it helps others facing a similar issue.

How to get Facebook Email from flutter_facebook_login in Firebase?

I am using flutter_facebook_login plugin(3.0.0). But my Facebook email doesn't appear inside my Firebase user identifier column; instead, I see this "--". Please help!
Future loginWithFacebook() async {
FacebookLogin facebookLogin = FacebookLogin();
final result = await facebookLogin.logIn(['email', 'public_profile']);
//result.accessToken.
final token = result.accessToken.token;
print('Facebook token userID : ${result.accessToken.permissions}');
final graphResponse = await http.get(
'https://graph.facebook.com/v2.12/me?fields=name,first_name,last_name,email&access_token=${token}');
final profile = jsonDecode(graphResponse.body);
print(profile);
if (result.status == FacebookLoginStatus.loggedIn) {
final credential = FacebookAuthProvider.getCredential(accessToken: token);
FirebaseUser fbUser = (await _auth.signInWithCredential(credential)).user;
//print('Our credential is : $credential');
print('Facebook firebase user ${fbUser.}');
}
return _userFromFacebookLogin(profile);
}
}
Possible Points.
Some Facebook accounts are created using Mobile numbers, so whenever we request for email address we get an empty string.
Email was set to "--" on firebase auth due to missing permission to read email, which fixed by:
final FacebookLoginResult facebookLoginResult = await facebook_Login.logIn(['email', 'public_profile']);
After reading post in firebase-talk google group here https://groups.google.com/forum/#!topic/firebase-talk/gPGNq-IkTLo, I found out the answer. The issue was happened because I'm using "Allow creation of multiple accounts with the same email address" in Firebase Auth sign-in method.
So I change the option into: "Prevent creation of multiple accounts with the same email address" can it's working properly now. It's simple as that. It's true I need more logic to merge accounts having the same email address, but it's okay.
Maybe everyone else having the same issue, can also try this, and hopefully it's solved as well.

updatePhoneNumber failed: First argument "phoneCredential" must be a valid phone credential

I am trying to updatePhoneNumber and keep getting the following error above. Looking at the docs I was under the impressing this is a method?
Js:
user.updatePhoneNumber({
phoneNumber: "+15618104444",
});
I tried setting this in the updateProfile Method as well and still no luck.
user.updateProfile({
displayName: displayName,
photoURL: photoURL,
phoneNumber: "+15618104444"
});
updatePhoneNumber requires a phone credential since the phone number needs to be verified by SMS.
// 'recaptcha-container' is the ID of an element in the DOM.
var applicationVerifier = new firebase.auth.RecaptchaVerifier(
'recaptcha-container');
var provider = new firebase.auth.PhoneAuthProvider();
provider.verifyPhoneNumber('+16505550101', applicationVerifier)
.then(function(verificationId) {
var verificationCode = window.prompt('Please enter the verification ' +
'code that was sent to your mobile device.');
return firebase.auth.PhoneAuthProvider.credential(verificationId,
verificationCode);
})
.then(function(phoneCredential) {
return user.updatePhoneNumber(phoneCredential);
});

Flutter - firebase_auth updateProfile method is not working

I'm creating app using Flutter with Firebase. I have some weird issues. I'm creating authentication and it is working fine but when i try to add some collections to my Firestore Database, the record of displayName is set to null.
Future<FirebaseUser> createUser(email, password, displayName) async {
final FirebaseUser user = await _auth.createUserWithEmailAndPassword(
email: email, password: password);
UserUpdateInfo info = new UserUpdateInfo();
info.displayName = displayName;
_auth.updateProfile(info);
Firestore.instance.collection('users').document().setData({
'name': user.displayName,
'uid': user.uid,
'email': user.email,
'isEmailVerified': user.isEmailVerified,
'photoUrl': user.photoUrl,
});
return user;
}
this is Future class that creates user.
void _handleSubmitted() {
userAuth
.createUser(
emailController.text, passwordController.text, nameController.text)
.then((onValue) {
print("Sign Up button clicked: $onValue");
});
}
this method is handle when sign up button is clicked.
and collection looks like this picture.
If I recall correctly, the local user profile is not immediately updated when you call updateDisplayName or updatePhotoURL. This means you should either just write the local values to the database (easiest) or force a reload of the profile (safest).
Write the local values
As said, this is the simplest approach:
FirebaseFirestore.instance.collection('users').doc().set({
'name': displayName,
'uid': user.uid,
'email': user.email,
'isEmailVerified': user.emailVerified, // will also be false
'photoUrl': user.photoURL, // will always be null
});
Note that emailVerified will always be false, and photoUrl will always be null on a newly created email+password account.
Force a reload of the profile
You can force a reload of the user data by calling FirebaseUser.reload():
await _auth.currentUser!.updateDisplayName(displayName);
await _auth.currentUser!.updateEmail(newEmail);
await _auth.currentUser!.updatePhotoURL(photoURL);
await _auth.currentUser!.reload();
user = _auth.currentUser;
To Update users display name and photo url this will definitely helpful.
FirebaseUser user = await FirebaseAuth.instance.currentUser();
UserUpdateInfo userUpdateInfo = new UserUpdateInfo();
userUpdateInfo.displayName = name;
userUpdateInfo.photoUrl = url;
user.updateProfile(userUpdateInfo);
It's inconvenient the way Firebase Auth works, and they say it won't change for now, so I made a package to address this, grab it here: firebase_user_stream
In the Readme I explain the issues and how the package fixes them, there are examples and etc, enjoy!
EDIT (Jul/2020):
Firebase Auth for Flutter now has a userChanges stream:
which triggers whenever auth state, id token or profile changes occur. Essentially, this acts as a way to obtain realtime changes on currently stateless functionality (such as updateProfile).
I also tried everything but however, this works for me! Just call the FirebaseAuth.instance twice like the one in the code.
FirebaseUser currentUser = await _auth.currentUser();
await currentUser.reload();
currentUser = await _auth.currentUser();
print("Current User ${currentUser.displayName}");
print("Current User ${currentUser.photoUrl}");
hope this helps you!

Get Identifier field from Firebase Auth Console

I'm trying to get the email from the user that's currently authenticated using the Facebook Firebase provider. The email is listed under the Identifier field inside the Project's Firebase Authentication Console:
However when I invoke firebase.auth().currentUser the user information loads, however the email field is null. Any ideas on how to get the Identifier (which is where I see the email address) from Firebase? Is this even possible?
Below is the code I'm using:
componentDidMount() {
let user = firebase.auth().currentUser;
let name, email, photoUrl, uid, emailVerified;
if (user !== null) {
name = user.displayName;
email = user.email;
photoUrl = user.photoURL;
emailVerified = user.emailVerified;
uid = user.uid;
console.log(name, email, photoUrl, emailVerified, uid);
}
}
Note: Prevent creation of multiple accounts with the same email address is enabled in Firebase. Also, Facebook API permissions are set to ['public_profile', 'email']
After some testing and debugging I found that the email field will be populated if you're using a regular Firebase Email/Password Sign In method. However, if you're using another Sign In provider method such as Facebook, the email field will appear null (not sure why).
Further inspection of the user object revealed a providerData property.
It's an array that contains all the provider information (including the email address):
So, I updated my code to accommodate this:
componentDidMount() {
let user = firebase.auth().currentUser;
let name, email, photoUrl, uid, emailVerified;
if (user) {
name = user.displayName;
email = user.email;
photoUrl = user.photoURL;
emailVerified = user.emailVerified;
uid = user.uid;
if (!email) {
email = user.providerData[0].email;
}
console.log(name, email, photoUrl, emailVerified, uid);
}
}
In my case, the getEmail() method always returns data for three sign-in possibilities (if user gave authorization to my app to show/use email): Sign in with Email, Sign in with Google, Sign in with Facebook.
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
Log.v(TAG,"user.getEmail():"+user.getEmail());
if (user.getEmail() == null){
// User did not authorize the app to show/user email
}
else {
Log.v(TAG,"user.getEmail():"+user.getEmail());
}

Resources