facebookAuthCredential.idToken is null in Flutter - firebase

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();
}

Related

Facebook login returns generic profile picture URL

Well I'm using the code from Firebase and the in the user object the photoURL which being returned is General facebook profile with no image
which is generic profile picture
Future signInWithFacebook() async {
try {
// Trigger the sign-in flow
final result = await FacebookAuth.instance.login();
// Create a credential from the access token
final FacebookAuthCredential facebookAuthCredential =
FacebookAuthProvider.credential(result.token);
// Once signed in, return the UserCredential
UserCredential res = await FirebaseAuth.instance.signInWithCredential(facebookAuthCredential);
User user = res.user;
//create a new document for the user with the uid
await UserProfileDatabaseService(uid: user.uid).updateUserData(
user.displayName,
user.email,
user.emailVerified,
user.phoneNumber,
user.isAnonymous
, {'helper': true},
250
); return user;
} catch (e) {
print(e.toString());
return null;
}
}
you can use the following code to display the Facebook profile picture URL after a successful login:
await FacebookAuth.instance.login();
final user = await FacebookAuth.instance.getUserData();
Image.network(user["picture"]['data']['url']);
This code logs the user in via FacebookAuth, retrieves their user data, and displays their profile picture using the Image.network widget.

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.

Flutter FirebaseUser how to access the user data

I am learning Firebase with Flutter.
Currently making an anonymous login option, here is the class I created:
class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;
// sign in anonymously
Future signInAnonymous() async {
try{
// signs in as anon user
AuthResult signInResult = await _auth.signInAnonymously();
// retruns currently signed in user, else null
FirebaseUser userFromResult = signInResult.user;
return userFromResult; // HERE: if I add .uid, the id object is displayed
}catch(e){
print(e.toString());
return null;
}
}
}
In my login page after creating an instance and using the method, when I print the result I get FirebaseUser(Instance of 'PlatformUser') insted of the user information, here is the code:
onPressed: () async {
dynamic result = await _auth.signInAnonymous();
if(result == null){print('Error signing in.');}
else{
print('Signed in successfully');
print(result);
}
How can I access the user data?
UPDATE: If I change return userFromResult; to return userFromResult.uid; the id string is returned.
I still wonder, however, how to print the full object.
Your Result inside of the onpressed is a dynamic type cast, but it is a FirebaseUser inside.
// onPressed Callback
dynamic result = await _auth.signInAnonymous();
You can change your SignIn method with the right return type and use instead of dynamic the FirebaseUser.
Future<FirebaseUser> signInAnonymous() async {
// [...]
return userFromResult; // HERE: if I add .uid, the id object is displayed
}
onPressed: () async {
FirebaseUser result = await _auth.signInAnonymous();
print(result.uid); // should contain the id
// [...]
The difference is that in version 0.13.x the user data is available, but in the version used in this example the bersion used is 0.16.x.

How to check if phone number is already registered in firebase authentication using flutter

So i am making a simple sign up and login screens in flutter application that uses phone authentication of firebase. For sign up im able to register new user, as the user provides his phone number and gets OTP. But for login i wanna check if the entered number is already registered. If so he gets otp and logs in or if not registered then asks to sign up first.
Firebase admin SDK supports this. Here's how to set up firebase admin (documentation). After you set up admin, you can use cloud_functions package to call APIs from the firebase admin SDK and the API we'll be using is one that allows us to get a user by phone number (documentation). If the API response is a user record, we know a phone exists.
In this example, I'm using node.js. In functions/index.js:
exports.checkIfPhoneExists = functions.https.onCall((data, context) => {
const phone = data.phone
return admin.auth().getUserByPhoneNumber(phone)
.then(function(userRecord){
return true;
})
.catch(function(error) {
return false;
});
});
In your dart code:
final HttpsCallable callable = CloudFunctions.instance.getHttpsCallable(functionName: 'checkIfPhoneExists');
dynamic resp = await callable.call({'phone': _phone});
if (resp.data) {
// user exists
}
Once the OTP is sent to the user you can verify if the user is a new user or an existing one in verify OTP function
verifyOtp(String input, context) async {
String retVal = "error";
OurUser _user = OurUser();
print(input);
final AuthCredential credential = PhoneAuthProvider.credential(
verificationId: _verificationId, smsCode: input);
try {
// await _auth.signInWithCredential(credential);
UserCredential _authResult = await _auth.signInWithCredential(credential);
// Here i have to save the details of the user in the database
if (_authResult.additionalUserInfo.isNewUser) {
currentUser.uid = _authResult.user.uid;
currentUser.phone = _inputText;
currentUser.type = "Customer";
retVal = await OurDatabase().createUser(currentUser);
} else {
// get the information of the user from the database this already exists
currentUser = await OurDatabase().getUserInfo(_authResult.user.uid);
if(currentUser!= null) {
Navigator.pushNamedAndRemoveUntil(
context, "/homescreen", (route) => false);
}
}
print("End of the await");
// when signup with the otp
if (retVal == "success") {
print("why not inside this mane");
Navigator.pushNamedAndRemoveUntil(
context, "/homescreen", (route) => false);
}
saveAllData();
} catch (e) {
print(e);
print("Something went wrong");
//prin
}
}
Now this is when you want to verify OTP from the user and after the top is verified you can know if the user was indeed a new user or an old one but what if you wanted to know that beforehand then the best possible solution would be to create a new collection in the firestore that would have only one document(so you are charged only for one document read) that would just contain all the numbers of the users that are registered within your application,
I used a simple straight forward way and it worked just fine.
First, add the mobile number to the firebase database in a separate node when the user creates the account.
await dbref.child("RegisteredNumbers").push().set({
"phoneNo": FirebaseAuth.instance.currentUser!.phoneNumber,
});
whenever a user tries to log in or signup check in this node if the provided number is available in It or not.
Future<bool> checkNumberIsRegistered({required String number}) async {
bool isNumberRegistered = false;
try {
await dbref.child("RegisteredNumbers").once().then((data) {
for (var i in data.snapshot.children) {
String data = i.child("phoneNo").value.toString();
if (number == data) {
isNumberRegistered = true;
return isNumberRegistered;
} else {
isNumberRegistered = false;
}
}
});
return isNumberRegistered;
} catch (e) {
return false;
}
}
Hope it helps

How to use timer in flutter app for changing data?

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.

Resources