Flutter, Firebase Auth How to access uid everywhere in the app? - firebase

it's my first app I try to code.
I am using Firebase Auth and Cloud Firestore in my app. How can I make the uid of the logged-in User available on every screen without using await?
I know I can get it that way:
final FirebaseUser user = await _auth.currentUser();
String id = user.uid;
but I need to access the uid without awaiting it, for example here:
Stream<QuerySnapshot> get groups {
return Firestore.instance.collection("userGroups")
.where("userId", isEqualTo: " uid ").snapshots();
}
As I am using the provider package I thought I can make it available through it. But how is the best way to do it?
Thank You!

You can provide the firebase auth stream above your app:
StreamProvider<FirebaseUser>.value(
value: FirebaseAuth.instance.onAuthStateChanged,
lazy: false,
child: YourApp(),
)
and when want to get the user you can:
final user = Provider.of<FirebaseUser>(context);
Note, this will not listen to changes to the user object beyond signins and sign outs. To listen to actual user changes on the user (such as isVerified or photoUrl changes) you'll want to use a package like firebase_user_stream and replace the previous stream with something like:
StreamProvider<FirebaseUser>.value(
value: FirebaseUserReloader.onAuthStateChangedOrReloaded.asBroadcastStream(),
lazy: false,
),

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 auto signOut a firebase user in flutter?

I'm working on a flutter app with firebase as a backend and an authentication, now I want the app to have a trial version where the user can signInWithGoogle and use the app with full features ( for like one hour ), and then signs out automatically after that one hour ends.
SO FAR, I accomplished the following:1- signIn with google account.
2- add signed in user to _trial collection with timeStamp.
Future enterTrial() async {
final UserCredential user = await Auth.instance.googleSignInMethod();
final User actualUser = user.user;
try {
//_trial is a collection for users in Trial Period
await _trial.doc(actualUser.email).set({.
"name": actualUser.displayName,
"email": actualUser.email,
"creationDate": FieldValue.serverTimestamp(),
});
} catch (e) {
print(e);
}
}
WHAT's LEFT:
after one hour from signIn, signOut and move user from _trial collection to _expired collection.
so that I could check if the user is expired or not.
is there a way to automatically signOut after sometime? or to - periodically - compare creationTime with currentTime and accordingly signOut?
Thanks In Advance.
Yes You can achive this by using background_fetch package this provide you a callback function in that function you can call your logout method.

multiprovider - pass value from first to second provider

I am trying to setup a MultiProvider but I am struggling to figure out how to pass a value from one to the other. I have a list of data that has a userID in it, my app would previously make a StreamProvider for user info and then another for the list of data. This was fine as I could pass the user.uid to the data and then apply the filter. Something like this...
final user = Provider.of<User>(context);
return StreamProvider<List<UserLevelTwo>>.value(
value: DatabaseService(uid: user.uid).levelTwoSelector,
The problem was that I couldn't access the List throughout the app so after researching it looks like it is meant to be put before the MaterialApp with the other Provider. I have got this setup but only with manually passing the UID.
Widget build(BuildContext context) {
return MultiProvider(
providers: [
StreamProvider<User>.value(value: AuthService().user),
StreamProvider<List<UserLevelTwo>>.value(value: DatabaseService(uid: 'rgDe5I0QgFfa123mIgxo8VQew9T2').levelTwoSelector),
],
child: MaterialApp(
What I need help with is how do I pass the uid from the first provider into the second provider?
The second streamprovider calls my DB service which is as below:
class DatabaseService {
final String uid;
DatabaseService({this.uid});
Query getLevelTwoSelectorData(){
return Firestore.instance.collection('ULTs')
.where("UserID", isEqualTo: uid)
.where("D", isEqualTo: false)
.orderBy("R", descending: true)
.orderBy("Desc");
}
Stream<List<UserLevelTwo>> get levelTwoSelector {
return getLevelTwoSelectorData().snapshots().map(_userLevelTwoListFromSnapshot);
}
}
If anyone could help me that would be greatly appreciated!
Thanks
Thanks for the help. I got this to work by using the same method described here Pass user uid to Firestore stream query in Flutter
More or less it seems that you have one StreamProvider for the user info, then once that isn't null, you create a multiprovider for the list with the uid from the original provider.
Thanks again!

How to make Flutter authentification just over Firestore (without Firebase Auth)

I have to create a manual Registration and Sign in over Firestore without using Firebase Auth.
The reason for that requrirement is once when user wants to reset his password, first of all I have to sent him a confirmation code (MD5) on his email address and when he receives the code, above new password that he should enter, he must paste that code.
I'll sent him a code on email and in the same time, that code will be also in Firestore from which later on I will compare (code is correct or not).
By the end, (if entered code is correct, his new password will overwrite previous stored password in his document). Documents are named by email address which means that email should be unique.
Here is the code that I'm using for registration:
void onPressedRegister(BuildContext context, String fullName, String email,
String password, String phoneNumber, dynamic formKey) {
if (formKey.currentState.validate()) {
db.collection("firestoreUsers").document(email).setData({
'fullName': fullName,
'email': email,
'password': password,
'phoneNumber': phoneNumber
});
Now I have a problem with Sign in because I can't check is user stored in database or not. Here is what I wrote:
Future<bool> signInOverFirestore(String email, String password) async {
db
.collection('firestoreUsers')
.where('email', isEqualTo: email)
.getDocuments();
return true;
}
So is it possible to make sign in like this and also update that user later on when he enter correct code from his email, or not?
If you're not using Firebase Auth with Firestore and security rules, it's basically impossible to have secure per-user data. What you have now is storing plaintext email addresses and passwords for everyone in the world to see. All someone has to do is use the Firestore REST API to query the entire firestoreUsers collection to see everything.
According to my understanding, you want to make Firestore data public (access/read without authentication) check if some document with a value and if that value doesn't exist, you want to create a new document. In order to do that you can try to fetch the document which is equal to your value(email) and catch the error or listen if it is successful. You may accomplish it with the following example.
_db
.collection('path')
.where('field', isEqualTo: '')
.getDocuments()
.then((value) {
print('User on Firestore : $value');
}).catchError((e) {
print('Error no document found');
});

Not Able To get the token Id in flutter

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!

Resources