How to obtain the refreshToken when google sign in with Flutter / Firebase - flutter

I've already did the google sign with the firebase flutter toolkit. When sign in is done, I receive the idToken to read/write on the database.
final FirebaseAuth _auth = FirebaseAuth.instance;
final GoogleSignIn googleSignIn = GoogleSignIn();
Future<String> signInWithGoogle() async {
await Firebase.initializeApp();
final GoogleSignInAccount googleSignInAccount = await googleSignIn.signIn();
final GoogleSignInAuthentication googleSignInAuthentication = await googleSignInAccount.authentication;
final AuthCredential credential = GoogleAuthProvider.credential(
accessToken: googleSignInAuthentication.accessToken,
idToken: googleSignInAuthentication.idToken,
);
final UserCredential authResult = await _auth.signInWithCredential(credential);
final User user = authResult.user;
final idToken = await user.getIdToken();
}
The problem is, I need to auto login user when he opens the app again. So, when I sign in with google, I do not receive the refresh token, needed to get the new valid idToken, as the Doc.
How can I get the refreshToken ?

You can use SharedPreferences to set a bool value when a user is logged in from google and check the value when the app restarts. Don't forget to remove this value from shared preferences when user logged out.

I'm pretty sure googl_sign_in keeps the refresh token internally and you always receive a valid idToken. If it's about to expire, you can force renewal.
Answering your direct question about refreshToken:
You can create your own implementation of the google_sign_in class. In the end it's just an OAuth token. Here's a web implementation of a custom google_sign_in service: https://stackoverflow.com/a/69746036/5492496
I'm pretty sure you can do the same for Android & iOS using web view, you will loose the single click functionality on Android though.

Related

Google sign in with Firebase on Flutter logs in without selecting google account or authorizing it

I'm trying to implement a sign in with google on a flutter app using firebaseAuth, the user can log in just fine, but I've realized that the app doesn't ask for a google account nor does it ask for authorization if I delete the account in the firebase console.
This is my google Sign in method:
Future<UserCredential> signInWithGoogle() async {
final GoogleSignInAccount? googleUser = await GoogleSignIn().signIn();
final GoogleSignInAuthentication? googleAuth =
await googleUser?.authentication;
final credential = GoogleAuthProvider.credential(
accessToken: googleAuth?.accessToken,
idToken: googleAuth?.idToken,
);
return await FirebaseAuth.instance.signInWithCredential(credential);
}
I've tried changing the way the sign out happens but nothing changed, currently using FirebaseAuth.instance.signOut()
For signing out.

how to keep user logged in

this is how I login using Google and firebase. but I couldn't figure it out as to how to keep the use logged in.. when the app restarts it log the user out automatically
import 'package:firebase_auth/firebase_auth.dart';
import 'package:google_sign_in/google_sign_in.dart';
final FirebaseAuth _auth = FirebaseAuth.instance;
final GoogleSignIn googleSignIn = GoogleSignIn();
Future<String> signInWithGoogle() async {
final GoogleSignInAccount? googleSignInAccount = await googleSignIn.signIn();
final GoogleSignInAuthentication googleSignInAuthentication =
await googleSignInAccount!.authentication;
final AuthCredential credential = GoogleAuthProvider.credential(
accessToken: googleSignInAuthentication.accessToken,
idToken: googleSignInAuthentication.idToken,
);
final authResult = await _auth.signInWithCredential(credential);
final User? user = authResult.user;
assert(!user!.isAnonymous);
final User? currentUser = _auth.currentUser;
assert(user!.uid == currentUser!.uid);
return 'signInWithGoogle succeeded: $user';
}
You can call await _auth.currentUser() at the start of your app to check the current user. Further you may want to store the token in shared preferences.
Firebase automatically persists the user credentials in the shared storage, and restores them when the app restarts. There's nothing you need to do for that.
What you will need to do though is listen for the authentication state as shown in the first code snippet in the documentation on getting the current user:
FirebaseAuth.instance
.authStateChanges()
.listen((User? user) {
if (user != null) {
print(user.uid);
}
});
This code needs to run when the app starts, so I typically have it in my top-level widget and then store the user in the state so that my build method can use it. By listening to auth state changes, the code is run automatically when the user sign-in state is restored at startup (which happens asynchronously, so may take a few moments) but also when the user would later be logged out (for example, if you disable the account in the Firebase Authentication console).
It may not be the best way to do it but this worked.
chooseLogin() {
if (_auth.currentUser == null) {
return const SignUo();
} else {
return const Splash();
}
}

Is it possible to fetch emails from pop3 using sign in with google rather than app password?

I am fetching emails from pop3 with app password which is working perfectly fine. Can I replace app password with sign-in with google? I am using enough_email package.
I am done like this:
await popClient.connectToServer(host, port, isSecure: true);
await popClient.login(email!, token!);
and signing with google like this:
FirebaseAuth auth = FirebaseAuth.instance;
User? user;
final GoogleSignIn googleSignIn = GoogleSignIn();
final GoogleSignInAccount? account = await googleSignIn.signIn();
final GoogleSignInAuthentication authentication = await account.authentication;
final AuthCredential authCredential = GoogleAuthProvider.credential(
accessToken: authentication.accessToken,
idToken: authentication.idToken,
);
final UserCredential userCredential = await auth.signInWithCredential(authCredential);
user = userCredential.user;
I already tried to login with accessToken and idToken but getting PopException -ERR [AUTH] Username and password not accepted
After some research I found the following documents
https://developers.google.com/gmail/imap/xoauth2-protocol
https://developers.google.com/identity/protocols/oauth2
that leads me to the right direction. To authenticate client using sign-in with google. Here is my code for reference:
ImapClient imapClient = ImapClient();
await imapClient.connectToServer("imap.google.com", 993, isSecure: true);
await imapClient.authenticateWithOAuth2(user, accessToken);
await imapClient.selectInbox();
final FetchImapResult result = await imapClient.fetchRecentMessages();
List<MimeMessage> mimeMessages = result.messages;
for (MimeMessage mimeMessage in mimeMessages) {
print(mimeMessage.decodeTextPlainPart());
}
await imapClient.disconnect();
user: email address
accessToken: google sign-in authentication token

Flutter Web Google Sign In

I try to implement a signIn with Google in Flutter Web. I use GoogleSignn 4.1.1 and Firebase Auth 0.15.4. I do not get any error message. It just does not pop up.
I registered the web app in Firebase (Added Dependencies) and even added the <meta> Tag with the google-signin-client_id
The Firebase Auth with Google works when I run it on Android
I also ran the Example App from GoogleSignIn in Web. It also does not pop up.
This is my Login Code (Works on Android)
final FirebaseAuth _auth = FirebaseAuth.instance;
FirebaseUser user = await _auth.currentUser();
if (user != null) {
log.d('alreadyLoggedIn');
} else {
final GoogleSignIn _googleSignIn = GoogleSignIn(clientId: Constants.GOOGLE_SIGN_IN_CLIENT_ID);
final GoogleSignInAccount googleUser = await _googleSignIn.signIn();
final GoogleSignInAuthentication googleAuth =
await googleUser.authentication;
final AuthCredential credential = GoogleAuthProvider.getCredential(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);
await _auth.signInWithCredential(credential);
user = await _auth.currentUser();
assert(user.email != null);
assert(user.displayName != null);
assert(!user.isAnonymous);
assert(await user.getIdToken() != null);
}
return user;
}
I hope someone knows how this can be fixed.
Have you followed all of the instructions (including adding OAuth ID to index.html) from this page?
https://pub.dev/packages/google_sign_in_web
You get your CLIENT ID from https://console.developers.google.com/apis/credentials
You also have to run from terminal like this, for it to work on localhost in debug:
flutter run -d chrome --web-hostname localhost --web-port 5000
The default authorised port is 5000, you can add other URIs on the same page you got your CLIENT ID (e.g.8764367243864-987523.apps.googleusercontent.com), it's under "Authorised JavaScript origins"
https://console.developers.google.com/apis/credentials)

Make flutter app force a user to choose an account with FirebaseAuth and GoogleSignInAuthentication

I want to force a user to select one of his account during login time. Is there any method to do so? I haven't found any configuration like prompt=select_account+consent.
Now, with these codes, after a user logout and then try to login again, it will automatically sign in with the selected account, there is no window showing up for user to select an account.
pubspec.yaml
firebase_auth: ^0.8.1+4
google_sign_in: ^3.2.4
Login part
GoogleSignInAccount googleUser = await _googleSignIn.signIn();
GoogleSignInAuthentication googleAuth = await googleUser.authentication;
final AuthCredential credential = GoogleAuthProvider.getCredential(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);
user = await _auth.signInWithCredential(credential);
Logout part
await FirebaseAuth.instance.signOut();
GoogleSignIn _googleSignIn = GoogleSignIn();
await _googleSignIn.signOut();
Use GoogleSignInAccount.disconnect() before signing out to revoke the previous authentication:
await _googleSignIn.disconnect();
await FirebaseAuth.instance.signOut();
Harold's answer used to work for me, but recently the GoogleSignIn().currentUser appears null for some devices I tested, and then the disconnect function won't work. So, what solved that problem was ensuring it is signed in to Google.
final googleCurrentUser =
GoogleSignIn().currentUser ?? await GoogleSignIn().signIn();
if (googleCurrentUser != null)
await GoogleSignIn().disconnect().catchError((e, stack) {
FirebaseCrashlytics.instance.recordError(e, stack);
});
await _auth.signOut();
A simple way to go about it :-
In your sign out method just use
_auth.signOut();
Now inside Google Sign In package, inside google_sign_in.dart
Future<GoogleSignInAccount> signIn() {
final Future<GoogleSignInAccount> result =
_addMethodCall(GoogleSignInPlatform.instance.signIn, canSkipCall: false);
bool isCanceled(dynamic error) =>
error is PlatformException && error.code == kSignInCanceledError;
return result.catchError((dynamic _) => null, test: isCanceled);
}
Find the above method & set the canSkipCall parameter as false
final Future<GoogleSignInAccount> result =
_addMethodCall(GoogleSignInPlatform.instance.signIn, canSkipCall: false);
This will enable choosing a user every time you try to sign in