Flutter How to Link Multiple Auth Providers to an Firebase Account? - firebase

I am using firebase in my flutter application. In my app user can sign in or register using 3 ways.
Email id and password.
Google
Facebook.
This is my Firebase setting.
This is my user collection.
Now my doubt is that how do I link multiple auth providers for a user having same email id ?
I am storing user's info into User collection with Uid. If I enable multiple account to multiple providers how can I store user's data in same document ?
I have search a lot on google but didn't find proper solution.
For registration with email ID I am using this.
final newUser = await _auth.createUserWithEmailAndPassword(
email: widget.email, password: widget.pass);
Then I am storing user's data into User collection. (In this I have one more form, from where I am getting other data).
Google sign in code
Future<User> signInWithGoogle() async {
// Trigger the authentication flow
final GoogleSignInAccount googleUser = await _googleSignIn.signIn();
// Obtain the auth details from the request
final GoogleSignInAuthentication googleAuth = await googleUser.authentication;
// Create a new credential
final GoogleAuthCredential credential = GoogleAuthProvider.credential(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);
// Once signed in, return the UserCredential
final UserCredential authResult = await _firebaseAuth.signInWithCredential(credential);
final User user = authResult.user;
print('authResult');
print(authResult);
return user;
}

Users are actually allowed to sign in to your app using multiple authentication providers by linking auth provider credentials to an existing user account.
You can follow the documentation here for the process of linking auth provider credentials to an existing user account:
I suggest to also visit the official documentation of Firebase for Flutter.

Related

Flutter Firebase - failing to properly delete a Google authenticated user

I'm trying but failing to re-trigger the authentication steps that the user gets taken through when they authenticate themselves using Google sign-in, following deletion of the user. The deleted user simply gets signed in immediately (instead of being taken through the authentication steps), when using Google sign-in the second time. I want to be able to re-trigger the authentication steps for my own testing purposes.
Specifically, I've got a user who I've authenticated and signed in as per the FlutterFire documentation, i.e.
Future<UserCredential> signInWithGoogle() async {
// Trigger the authentication flow
final GoogleSignInAccount googleUser = await GoogleSignIn().signIn();
// Obtain the auth details from the request
final GoogleSignInAuthentication googleAuth = await googleUser.authentication;
// Create a new credential
final GoogleAuthCredential credential = GoogleAuthProvider.credential(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);
// Once signed in, return the UserCredential
return await FirebaseAuth.instance.signInWithCredential(credential);
}
I then proceed to delete the user; again, as per the FlutterFire documentation, i.e.
try {
await FirebaseAuth.instance.currentUser.delete();
} catch on FirebaseAuthException (e) {
if (e.code == 'requires-recent-login') {
print('The user must reauthenticate before this operation can be executed.');
}
}
That works, insomuch as the user is no longer listed amongst the authenticated users in the Firebase console. However, if I now proceed to call signInWithGoogle() again, then instead of getting taken through the authentication steps again (i.e. being prompted to enter an email, password, etc.), the user simply gets signed in straight away. It's as if the user hasn't been properly deleted. How would I go about re-triggering the authentication steps?
You must also call GoogleSignIn().signOut() after the Firebase sign out or delete.
In my case, I had to reauthenticate firebase user inside the delete functions try-catch as currentUser() always return null AND GoogleSignIn().signOut() didnt work. Maybe a bug.
import 'package:google_sign_in/google_sign_in.dart';
import 'package:firebase_auth/firebase_auth.dart';
final GoogleSignIn _googleSignIn = GoogleSignIn();
final FirebaseAuth _auth = FirebaseAuth.instance;
//will need to sign in to firebase auth again as currentUser always returns null
//this try-catch block should be inside the function that deletes user
try {
//FirebaseUser user = await _auth.currentUser(); //returns null so useless
//signin to google account again
GoogleSignInAccount googleSignInAccount = await _googleSignIn.signIn();
GoogleSignInAuthentication googleSignInAuthentication =
await googleSignInAccount.authentication;
//get google credentials
AuthCredential credential = GoogleAuthProvider.getCredential(
idToken: googleSignInAuthentication.idToken,
accessToken: googleSignInAuthentication.accessToken);
//use credentials to sign in to Firebase
AuthResult authResult = await _auth.signInWithCredential(credential);
//get firebase user
FirebaseUser user = authResult.user;
print(user.email);
//delete user
await user.delete();
//signout from google sign in
await _googleSignIn.signOut();
} catch (e) {
print('Failed to delete user ' + e.toString());
}

what does user.isAnonymous in firebase Authentication signify ? I mean in which case the user is Anonymous?

what does user.isAnonymous in firebase Authentication signify ? I mean in which case the user is Anonymous?
final AuthResult authResult = await _auth.signInWithCredential(credential);
final FirebaseUser user = authResult.user;
assert(!user.isAnonymous);
final FirebaseUser currentUser = await _auth.currentUser();
That isAnonymous property for a user is true when that user signed in to Firebase Authentication with the anonymous identity provider. This means the user didn't provide any credentials, and their sign-in not based on any check or knowledge of the user.

Firebase Id Token from Flutter App is not verified on the REST server

I want support social login with firebase auth. But I want to keep user's detailed profiles in my own RDB .
I thought that after user signed in with social account I could get encoded token which contains user's id and email.
And then if I send the token to the REST server (I built it with spring boot), then the server can decode it to load user's detailed profile from RDB.
I followed the guide from this article.
https://blog.codemagic.io/firebase-authentication-google-sign-in-using-flutter/
I can see sign in process successfully done and the id token printed at the console.
the flutter(dart) code is below.
final GoogleSignInAccount googleSignInAccount = await googleSignIn.signIn();
final GoogleSignInAuthentication googleSignInAuthentication =
await googleSignInAccount.authentication;
final AuthCredential credential = GoogleAuthProvider.getCredential(
accessToken: googleSignInAuthentication.accessToken,
idToken: googleSignInAuthentication.idToken,
);
final AuthResult authResult = await _auth.signInWithCredential(credential);
final FirebaseUser user = authResult.user;
name = user.displayName;
email = user.email;
imageUrl = user.photoUrl;
final FirebaseUser currentUser = await _auth.currentUser();
await currentUser.getIdToken().then((value) => print(value.token));
I copied the token into the server modult to test it could be decoded with no problem.
I added firebase-admin dependency. and run it with java code below.
FirebaseOptions options = new FirebaseOptions.Builder()
.setCredentials(GoogleCredentials.getApplicationDefault())
.setDatabaseUrl("https://test-firebase-auth-token.firebaseio.com")
.build();
FirebaseApp.initializeApp(options);
String firebaseToken = "eyJhbGciOiJSUzI1NiIsImtpZCI6ImQ1OThkYjVjZ..."; // actually it's long string.
FirebaseToken decodedToken = FirebaseAuth.getInstance().verifyIdToken(firebaseToken);
System.out.println(decodedToken.getUid());
System.out.println(decodedToken.getEmail());
And I got an error.
Exception in thread "main" com.google.firebase.auth.FirebaseAuthException: Failed to verify the signature of Firebase ID token. See https://firebase.google.com/docs/auth/admin/verify-id-tokens for details on how to retrieve an ID token.
at com.google.firebase.auth.FirebaseTokenVerifierImpl.checkSignature(FirebaseTokenVerifierImpl.java:154)
at com.google.firebase.auth.FirebaseTokenVerifierImpl.verifyToken(FirebaseTokenVerifierImpl.java:92)
at com.google.firebase.auth.FirebaseAuth$4.execute(FirebaseAuth.java:426)
at com.google.firebase.auth.FirebaseAuth$4.execute(FirebaseAuth.java:423)
at com.google.firebase.internal.CallableOperation.call(CallableOperation.java:36)
at com.google.firebase.auth.FirebaseAuth.verifyIdToken(FirebaseAuth.java:388)
at com.google.firebase.auth.FirebaseAuth.verifyIdToken(FirebaseAuth.java:362)
at com.example.demo.DemoApplicationTests.main(DemoApplicationTests.java:36)
I tried it again with firebase-server-sdk dependency instead of firebase-admin.
And I got an another error message. (seems that it caused by same reason with above error message)
Caused by: com.google.firebase.auth.FirebaseAuthException: Token isn't signed by a valid public key
at com.google.firebase.auth.internal.FirebaseTokenVerifier.verifyTokenAndSignature(FirebaseTokenVerifier.java:61)
at com.google.firebase.auth.FirebaseAuth$1.call(FirebaseAuth.java:146)
at com.google.firebase.auth.FirebaseAuth$1.call(FirebaseAuth.java:140)
at com.google.firebase.tasks.Tasks$1.run(Tasks.java:63)
I checked the token on this site.
https://jwt.io/
I wonder the token is really signed with a (private) key?
Then how jwt.io site decode it without knowing the key?
It seems like it is just encoded with open algorithm like base64. Isn't it?
If so... the token is not secure any more.
What's the problem with my application. or Do I have miss-conception on security?
Any comment will be welcomed. Thanks...
As answered here, problem on print method. It has limited to ~1024 symbols.
You can print like this:
final idToken = await firebaseCredential.user!.getIdToken(true);
print(idToken.substring(0, 1000));
print(idToken.substring(1000));

Link Multiple Auth Providers to an Account

I have implemented Facebook and Google sign in.
But FireBase document says this will cause an error if the same user first signs up with Facebook and later try sign in with Google (with the same email).
So I follow doc and try to configure account linking.
But I do not know how to do.
Should I try link account every time user is logged in? Problem is I not know if the user already has signed in with another auth provider.
For example, the original code has:
Google:
void _signInWithGoogle() async {
final GoogleSignInAccount googleUser = await _googleSignIn.signIn();
final GoogleSignInAuthentication googleAuth =
await googleUser.authentication;
final AuthCredential credential = GoogleAuthProvider.getCredential(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);
final FirebaseUser user = await _auth.signInWithCredential(credential);
}
Facebook:
void _signInWithFacebook() async {
final AuthCredential credential = FacebookAuthProvider.getCredential(
accessToken: _tokenController.text,
);
final FirebaseUser user = await _auth.signInWithCredential(credential);
}
Is correct to call every time in _signInWithFacebook() and _signInWithGoogle() :
user = await auth.linkWithCredential(credential);
For example:
void _signInWithFacebook() async {
final AuthCredential credential = FacebookAuthProvider.getCredential(
accessToken: _tokenController.text,
);
final FirebaseUser user = await _auth.signInWithCredential(credential);
user = await auth.linkWithCredential(credential); //new
}
How I can implement correctly?
Thanks!
When the user enters their email address to sign in, you'll want to use fetchSignInMethodsForEmail() to find out if that email address is already known.
If a user has already signed up with another provider, that's a good moment to ask them if they want to merge those accounts, and then call the account linking API.

What is the logic behind this authentication in Firebase for Flutter?

I'm following the Firebase for Flutter Codelab and on the 8th step there's this _ensureLoggedIn() function:
final _googleSignIn = new GoogleSignIn();
final _auth = FirebaseAuth.instance;
Future<Null> _ensureLoggedIn() async {
GoogleSignInAccount user = _googleSignIn.currentUser;
if (user == null)
user = await _googleSignIn.signInSilently();
if (user == null) {
await _googleSignIn.signIn();
analytics.logLogin();
}
if (await auth.currentUser() == null) {
GoogleSignInAuthentication credentials =
await _googleSignIn.currentUser.authentication;
await auth.signInWithGoogle(
idToken: credentials.idToken,
accessToken: credentials.accessToken,
);
}
}
As a newbie to both Flutter and Firebase frameworks, i'm really struggling to understand the logic behind it: First we attempt to log the user with the GoogleSignIn package, and then regardless of what we will have in user we try to auth the user again with FirebaseAuth, which in turn will also use the GoogleSignIn.
Could you explain me why we do both?
My target is to have two separate screens for users who open my app - one for unauthorized/anonymous (which will have Log In and Register options), and one for authorized users that will see the normal app interface.
The login example in that Codelab seems poorly written, as the user can potentially cancel the non-silent signIn() and then googleSignIn.currentUser will be null when they try to access googleSignIn.currentUser.authentication. I think a better way to handle it is trigger the Google sign in and handle the Firebase auth in in the googleSignIn.onAuthStateChanged listener.
As for why both are used in that example: If you want to authenticate a user on Firebase with a Google account you have to provide an idToken and accessToken, which must be obtained from a valid Google login. So first, you have them sign in to their Google account (via googleSignIn) and use the tokens from that to authenticate with Firebase (via auth).
Using googleSignIn is only required if you want to authenticate with Firebase using a Google account; you can also use Firebase Auth with a username/password combination (which requires that the account be created on Firebase first) or with a token from a Facebook login, or some other OAuth token.

Resources