How to use timer in flutter app for changing data? - firebase

I am using shared preferences to store the token, email, username and other user details when a user logs in using firebase authentication. The firebase token expires in every one hour so I need to refresh the token on the basis of when the user has returned to the app which I am doing in getCurrentUser() function below. I want to know that if a user has logged in my app, used it for 5 minutes or so and then close the application, will that timer function would still be listening and call the function after the timeout or not?
If it doesn't do so then How can I achieve checking this?
void checkTokenValidity(int time) {
Timer(Duration(seconds: time), () async {
print('token timed out');
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString('token', 'expired');
prefs.remove("currentUser");
});
}
Future<String> getCurrentUser() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
final String currentToken = prefs.getString('token');
final String cuser = prefs.getString('currentUser');
print("current: $cuser");
if (cuser != null && currentToken != 'expired') {
print('signed in and $currentToken');
SharedPreferences prefs = await SharedPreferences.getInstance();
String token = prefs.getString('token');
String uid = prefs.getString('userId');
String email = prefs.getString('userEmail');
String photo = prefs.getString('photo');
_authenticatedUser =
User(email: email, id: uid, token: token, photo: photo);
return 'success';
} else if (currentToken == 'expired') {
print('token is expired');
final FirebaseUser user = await FirebaseAuth.instance.signInAnonymously();
var token = await user.getIdToken();
prefs.setString('token', token);
String uid = prefs.getString('userId');
String email = prefs.getString('userEmail');
String photo = prefs.getString('photo');
_authenticatedUser =
User(id: uid, email: email, token: token, photo: photo);
checkTokenValidity(3600);
return 'token';
} else {
print('user is null');
return null;
}
}
In my authentication function which is not here, I have called checkTokenValidity(3600) just after the user successfully logs in.
I have also tried using FirebaseUser user = await FirebaseAuth.instance.currentUser(); but that also didn't solve the problem.

You went the wrong way. The right way is to add error handler on 401 (Unauthorized) error and handle it by refreshing token and retrying the same query.

Related

Avoid user Login with Firebase on Creation [duplicate]

This question already has an answer here:
Flutter - remove auto login after registration in Firebase
(1 answer)
Closed 1 year ago.
I have an app where users are supposed to be created only by Admin User's the problem is that when a new user is created in Firebase the app sign's in with the new user information, so the original logged user (Admin User), has to logged out, and log back in to create a new user.
This is my function to create a new User:
void createUser(
String email,
String password,
String nombre,
String dui,
DateTime fechaNacimiento,
String telefono,
String nombreContacto,
String telefonoContacto,
DateTime fechaIngreso,
String radio,
File foto,
String acceso,
) async {
try {
final auth = FirebaseAuth.instance;
UserCredential authResult = await auth.createUserWithEmailAndPassword(
email: email,
password: password,
);
//var uploadUid = authResult.user?.uid;
final ref = FirebaseStorage.instance
.ref()
.child('user_images')
.child(authResult.user!.uid + '.jpg');
await ref.putFile(foto);
final url = await ref.getDownloadURL();
await FirebaseFirestore.instance
.collection('users')
.doc(authResult.user!.uid)
.set({
'nombre': nombre,
'dui': dui,
'fechaNacimiento': fechaNacimiento,
'telefono': telefono,
'nombreContacto': nombreContacto,
'telefonoContact': telefonoContacto,
'fechaIngreso': fechaIngreso,
'radio': radio,
'foto': url,
'acceso': acceso,
'uid': authResult.user!.uid,
'correo': email,
'contrasena': password,
});
} catch (err) {
print(err);
}
}
Any Ideas on what to do to avoid the log in on user creation of the newly created user.
Kind Regards
The original admin user does not have to be logged out to create a new user. Simply do this.
FirebaseApp secondaryApp = await Firebase.initializeApp(
name: 'SecondaryApp',
options: Firebase.app().options,
);
try {
UserCredential credential = await FirebaseAuth.instanceFor(app: secondaryApp)
.createUserWithEmailAndPassword(
email: 'email',
password: 'password',
);
if (credential.user == null) throw 'An error occured. Please try again.';
await credential.user.sendEmailVerification();
} on FirebaseAuthException catch (e) {
if (e.code == 'weak-password') {
return _showError('The password provided is too weak.');
} else if (e.code == 'email-already-in-use') {
return _showError('An account already exists for this email.');
}
} catch (e) {
return _showError('An error occured. Please try again.');
}
...
// after creating the account, delete the secondary app as below:
await secondaryApp.delete();
The above code will not logout the admin user, the admin user can still continue with normal operations after creating the account.

facebookAuthCredential.idToken is null in Flutter

I successfully integrated facebook login in android and ios, I also getting facebook access token on login but getting null in token id. Below is the code
Future<UserCredential> signInWithFacebook() async {
// Trigger the sign-in flow
final LoginResult loginResult = await FacebookAuth.instance.login();
// Create a credential from the access token
final OAuthCredential facebookAuthCredential =
FacebookAuthProvider.credential(loginResult.accessToken!.token);
print(facebookAuthCredential.idToken); //Here getting null
// Once signed in, return the UserCredential
return FirebaseAuth.instance.signInWithCredential(facebookAuthCredential);
}
You won't get an idToken at this point with Facebook authentication flow, only accessToken. Use the following code skeleton to manage Facebook sign-in and evaluate the results at specific lines with breakpoints:
Future<UserCredential> signInWithFacebook() async {
final LoginResult loginResult = await FacebookAuth.instance.login();
if (loginResult.status == LoginStatus.success) {
final AccessToken accessToken = loginResult.accessToken!;
final OAuthCredential credential =
FacebookAuthProvider.credential(accessToken.token);
try {
return await FirebaseAuth.instance.signInWithCredential(credential);
} on FirebaseAuthException catch (e) {
// manage Firebase authentication exceptions
} catch (e) {
// manage other exceptions
}
} else {
// login was not successful, for example user cancelled the process
}
}
Then you can call this function with await, and once the future is completed, you can access user data:
final userCredential = await signInWithFacebook();
if (userCredential != null) {
// here you will have your Firebase user in:
// userCredential.user
final idToken = userCredential.user!.getIdToken();
}

firebase is not creating the user (user.uid = null)

I am trying to implemente facebook signin in flutter, however, firebase does not create a 'uid'. Doesn't the firebase create a uid automatically?
it returns:
The getter 'uid' was called on null.
Receiver: null
Tried calling: uid
below is the sign in method:
Future<UserCredential> signInWithFacebook(BuildContext context) async {
final LoginResult result = await FacebookAuth.instance.login();
if(result.status == LoginStatus.success) {
final OAuthCredential credential = FacebookAuthProvider.credential(result.accessToken.token);
return await FirebaseAuth.instance.signInWithCredential(credential)
.then((user) async {
final graphResponse = await http.get(Uri.parse(
'https://graph.facebook.com/v2.12/me?
fields=name,picture,email&access_token=${result
.accessToken.token}'));
final Map profile = jsonDecode(graphResponse.body);
if (profile != null){
authService.createUser(name: name, email: email, dob: dob, sex: sex);
}
return user;
});
}
Navigator.pushReplacement(context, MaterialPageRoute(
builder: (context) => Profile()));
return null;
}
The sign in method returns a facebook alert dialog requesting the permission to share email, when press continue red screen with the error appears. why is the firestore not creating the user? Thanks! I am not familiar with the system and just learning.
create user method in authServices:
Future<bool> createUser(
{String name,
User user,
String email,
String password,
String phone,
String sex,
String dob}) async {
var res = await firebaseAuth.createUserWithEmailAndPassword(
email: '$email',
password: '$password',
);
if ((res.user != null)) {
await saveUserToFirestore(name, res.user, email, dob, phone, sex);
return true;
} else {
return false;
}
}
As far as I can understand your code you first login the user with Facebook and then again create a new user with createUserWithEmailAndPassword. If you use the same email for both the second one will fail and give you null.
To track the auth state for all providers use the onAuthStateChanged listener:
firebase.auth().onAuthStateChanged((user) => {
if (user) {
// User is signed in, see docs for a list of available properties
// https://firebase.google.com/docs/reference/js/firebase.User
var uid = user.uid;
// ...
} else {
// User is signed out
// ...
}
});
More about it here.

Check if user is logged in or not before authentication using google sign in - flutter

In my flutter app, am trying to check if a user is logged in or not before authenticating the user in firebase, so if he is not then do not authenticate
Future<String> loginUserWithGoogle() async {
String returnValue = "error";
GoogleSignIn _googleSignIn = GoogleSignIn(
scopes: [
'email',
'https://www.googleapis.com/auth/contacts.readonly',
],
);
UserData _user = UserData();
try {
GoogleSignInAccount _googleUser = await _googleSignIn.signIn();
GoogleSignInAuthentication _googleAuth = await _googleUser.authentication;
final AuthCredential credential = GoogleAuthProvider.credential(idToken: _googleAuth.idToken, accessToken: _googleAuth.accessToken);
UserCredential _authResult = await _auth.signInWithCredential(credential);
if (_authResult.additionalUserInfo.isNewUser) {
String userGoogleName = _authResult.user.displayName;
List userSplitName = userGoogleName.split(" ");
String userGoogleFirstName = userSplitName.first;
String userGoogleLastName = userSplitName.last;
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setString('googleUid', _authResult.user.uid);
await prefs.setString('googleEmail', _authResult.user.email);
await prefs.setString('googleFirstName', userGoogleFirstName);
await prefs.setString('googleLastName', userGoogleLastName);
await prefs.setString('googleUserType', "user");
returnValue = "new";
} else {
_currentUser = await RealDatabase().getUserData(_authResult.user.uid);
if (_currentUser != null) {
returnValue = "success";
}
}
} on PlatformException catch (e) {
returnValue = e.message;
} catch (e) {
print(e);
}
return returnValue;
}
}
Here what I want to check is that if it is a new user then save his google data in sharedpreference and take him to another page where he can complete some other registration and then sign him in. but what this code does is that if it is a new user it will authenticate, save the info in sharedpeference and then take him to the page and if maybe that user decided to go back to the previous page (since i use Navigator.push(context)) and still click the google sign in button again then it will take him to the home screen without him completing the other registration I want him to do because it already authenticated him first. So please is there a way to do this without first authenticating the user.
You can use stream provider to control if user logged in or not.
Here is an example how to use stream provider in your project;
https://flutterbyexample.com/lesson/stream-provider.

Firebase/Flutter: reload() not refreshing user.isEmailVerified

I'm sending a verification link when a user registers in the app, but when I try to create a stream that listens for when the user has clicked the verify link in the email.
I'm aware that I somehow need to refresh the user token, but I can't seem to get it to work. I thought reload() method was the one, but maybe I'm just not implementing it correctly.
The problem is that the Stream always returns isEmailVerified == false, only way to make it true is for the user to log out and log in again, which is something I'd like to avoid. How do I do this?
I've created this future:
//CHECKS IF EMAIL IS VERIFIED
Future<bool> checkIfEmailIsVerified() async {
FirebaseUser currUser = await _auth.currentUser();
await currUser.reload();
currUser = await _auth.currentUser();
final bool flag = currUser.isEmailVerified;
if (currUser != null) {
return flag;
} else {
return false;
}
}
and this stream:
//IS EMAILVERIFIED STREAM
Stream<EmailVerified> get emailVerified async* {
final bool isEmailVerified = await checkIfEmailIsVerified();
yield EmailVerified(isEmailVerified);
}
Unfortunately it's necessary to get fresh instance of the user after reload:
User user = FirebaseAuth.instance.currentUser;
if (user != null) {
await user.reload();
user = FirebaseAuth.instance.currentUser;
if (user.emailVerified) {
...
}
}
Try using a single await over chained futures
FirebaseUser currUser = await _auth.currentUser().then((u) => u.reload().then((_) => _auth.currentUser()));
final bool flag = currUser.isEmailVerified;

Resources