Not Able To get the token Id in flutter - firebase

I am using below code to register new user in Flutter Application with Firebase as backend, but I am not able to retrieve the token id in my application, when I tried to save it the firestore database it is storing it as null.
FirebaseAuth.instance
.createUserWithEmailAndPassword(
email: emailInputController.text,
password: pwdInputController.text)
.then((currentUser) =>
Firestore.instance
.collection("users")
.document(currentUser.user.uid)
.setData({
"id": currentUser.user.uid,
"tokenId": currentUser.user.getIdToken(),
"fullname": fullNameInputController.text,
})
I receive this in my logs about the tokenId
Invalid argument: Instance of 'Future<'IdTokenResult'>'

As indicated in the official documentation getIdToken(), it actually returns a promise, so you won't be able to directly access the value. You will need to await the value to be loaded and them manage it. You will need to handle as in the below code sample:
FirebaseAuth.instance.currentUser().then(user => {
if (user != null) {
user.getIdToken().then(token => {
//handle token
}
}
});
In case you want a more direct way, the below line should work for you too.
var token = await FirebaseAuth.instance.currentUser().getIdToken();
While this codes are untested,, I believe they should help you have handle the promise, you should have the value and saving it in your database should not face any more issues.
Let me know if the information helped you!

Related

How do you send a Firebase verification email (so that a user can verify their email) to a user in React Native?

I am making a React Native app where you can create accounts via Firebase authentication. I am try to set up a way to verify your email. I thought the code below would work, but it doesn't work. Anyone know the solution to this:
Code:
async function handleSignUp() {
await createUserWithEmailAndPassword(auth, email, password)
.then((userCredentials) => {
let user = auth.currentUser;
const actionCodeSettings = {
url: `${configData.BASE_URL}/sign-in/?email=${user.email}`,
};
auth.currentUser.sendEmailVerification(actionCodeSettings).then(() => {
alert("Verification link has been sent to your email");
});
})
.catch((error) => alert(error.message));
}
You're using the v9 or later SDK, which uses a modular syntax like you see here: await createUserWithEmailAndPassword(auth, email, password).
Similar to createUserWithEmailAndPassword, sendEmailVerification is a top-level function too in this API, so the syntax is:
sendEmailVerification(auth.currentUser, actionCodeSettings).then(() => {
alert("Verification link has been sent to your email");
});
This API is quite well covered in the Firebase documentation on sending an email verification link, so I recommend keeping that handy while coding.

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 can I add some data to firestore as per user logged in, as well as how can I create the profile of the user?

I'm developing a restaurant application with flutter, I want to use firestore as my database and I've already authenticated with firebase with the help of Andrea Bizzotto's YouTube tutorial of authentication but I'm not able to understand any tutorial or either not able to integerate into the login/signup page for creating a user info in cloud firestore, can someone give me a way to do this, just give a basic introduction or direction to find a way to complete it. Thanks in advance.
after you sign up the user with firebase auth if it's successful you should then run this method to update his data.
Future<dynamic> updateUserData(FirebaseUser user, String photoUrl, String displayName) async {
final Firestore _db = Firestore.instance;
DocumentSnapshot snapshot =
await _db.collection('users').document(user.uid).get();
DocumentReference ref = _db.collection('users').document(user.uid);
if (snapshot.exists) {
return ref;
} else {
return Firestore.instance.runTransaction((Transaction tx) async {
return tx.set(
ref,
{
'uid': user.uid,
'email': user.email,
'name': displayName,
'photo': photoUrl,
'lastSeen': DateTime.now()
},
);
}).then((val) => val.length);
}
}

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!

How to get the email of any user in Firebase based on user id?

I need to get a user object, specifically the user email, I will have the user id in this format:
simplelogin:6
So I need to write a function something like this:
getUserEmail('simplelogin:6')
Is that possible?
It is possible with Admin SDK
Admin SDK cannot be used on client, only in Firebase Cloud Functions which you can then call from client. You will be provided with these promises: (it's really easy to set a cloud function up.)
admin.auth().getUser(uid)
admin.auth().getUserByEmail(email)
admin.auth().getUserByPhoneNumber(phoneNumber)
See here https://firebase.google.com/docs/auth/admin/manage-users#retrieve_user_data
In short, this is what you are looking for
admin.auth().getUser(data.uid)
.then(userRecord => resolve(userRecord.toJSON().email))
.catch(error => reject({status: 'error', code: 500, error}))
full snippet
In the code below, I first verify that the user who calls this function is authorized to display such sensitive information about anybody by checking if his uid is under the node userRights/admin.
export const getUser = functions.https.onCall((data, context) => {
if (!context.auth) return {status: 'error', code: 401, message: 'Not signed in'}
return new Promise((resolve, reject) => {
// verify user's rights
admin.database().ref('userRights/admin').child(context.auth.uid).once('value', snapshot => {
if (snapshot.val() === true) {
// query user data
admin.auth().getUser(data.uid)
.then(userRecord => {
resolve(userRecord.toJSON()) // WARNING! Filter the json first, it contains password hash!
})
.catch(error => {
console.error('Error fetching user data:', error)
reject({status: 'error', code: 500, error})
})
} else {
reject({status: 'error', code: 403, message: 'Forbidden'})
}
})
})
})
BTW, read about difference between onCall() and onRequest() here.
Current solution as per latest update of Firebase framework:
firebase.auth().currentUser && firebase.auth().currentUser.email
See: https://firebase.google.com/docs/reference/js/firebase.auth.Auth.html#currentuser
Every provider haven't a defined email address, but if user authenticate with email. then it will be a possible way to achieve above solution.
To get the email address of the currently logged in user, use the getAuth function. For email and password / simplelogin you should be able to get the email like this:
ref = new Firebase('https://YourFirebase.firebaseio.com');
email = ref.getAuth().password.email;
In my opinion, the password object is not very aptly named, since it contains the email field.
I believe it is not a Firebase feature to get the email address of just any user by uid. Certainly, this would expose the emails of all users to all users. If you do want this, you will need to save the email of each user to the database, by their uid, at the time of account creation. Other users will then be able to retrieve the email from the database by the uid .
simple get the firebaseauth instance.
i created one default email and password in firebase. this is only for the security so that no one can get used other than who knows or who purchased our product to use our app.
Next step we are providing singup screen for user account creation.
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
String email = user.getEmail();
every time user opens the app, user redirecting to dashboard if current user is not equal to our default email.
below is the code
mAuth = FirebaseAuth.getInstance();
if (mAuth.getCurrentUser() != null){
String EMAIL= mAuth.getCurrentUser().getEmail();
if (!EMAIL.equals("example#gmail.com")){
startActivity(new Intent(LoginActivity.this,MainActivity.class));
finish();
}
}
i Am also searching for the same solution finally i got it.
I had the same problem. Needed to replace email in Firestore by uid in order to not keep emails all around the place. It is possible to call it from a script on your computer using Service Account. You don't need Firebase Functions for this.
First Generate service account and download its json key.
Firebase Console > gear icon > Project settings > Service accounts > Generate a new private key button.
https://console.firebase.google.com/u/0/project/MYPROJECT/settings/serviceaccounts/adminsdk
Then create project, add the key and call the Admin SDK.
npm init
npm install dotenv firebase-admin
Place the json key file from above into .keys directory, keeping the project directory clean of keys files. Also .gitignore the directory.
Write the path of the json key file into .env file like this: GOOGLE_APPLICATION_CREDENTIALS=".keys/MYPROJECT-firebase-adminsdk-asdf-234lkjjfsoi.json". We will user dotenv to load it later.
Write following code into index.js:
const admin = require('firebase-admin');
admin.initializeApp({
credential: admin.credential.applicationDefault(),
});
(async () => {
const email = "admin#example.com";
const auth = admin.auth();
const user = await auth.getUserByEmail(email);
// Or by uid as asked
//const user = await auth.getUser(uid);
console.log(user.uid, user.email);
//const firestore = admin.firestore();
// Here be dragons...
})();
Run as follows node -r dotenv/config index.js
See the docs
Current solution (Xcode 11.0)
Auth.auth().currentUser? ?? "Mail"
Auth.auth().currentUser?.email ?? "User"

Resources