Google Sign-In With Flutter: Error Code -4 - ios

I currently try to implement google_sign_in package in Flutter (https://pub.dartlang.org/packages/google_sign_in).
For this, I followed the example of their repository (https://github.com/flutter/plugins/blob/master/packages/google_sign_in/lib/google_sign_in.dart).
In that example in "initState" is a call signInSilently.
#override
void initState() {
super.initState();
_googleSignIn.onCurrentUserChanged.listen((GoogleSignInAccount account) {
setState(() {
_currentUser = account;
loggedIn = true;
});
});
loggedIn = false;
_googleSignIn.signInSilently();
}
I tried this code in iOS. On my first App Start, it worked well. But since I logged out I get an error here all the time I restart my app.It is the following PlatformException:
PlatformException(sign_in_required, com.google.GIDSignIn, The operation couldn’t be completed. (com.google.GIDSignIn error -4.))
I found in question Google Sign-In Error -4 that the error code is because of a missing Auth in Keychain.
The solution while swift programming is to call the method * hasAuthInKeychain* before the try to signInSilently. My problem is that the GoogleSignIn class in the flutter package has no function named like this.
Is there another call I need to run with this package to be sure I can try a silent log in? Or am I doing something wrong to get this message or is there even the possibility of catching this error?
Edit
I tried Marcel's solution, too. Somehow it is not catching the PlatfromException.
I do not know if this will help: signInSilently() is calling a method in which there is a the following call (google_sign_in.dart, line 217):
await channel.invokeMethod(method)
In platform_channel.dart there is a call
codec.decodeEnvelope(result);
The platform exception gets thrown in here.
if (errorCode is String && (errorMessage == null || errorMessage is String) && !buffer.hasRemaining)
throw PlatformException(code: errorCode, message: errorMessage, details: errorDetails);
else
throw const FormatException('Invalid envelope');
Edit 2
Since I just run my app and not started it in debug mode it somehow works again without throwing an exception. I do not know how this affects the code and why I got this exception. I can also run the code in debug mode again.
Since then I had the exception once again. Again I restarted android studio and runned the application once without debug mode.

You could just check if the sign in failed by handling the PlatformException like this:
void _setUpGoogleSignIn() async {
try {
final account = await _googleSignIn.signInSilently();
print("Successfully signed in as ${account.displayName}.");
} on PlatformException catch (e) {
// User not signed in yet. Do something appropriate.
print("The user is not signed in yet. Asking to sign in.");
_googleSignIn.signIn();
}
}

This is one way to catch the error and run _googleSignIn.signIn();
GoogleSignInAccount googleSignInAccount = await googleSignIn
.signInSilently(suppressErrors: false)
.catchError((dynamic error) async {
GoogleSignInAccount googleSignInAccount =
await _googleSignIn.signIn();
});

In my case, I did not want the user to see the login window automatically. In this case I changed from signIn to signOut. This way, I send the user to another view with an explanatory message and a login button.
GoogleSignInAccount googleSignInAccount = await googleSignIn
.signInSilently(suppressErrors: false)
.catchError((dynamic error) async {
GoogleSignInAccount googleSignInAccount = await _googleSignIn.signOut();
return googleSignInAccount;
});

Related

Plugin.FirebaseAuth.FirebaseAuthException: An error occurred when accessing the keychain Xamarin.forms app

I'm using the latest stable package of Plugin.FirebaseAuth (4.1.0). But when I try to call the SignInWithEmailAndPasswordAsync(email, password) when using the iOS simulator. I get an exception?
Method:
public async Task<bool> SignIn(string email, string password)
{
try
{
var result = await CrossFirebaseAuth.Current.Instance.SignInWithEmailAndPasswordAsync(email, password);
var token = await result.User.GetIdTokenAsync(true);
Preferences.Set("MyFirebaseRefreshToken", token);
AccountManager.CurrentUserId = result.User.Uid;
return true;
}
catch (FirebaseAuthException ex)
{
Console.WriteLine(ex.Reason);
await App.Current.MainPage.DisplayAlert($"Alert", (ex.Reason.ToString()), "OK");
return false;
}
}`
Error:
If it only happens to ios, that's maybe because you didn't add the Team ID prefix before your App ID. Like this:
Auth.auth().useUserAccessGroup("XK********.com.matkonit.SharedItems")
You can refer to this page.
Ok So the issue turns out to be with the simulator for iOS.
The fix:
You'll need an apple developer account, and a provisioning profile. You'll also need a custom entitlements.plist

AcquireTokenInteractive: What Do I do when a user abandons the login process?

I am writing a console app using some code I found on GitHub. The code below works fine.
If the token is cached, it is retrieved and the application continues. (AcquireTokenSilent)
If no token is found then I prompt the user for their credentials. They're taken to the company login site. They login, and the application continues. (AcquireTokenInteractive)
However, let's say the user simply changes their mind and abandons the login process by closing web the browser. Now, my code is just sitting there. All you see is a command window doing nothing.
It's clearly waiting for some response, that will never come since the Web Browser will no longer be communicating with my Console App.
How would I receive a message from the closing Web Browser, so my Console App knows to throw an exception or process the abandoned login somehow?
namespace PublicClientAuthentication
{
class AuthenticationProvider : IAuthenticationProvider
{
IPublicClientApplication _clientApp;
private string[] _scopes;
public AuthenticationProvider(IPublicClientApplication app, string[] scopes)
{
_clientApp = app;
_scopes = scopes;
}
public async Task AuthenticateRequestAsync(HttpRequestMessage request)
{
AuthenticationResult authResult = null;
try
{
var accounts = await _clientApp.GetAccountsAsync();
authResult = await _clientApp.AcquireTokenSilent(_scopes.ToArray(), accounts.FirstOrDefault()).ExecuteAsync().ConfigureAwait(false);
}
catch (MsalUiRequiredException ex)
{
System.Diagnostics.Debug.WriteLine(ex);
authResult = await _clientApp.AcquireTokenInteractive(_scopes)
.WithPrompt(Microsoft.Identity.Client.Prompt.ForceLogin)
.ExecuteAsync().ConfigureAwait(false);
}
catch (MsalServiceException ex)
{
System.Diagnostics.Debug.WriteLine(ex);
}
catch (MsalClientException ex)
{
System.Diagnostics.Debug.WriteLine(ex);
authResult = await _clientApp.AcquireTokenInteractive(_scopes).ExecuteAsync();
}
catch (Exception e)
{
System.Diagnostics.Debug.Write(e);
}
request.Headers.Add("Authorization", authResult.CreateAuthorizationHeader());
}
}
}
Adding WithUseEmbeddedWebView did the trick.
This code works in the .NET Framework. It does not work with .NET Core
authResult = await _clientApp.AcquireTokenInteractive(_scopes)
.WithUseEmbeddedWebView(true)
.WithPrompt(Prompt.ForceLogin)
.ExecuteAsync().ConfigureAwait(false);

React native track player fails to add songs to playlist

I am working on a music player in react native and have been using the package react-native-track-player. I have so far not had a problem with the package in android. but when I try to run it on ios I get the error
You attempted to set the key0with the value
{"id":"0",
"url":{"uri":"https://urltosong.mp3"},
"artwork":"https://url_to_artwork.jpg",
"artist":"author",
"title":"song titile"
}
on an object that is meant to be immutable and has been frozen.
The code that generate the error is
async function togglePlayback() {
const currentTrack = await TrackPlayer.getCurrentTrack();
if (currentTrack == null) {
await TrackPlayer.reset();
await TrackPlayer.add(playlist); //this was never adding and die silently
await TrackPlayer.play();
} else {
await TrackPlayer.add(playlist); //adding this line the error above appeared
await TrackPlayer.play();
//console.warn(TrackPlayer.getCurrentTrack())
}
}
I am using this version of the package "react-native-track-player": "^2.0.0-rc13",
I don't know if there is something I am missing. I will appreciate your help in fixing this.
Change your track to this:
{"id":"0",
"url":"https://urltosong.mp3",
"artwork":"https://url_to_artwork.jpg",
"artist":"author",
"title":"song titile"
}
Urls should be either a string or a Resource Object

await should wait for complete the task in Dart

I am using async await in my Flutter app to wait for complete execution of first line and then execute the next line, but in my case next line is never called, I tried using debug point too, but never stopped at break point.
I am using third party library for location permission in my Flutter app, check the code snippet below.
void checkPermission() async {
PermissionStatus permission = await PermissionHandler()
.checkPermissionStatus(PermissionGroup.location);
if (permission == PermissionStatus.unknown ||
permission == PermissionStatus.denied) {
Map<PermissionGroup, PermissionStatus> permissions =
await PermissionHandler()
.requestPermissions([PermissionGroup.location]);
Scaffold.of(context)
.showSnackBar(SnackBar(content: Text('${permissions[PermissionGroup.location]}')));
if (permissions[PermissionGroup.location] == PermissionStatus.granted) {
platform.invokeMethod('locationPermissionGranted');
}
}
}

Flutter: Location package Not working on First Time App Install

My current app uses the Location package (link) to obtain the user's current latitude and longitude to be used to find nearby facilities.
This is the code I am using (similar to the example in the documentation)
Map<String, double> _currentLocation;
Map<String, double> _startLocation;
StreamSubscription<Map<String, double>> _locationSubscription;
String error;
bool _permission = false;
Location _location = new Location();
// Platform messages are asynchronous, so we initialize in an async method.
initPlatformState() async {
Map<String, double> location;
try {
_permission = await _location.hasPermission();
location = await _location.getLocation();
error = null;
} on PlatformException catch (e) {
if (e.code == 'PERMISSION_DENIED') {
error = 'Permission denied';
} else if (e.code == 'PERMISSION_DENIED_NEVER_ASK') {
error = 'Permission denied - please ask the user to enable it from the app settings';
}
location = null;
}
setState(() {
_startLocation = location;
print("Starting coordinates: ${_startLocation["latitude"]}, ${_startLocation["longitude"]}");
});
}
#override
void initState() {
super.initState();
initPlatformState();
_locationSubscription =
_location.onLocationChanged().listen((Map<String,double> result) {
setState(() {
_currentLocation = result;
print("Current coordinates: ${_currentLocation["latitude"]}, ${_currentLocation["longitude"]}");
});
});
}
The only problem I am facing is that whenever there is a fresh install of a new apk of the app, the app does not find the location after location permission has been granted.
After location has been granted I have set up a print statement to print out the user's location but for some reason it is not printing anything the first time only. After I restart the app then it prints out the location just fine.
First Time Opening After Install
After Restarting the App
Any experts that use the Location package that could help me with this problem?
According to plugin’s source code when you invoke getLocation method it asks ActivityCompat.requestPermissions to get required permission and then process. According to docs from Google:
This method functions asynchronously. It returns right away, and after the user responds to the prompt, the system calls the app's callback method with the results
, but flutter plugin has an issue about location callbacks for Android 6+ and as a workaround it is recommended to aim SDK 21.
So it seems that “native” part of this plugin doesn’t play well with Android 6+. There are two workarounds:
Set SDK to 21 version for your Android project, but I would definitely not recommend doing that.
Create some sort of “hello screen”, which will introduce the app and handle permissions there.
Meanwhile, I am really interested in what is wrong with the plugin cause its implementation seems good, so in case I’ll find how to fix it I’ll get back here.

Resources