Google Plus iOS GPGManager error "Mismatched Authentication" - ios

I have a Unity app that includes a button to sign into Google Plus. In the iOS version of the app I've had to override the default behaviour of the Google Plus SDK to prevent it switching to Safari to sign in and to use a WebView instead. This is due to Apple's policy of rejecting apps that sign into Google Plus via Safari.
That's all fine and the sign-in process works via the WebView. I've added a "Cancel" button that overlays the WebView so that users can decide not to sign in and return to the app. The code that handles the cancel button is:
- (IBAction)handleCancelClick:(id)sender {
// Cancel the sign-in process
[[GPGManager sharedInstance] signOut];
//Reactivate the sign-in button
UnitySendMessage("Persistent Services", "cancelSignIn", "");
// Hide the webview
[_unityViewController dismissViewControllerAnimated:YES completion:nil];
}
The problem is that subsequent attempts to sign in after using the cancel button fail. I see the following error when debugging in XCode:
2015-04-29 21:33:05.328 [Core] (Error) -[GPGManager finishedWithAuth:error:]:[main]
FAILED LOGGING INTO GOOGLE PLUS GAMES Error Domain=com.google.GooglePlusPlatform
Code=-1 "Mismatched authentication" UserInfo=0x1c163870
{NSLocalizedDescription=Mismatched authentication}
As I say, this only happens after the cancel button has been used. If the sign-in process goes through in a single flow, it works.
The Unity code that's called when the sign-in button is pressed is:
public void logIn () {
Debug.Log ("Attempting to log in");
signingIn = true;
Social.localUser.Authenticate ((bool success) =>
{
if (success) {
...
} else {
Debug.Log ("Failed to Log in to Google Play Services");
}
signingIn = false;
});
}
I'm assuming that the first time Social.localUser.Authenticate() is called, some state is set up that isn't overwritten on subsequent calls. When the sign-in process finishes it then checks the callback auth code against what was set on the first call to Authenticate() and they don't match. But I may be way off the mark.
Can you suggest a way of completely resetting the sign-in process when cancelling or of otherwise resolving this problem?

I got around the problem by making sure Social.localuser.Authenticate() isn't called a second time. Subsequent presses of the 'sign-in' button just cause the WebView to be redisplayed.
To achieve this, I set a flag in the Unity method called from Objective-C code when the 'Cancel' button is pressed.
#if UNITY_IOS
/**
* Currently only called from Native iOS code.
*/
public void cancelSignInFromIos() {
hasCancelledInIos = true;
}
#endif
I then wrap the sign-in code in a conditional statement that checks the value of this flag. If the 'Cancel' button has been pressed, I send a message back to native code to instruct it to display the WebView.
if (!hasCancelledInIos)
Social.localUser.Authenticate ((bool success) =>
{
...
}
} else {
#if UNITY_IOS
hasCancelledInIos = false;
fromUnityshowWebView ();
#endif
}
The native method is defined in Unity-side code like this:
#if UNITY_IOS
System.Runtime.InteropServices.DllImport("__Internal")]
extern static public void fromUnityshowWebView();
#endif
And is implemented in native code like this:
extern "C" {
void fromUnityshowWebView() {
[[NSNotificationCenter defaultCenter] postNotificationName:ApplicationOpenGoogleAuthNotification object:#"blank"];
}
}
The notification fired here is handled by a method that displays the WebView overlay e.g.
- (void) showWebView
{
[unityViewController presentViewController:self.webViewController animated:YES completion:nil];
}
Long-winded, but it works.

Related

Hide Activity Indicator when firebase authentication redirects to recapcha window (iOS - Swift)

I have implemented a Phone Authentication in my iOS app using Firebase, In that case, I have added an activity indicator to show a loading window. But, in some cases a ReCaptcha appears and, since the activity indicator is still in the window, the ReCaptcha cannot be completed, which makes the app freeze in one state.
Below is my code for login:
let activityIndicator = EAActivityIndicator()
activityIndicator.show()
Auth.auth().languageCode = "en"
PhoneAuthProvider.provider().verifyPhoneNumber(mobileNumber, uiDelegate: nil) { verificationId, error in
activityIndicator.hide()
if let error = error {
// handle error
} else {
self.navigationController?.pushViewController(ViewController(), animated: true)
}
}
As in the code, I show an activity indicator when the login button is tapped, if the mobile number is correct and if a 6 digit code is sent it will redirect to the Code entering View Controller. In certain cases, a ReCaptcha appears, but since the activity indicator is still on the screen, the ReCaptcha cannot be completed.
Screenshots:

How to get initial link for iOS using react native firebase dynamic links?

I am using react-native-firebase:: 5.6.0, I am having issue while getting initial link for iOS device. On android it's working fine. I am using "Firebase Dynamic Links" to redirect user inside login screen of my app if in case he is not logged in inside app, otherwise just opening app if he is already logged in.
It's working for android app but having an issue with ios app. I have used two function one is the get dynamic link if app is closed "getInitialLink" and another one is to check when app is opened "onLink".
This is function I am using after closing splash screen, only called once when opening app from closing state.
firebase.links().getInitialLink().then((url) => {
if (url && url === 'https://mycustomdomain.co.in') {
navigationToScreen(AUTH, INITIAL_SCREEN);
} else {
// INITIALIZE APP HERE
}
});
If app already opened I am getting dynamic link url value inside this function::
this.unsubscribeHandleOpenAppDynamicLinks = firebase.links().onLink(async (url) => {
let isLoggedIn = await AsyncStorage.getItem(LocalStorageKeys.IS_LOGGEDIN);
if (url) {
if ( isLoggedIn !== 'yes' && url === 'https://mycustomdomain.co.in') {
navigationToScreen(AUTH, INITIAL_SCREEN);
}
}
});
and clearing that listener on componentWillUnmount:: this.unsubscribeHandleOpenAppDynamicLinks();
In case of iOS only "onLink" function is working and I am
getting url value as "undefined". getInitialLink() function will
returns the URL that the app has been launched from. If the app was
not launched from a URL the return value will be null, but I am
getting "undefined" even when launching an app from url in case of iOS
only. I am getting url inside onLink() in case of iOS when app is
launched. Why this is happening??
Please suggest what I am doing wrong here.
If getInitialLink method does not work, it is either because of improper linking or due to Expo runtime. As an alternative, use Linking.getInitialURL method to get the initial URL. This requires a little bit of native code as well. This is because Linking module does not know how to interpret the shortened URL. So, we call the resolveShortLink method of Firebase SDK to get the embedded deep link. Once we receive the embedded deep link, we can handle it as usual in our app.
The native source code is documented in this article. But for completeness, I will post it here.
#import "FDLResolver.h"
#import <React/RCTLog.h>
#implementation FDLResolver
RCT_EXPORT_MODULE();
RCT_EXPORT_METHOD(resolveShortLink:(NSString *)shortLink :(RCTPromiseResolveBlock)resolve
:(RCTPromiseRejectBlock)reject)
{
id completion = ^(NSURL *_Nullable dynamicLink, NSError *_Nullable error) {
if (!error && dynamicLink) {
resolve(dynamicLink.absoluteString);
} else {
reject(#"Error", #"Error in getting dynamic link", error);
}
};
NSURL *shortLinkURL = [NSURL URLWithString:shortLink];
[[FIRDynamicLinks dynamicLinks] resolveShortLink:shortLinkURL completion:completion];
}
#end
And Linking module code is below.
Linking.getInitialURL().then(url => {
if (url && url.includes('page.link')) {
const shortLink = url.replace('exps', 'https')
NativeModules.FDLResolver.resolveShortLink(shortLink)
.then(link => {
const linkParts = link.split('?')
const query = qs.parse(linkParts[1])
this.parseRouteUrl(query.deep_link_id)
})
}
})

blank page after recaptcha verification Authenticate with Firebase on iOS using a Phone Number Swift

After recaptcha verification, page only returned blank. It did nothing to do next step.
Screen Shot
In your app delegate's application(_:open:options:) method, call Auth.auth().canHandle(url).
For the blank re-captcha page issue I was able to resolve it by doing these 3 things:
1st thing-
Inside the GoogleSerivce-Info.plist file make sure the REVERSED_CLIENT_ID is added to your project via the URL types using this. Follow the first part of the second step there: Add custom URL schemes to your Xcode project (look at the screenshot).
2nd thing-
In the project navigator select the blue project icon
Select Capabilities
Open Background Modes
Select Background fetch
3rd thing-
Before verifying the phone number call PhoneAuthProvider.provider(auth: Auth.auth())
#IBAction func phoneButton(sender: UIButton) {
// ***step 5***
PhoneAuthProvider.provider(auth: Auth.auth())
PhoneAuthProvider.provider().verifyPhoneNumber(phoneNumberTextField.text!, uiDelegate: nil) {
(verificationID, error) in
if let error = error {
print(error.localizedDescription)
return
}
guard let verificationId = verificationID else { return }
// do something with verificationID
}
}
On iOS, the appVerificationDisabledForTesting setting has to be set to TRUE before calling verifyPhoneNumber. This is processed without requiring any APNs token or sending silent push notifications in the background, making it easier to test in a simulator. This also disables the reCAPTCHA fallback flow.
Firebase Docs
I face this issue and fix it by adding this code into my AppDelegate.m
- (BOOL) application: (UIApplication *) app
openURL: (NSURL *) url
options: (NSDictionary <UIApplicationOpenURLOptionsKey, id> *)
options {
if ([[FIRAuth auth] canHandleURL: url]) {
return YES;
} else {
// URL not auth related, developer should handle it.
return NO;
}
}

Cordova iOS with Spotify iOS SDK - Trigger Auth

I'm just developing Web Apps based on Cordova, but I have a problem: I want to include Spotify in a new App.
Spotify has the iOS SDK (beta) with a beginner Tutorial. That worked fine (On App load Start the Auth).
Now I would like to implement that in my WebApp using Cordova.exec(); (Not on load - I would like to Auth on Button Click (Triggered by JavaScript).
I've generated a Cordova Plugin for that - that worked. And i can trigger a Method via Cordova.exec();.
This Method get triggered:
- (BOOL)startSpotifyAuth:(CDVInvokedUrlCommand*)command {
// Create SPTAuth instance; create login URL and open it
NSURL *loginURL = [[SPTAuth defaultInstance] loginURLForClientId:kClientId declaredRedirectURL:[NSURL URLWithString:kCallbackURL] scopes:#[#"login"]];
// Opening a URL in Safari close to application launch may trigger an iOS bug, so we wait a bit before doing so.
// [UIApplication performSelector:#selector(openURL:) withObject:loginURL afterDelay:0.1];
NSLog(#"*** GOT THIS IN DEBUG CONSOLE ***");
// Ask SPTAuth if the URL given is a Spotify authentication callback
if ([[SPTAuth defaultInstance] canHandleURL:loginURL withDeclaredRedirectURL:[NSURL URLWithString:kCallbackURL]]) {
NSLog(#"*** GOT THIS - NOT - IN DEBUG CONSOLE ***");
// Call the token swap service to get a logged in session
[[SPTAuth defaultInstance] handleAuthCallbackWithTriggeredAuthURL:loginURL tokenSwapServiceEndpointAtURL:[NSURL URLWithString:kTokenSwapURL] callback:^(NSError *error, SPTSession *session)
{
if (error != nil) {
NSLog(#"*** Auth error: %#", error);
return;
}
// Call the -playUsingSession: method to play a track
[self playUsingSession:session];
}];
return YES;
}
return NO;
}
As you can see by the Debug Outputs: I did not get inside the if(). But I don't know why: The loginURL looks correct.
You're using the wrong URL in your if statement. At that point, you need to validate the URL that gets handed to your application after the user has been bounced out to Safari for authentication, NOT the one you generate using SPAuth.
Are you still having issues with your project? Maybe my Spotify iOS SDK plugin can help. I just published the first version to the plugin registry.
You can install the plugin via the cordova command line client: cordova plugin add com.timflapper.spotify.
Add the ios platform if you haven't already done so: cordova platform add ios.
The following code is a simple example of how to authenticate with Spotify and play a single track:
var session, player;
var urlScheme = 'your-custom-url-scheme';
var clientId = 'your-own-client-id';
function onDeviceReady() {
spotify.authenticate(urlScheme, clientId, 'token', authDone);
}
function authDone(error, sess) {
if (error) return console.log("ERROR!", error);
console.log(sess);
session = sess;
player = spotify.createAudioPlayer(clientId);
player.login(session, function(error) {
if (error) return console.log(error);
player.play('spotify:track:2DlfLPbXH5ncf56Nytxd4w', function(error) {
if (error) return console.log(error);
});
});
}
document.addEventListener('deviceready', onDeviceReady, false);

Unity Facebook SDK FB.Feed not posting

I'm using the Unity Facebook SDK for my iOS game but FB.Feed isn't working. Using Unity 4.2.2 and Facebook SDK 4.2.4
Here's what happens:
Upon Game Start, FB init is called
void Awake () {
enabled = false;
FB.Init(SetInit, OnHideUnity);
}
private void SetInit()
{
enabled = true; // "enabled" is a magic global
FB.GetAuthResponse(LoginCallback);
}
private void OnHideUnity(bool isGameShown)
{
if (!isGameShown)
{
Time.timeScale = 0;
}
else
{
Time.timeScale = 1;
}
}
void LoginCallback(FBResult result)
{
Debug.Log("call login: " + FB.UserId);
FbDebug.Log("call login: " + FB.UserId);
Debug.Log("login result: " + result.Text);
FbDebug.Log("login result: " + result.Text);
if (result.Error != null)
{
Debug.LogError(result.Error);
return;
}
}
When I click the button for sharing on facebook, the following function is called:
public void FBShare() {
if(!FB.IsLoggedIn)
FB.Login("email,publish_actions", LoginCallback);
FB.Feed(
linkName: "My Game",
linkDescription: "Just played My Game. It's awesome!",
linkCaption: "I just smashed 4" + " friends! Can you beat it?",
picture: "http://www.friendsmash.com/images/logo_large.jpg",
callback: LogCallback
);
}
If I'm not logged in, then I get a login prompt on facebook (switching to the FB app)
If I haven't authorized the app, then I get a prompt for that as well.
And then the Facebook Share Feed appears with all the specified information I put in the function, all the previous steps working properly.
The problem starts once I touch the post button. The app starts uploading my post but then abruptly switches back to the game.
Tearing my hair out for the past 3 days. Can't find anything to help. Logs from the callback functions are return empty responses.
Login is an asynchronous function. Once you call it it will multitask out of your app and try to authenticate. You are immediately calling Feed after calling login. Feed will also try to multitask out of your app. You make a custom login callback for this case and call feed from inside of it.
I finally figured it out. The problem was an incorrect bundle ID on the Facebook app online, the changes took some time to propagate to all FB servers so it wasn't immediately apparent what was the problem.

Resources