Unity App Crashes When Sign-In via Play Games is Attempted - firebase

I have a problem that has been chasing me for the last few days.
I did all the necessary configuration to set up the Google Play Games sign-in method to my Unity game for Android.
However, every time I click the sign-in button the Play Games icon briefly appears at the top and the app suddenly crashes.
Analyzing the logcat, it seems there is an error on the highlighted line (last line of the 'InitializePlayGamesPlatform()' method) of the following script:
using GooglePlayGames;
using GooglePlayGames.BasicApi;
using UnityEngine;
public class UserManager : MonoBehaviour
{
private Firebase.Auth.FirebaseAuth firebaseAuth;
private Firebase.Auth.FirebaseUser firebaseUser;
void Start()
{
InitializePlayGamesPlatform();
}
private void InitializePlayGamesPlatform()
{
PlayGamesClientConfiguration config = new PlayGamesClientConfiguration.Builder()
.RequestServerAuthCode(false)
.Build();
PlayGamesPlatform.InitializeInstance(config);
PlayGamesPlatform.Activate();
************firebaseAuth = Firebase.Auth.FirebaseAuth.DefaultInstance;************
}
public void TrySignIn()
{
UnityEngine.Social.localUser.Authenticate((bool success) =>
{
if (!success)
{
Debug.LogError("UserManager: Failed to sign in into Play Games!");
return;
}
string authCode = PlayGamesPlatform.Instance.GetServerAuthCode();
if (string.IsNullOrEmpty(authCode))
{
Debug.LogError("UserManager: Failed to get auth code!");
return;
}
Debug.LogFormat("UserManager: auth code is: {0}", authCode);
Firebase.Auth.Credential credential = Firebase.Auth.PlayGamesAuthProvider.GetCredential(authCode);
firebaseAuth.SignInWithCredentialAsync(credential).ContinueWith(task =>
{
if (task.IsCanceled)
{
Debug.LogError("UserManager: sign-in was canceled!");
return;
}
if (task.IsFaulted)
{
Debug.LogError("UserManager: error signing in!");
return;
}
firebaseUser = task.Result;
Debug.LogFormat("UserManager: user signed in successfully: {0} ({1})", firebaseUser.DisplayName, firebaseUser.UserId);
});
});
}
public void TrySignOut()
{
firebaseAuth.SignOut();
}
}
Also, there are some other messages/errors in the logcat, such as:
Failed to read Firebase options from the app's resources. Either make sure google-services.json is included in your build or specify options explicitly.
InitializationException: Firebase app creation failed.
at Firebase.FirebaseApp.CreateAndTrack (Firebase.FirebaseApp+CreateDelegate createDelegate, Firebase.FirebaseApp existingProxy) [0x000da] in Z:\tmp\tmp.txs8ldQ514\firebase\app\client\unity\proxy
at Firebase.FirebaseApp.Create () [0x00000] in Z:\tmp\tmp.txs8ldQ514\firebase\app\client\unity\proxy\FirebaseApp.cs:119
at Firebase.FirebaseApp.get_DefaultInstance () [0x0000b] in Z:\tmp\tmp.txs8ldQ514\firebase\app\client\unity\proxy\FirebaseApp.cs:94
at Firebase.Auth.FirebaseAuth.get_DefaultInstance () [0x00000] in Z:\tmp\tmp.poUq23PLco\firebase\auth\client\unity\proxy\FirebaseAuth.cs:294
I repeated every procedure from the beginning a thousand times, searched everywhere and got no results.
I have tried running an older version of GPG plugin as well, but no success at all.
I kindly ask you to help me on this - I promise to put your names on the credits in case I publish!! :)
Thanks in advance!!

A Firebase support member (#Jesus) has just helped me to figure the issue out. The workaround is directly adding the sourceset to the mainTemplate.gradle.
I had to do the following:
Go to Project Settings > Publishing Settings > Build > checkmark Custom Main Gradle Template.
It will give you the location to the mainTemplate.gradle file.
Here is an example.
Go to the given directory and open mainTemplate.gradle with a text editor.
Add the following code to the referred file, right after lintOptions:
sourceSets { main { res.srcDirs += '<full path from your root directory to google-services.xml>' } }
Also, add this array to noCompress under aaptOptions:
noCompress = ['.unity3d', '.ress', '.resource', '.obb', 'google-services.json']
In the end, the mainTemplate.gradle should look like this:
lintOptions {
abortOnError false
}
sourceSets { main { res.srcDirs += 'C:\\Users\\USERNAME\\Documents\\Unity Projects\\GAMENAME\\Assets\\Plugins\\Android\\Firebase\\res\\values\\google-services.xml' } }
aaptOptions {
noCompress = ['.unity3d', '.ress', '.resource', '.obb', 'google-services.json']
ignoreAssetsPattern = "!.svn:!.git:!.ds_store:!*.scc:.*:!CVS:!thumbs.db:!picasa.ini:!*~"
}

in my case i have setted prograurd mode in release setting under publish settings>minify>release . Setting this to none have fixed my issue.

That particular issue is related to the google-services.json file that should be automatically included in your build if the file exists in your Assets/ directory. Note that the process is different enough from the Android process that you probably need to ignore the Firebase Android instructions and only follow the Unity ones unless you really know what you're doing.
I'll tackle the easiest problem first. Make sure you have your google-services.json file in your Assets/ directory. I usually keep mine in Assets/Data/ (which is where I also keep things like ScriptableObjects). If you don't have it, follow the instructions on the Download Firebase config file or object help page:
Get config file for your Android app Go to your the Settings icon
Project settings in the Firebase console. In the Your apps card,
select the package name of the app for which you need a config file.
Click google-services.json. Move your config file into the module
(app-level) directory of your app. Make sure that you only have this
most recent downloaded config file in your app.
Once you have that file, you'll need to make sure that Assets/Plugins/Android/Firebase/res/values/google-services.xml gets properly generated:
The reason why this exists is that the Unity SDK does not use the Google Services Gradle Plugin - doing so would break compatibility with older versions of Unity or teams that otherwise opt out of gradle integration. This is why I strongly advise against following any of the Android specific documentation unless you're willing to do a lot of work by hand.
If you do not have google-services.xml or you want to try to regenerate it, you should navigate to Assets>External Dependency Manager>Android Resolver>Force Resolve:
If this does fix your issue, then you should make sure that you enable auto-resolution to avoid this issue in the future:
There are also a few more subtle issues that crop up. One is that "External Dependency Manager for Unity" is a rename of the "Play Services Resolver" (developers kept thinking that it was tied to Play Services - which it isn't). So check that you don't have a Assets/Play Services Resolver directory. Also, if you're using the External Dependency Manager in the Unity Package Manager, make sure that Assets/External Dependency Manager doesn't exist as well. Since you're using two plugins that ship with EDM4U, you may have some duplication (although EDM4U should be smart enough to resolve that).
If you're still running into issues, it may help to share your directory layout WRT where the External Dependency Manager, google-services.json, google-services.xml, and the Firebase and the Play Games Sign-In plugin live. Also it could be worth noting if google-services.xml makes it into an exported project (or your APK). If any of this isn't something you want to share on Stack Overflow, feel free to reach out to Firebase Support and link this SO question.
--Patrick

Related

Custom logging from firebase function

I'm trying to follow this guide to put some custom logging into a firebase function. The function itself is running, and I can see the data being passed in (it's an https 'callable' function). But as soon as it hits the line where it tries to actually write that log entry, I get "Error: 7 PERMISSION_DENIED"
Since the console.log() calls write to the cloud logs, I'd assumed the firebase function has access to Cloud Logging. But perhaps it needs additional permission? I can't find any reference to where this should be set on that page though.
// Logging, logName, region, functions are provided by the surrounding app
const logging = new Logging()
const log = logging.log(logName)
const METADATA = {
resource: {
type: 'cloud_function',
labels: {
function_name: 'CustomLog',
region
}
}
};
exports = module.exports = functions.https.onCall(async data => {
const exVersion = 6
const exId = data.exId
console.log('***** exVersion:', exVersion, 'exId:', exId) // exId from caller
const entry = log.entry(METADATA, data.error) // data.error from caller
console.log('METADATA:', METADATA) // Shows in Logs Explorer
console.log('entry:', entry) // Shows in Logs Explorer
log.write(entry) // Results in Error: 7 PERMISSION_DENIED
return {
exVersion,
exId,
}
})
If I run it from the CLI using firebase function:shell, the log entry is created correctly, so I'm pretty confident the code is correct.
OK, I finally tracked it down. According to this answer, the service account used by firebase functions is {project-id}#appspot.gserviceaccount.com, and in my project, that account did not have the 'Logs Writer' role. Adding that role solves the problem.
I find it odd that the firebase functions don't need that role to log messages using console.log(), but perhaps that call is intercepted by the functions environment, and the logs are written as a different service account. It also explains why the functions running locally were able to write the logs, as they run using the 'owner' service account, which has full access.
According to the Firebase documentation page you have linked:
The recommended solution for logging from a function is to use the
logger SDK. You can instead use standard JavaScript logging calls such
as console.log and console.error, but you first need to require a
special module to patch the standard methods to work correctly:
require("firebase-functions/lib/logger/compat");
Once you have required the logger compatibility module, you can use console.log() methods as normal in your code.
Thus you might to require this library, however I am not sure this is producing your "Error: 7 PERMISSION_DENIED error, but you might also try some solutions that have worked for some members of the community.
Perhaps the logging API is not enabled in your project. You'll get a permission denied error when attempting to use it in that case.
It's a couple levels in, but the guide you linked points to
https://github.com/googleapis/nodejs-logging#before-you-begin, which includes a step to "Enable the Cloud Logging API"

Flutter Firebase Dynamic Link not working on new ios app install

I am trying to handle dynamic links within my flutter app and they work perfectly when the app is already installed. The link works fine regardless if the app is open or closed in the background. However when I try to use a link when the app is not installed I am properly brought to the app store, but then once I open the app from within the app store after the install completes it just opens the app and my dynamic link functionality never executes.
My dynamic link is similar to this:
https://startingxi.page.link/?link=https://url-redacted/game/gameId&apn=myapn&isi=mysi&ibi=myibi&st=new%20team%20vs%20&sd=1%20-%202&efr=1
I have tried with both efr=1 and without that.
My flutter code:
#override
void initState() {
super.initState();
initDynamicLinks();
futureInitState = initStateAsync();
}
Future<void> initDynamicLinks() async {
final PendingDynamicLinkData data = await FirebaseDynamicLinks.instance.getInitialLink();
final Uri deepLink = data?.link;
await processDeepLink(deepLink);
FirebaseDynamicLinks.instance.onLink(onSuccess: (PendingDynamicLinkData dynamicLink) async {
final Uri deepLink = dynamicLink?.link;
await processDeepLink(deepLink);
}, onError: (OnLinkErrorException e) async {
print('onLinkError');
print(e.message);
displayFlushBar('$e.message');
});
}
I have tried putting the initDynamicLinks within my futureInitState which is part of my FutureBuilder which does other async stuff at startup. Any help would be appreciated. I'm not sure how to debug this problem and this is my first ios/app dev experience so I'm not sure if there are some logs on the ios device that may help? But at the moment it seems as though I need to deploy to production and get my change live in the app store before I can test anything and that seems less than ideal. But since the dynamic link works in the other use cases I'm stumped on troubleshooting.
Thanks!
Common troubleshooting steps for issues on opening Firebase Dynamic Links after app install is to make sure that value of the ibi parameter matches the app's Bundle ID.
For Google-provided *.page.link domains: in the Capabilities tab of the app's Xcode project, Associated Domains shoudld be enabled, and the following is added in the Associated Domains list: applinks:{your_dynamic_links_domain}.
Also, verify that the AASA of your FDL https://{your_dynamic_links_domain}/apple-app-site-association contains an entry for your app. Verify "appID":"{team-id}.{bundle-id}" values are correct for your app
If these steps didn't work, it's best to reach out to Firebase Support as they have the tools to diagnose deeper into this issue.

UnauthorizedAccessException While trying to Clear the Cache in xamarin ios

I get an UnauthorizedAccessException while trying to perform following code in xamarin ios
documentsPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
documentsPath = Directory.GetParent(documentsPath).ToString();
InternalAppDataPath = Path.Combine(documentsPath, "Library");
InternalCacheDataPath = Path.Combine(InternalAppDataPath,"Caches");
if (Directory.Exists(InternalCacheDataPath))
{
Directory.Delete(InternalCacheDataPath, true);
}
While trying to perform the delete function UnauthorizedAccessException is thrown. How can I resolve this?
Thank You
You cannot delete that folder path as it is iOS system path. You will get unauthorized exception.
iOS does not allow a user the access directories that are not public, The only directory that you are allowed to make any changes in, Whatsoever is the document directory that is created for your application namely:
Environment.SpecialFolder.Personal
Now when you do this:
documentsPath = Directory.GetParent(documentsPath).ToString();
InternalAppDataPath = Path.Combine(documentsPath, "Library");
InternalCacheDataPath = Path.Combine(InternalAppDataPath,"Caches");
And then try to make changes into the parent folder of the document directory since you are not Authorized to access these directories it throws the UnauthorizedAccessException.
Now, this is not some sort of permission or something that you can take from the user and make this issue go, It is a restriction by Apple(In my knowledge) and hence you cannot do that.
Good luck
In case of queries feel free to revert.
In case you find some workaround do tell me about it here.

Ionic Firebase Auth Popup Window Closes Immediately

I'm following the Facebook Auth tutorial on the Firebase website. You can see it here: https://www.firebase.com/docs/web/libraries/ionic/guide.html
$scope.login = function() {
Auth.$authWithOAuthRedirect("facebook").then(function(authData) {
// User successfully logged in
}).catch(function(error) {
if (error.code === "TRANSPORT_UNAVAILABLE") {
Auth.$authWithOAuthPopup("facebook").then(function(authData) {
// User successfully logged in. We can log to the console
// since we’re using a popup here
console.log(authData);
});
} else {
// Another error occurred
console.log(error);
}
});
};
My issue is that I am correctly receiving the TRANSPORT_UNAVAILABLE error and I am getting to the following line of code
Auth.$authWithOAuthPopup("facebook").then(function(authData) {
// do stuff with the authData
})
But, when I run on my device or in emulator, the popup window that is coming from the InAppBrowser Plugin closes immediately and doesn't allow me to enter any of my credentials.
EDIT
Two things to note. First, with the above code auth does not work when done via the browser. So, if I do ionic serve and try to login nothing happens except that I see the url change briefly to http://localhost:8100/#/login&__firebase_request_key=0wRrfF07Ojg1PmJXNX1OsvrRFR2Q1LGj
but then it goes back to http://localhost:8100/#/login
Secondly, when I build the project via Xocde and run on my device, the InAppBrowser plugin seems to no longer be closing right away but instead freezes with a white screen. The logs in Xcode show the following
THREAD WARNING: ['InAppBrowser'] took '79.103027' ms. Plugin should use a background thread.
NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9802)
webView:didFailLoadWithError - -1200: An SSL error has occurred and a secure connection to the server cannot be made.
EDIT 2
Looks like the above issues with SSL error was because of an unrelated bug with upgrading to ios 9. I've since corrected those issues and now I'm back to the original. Except now the InAppBrowser window doesn't even open, I'm still hitting the catch block with TRANSPORT_UNAVAILABLE.
Not sure exactly how I fixed this issue. Hard to isolate what was breaking originally and what was breaking due to ios 9 upgrades. But, I've been able to fix the issue. I started by blowing away the /ios and /android folders inside of /platforms. I also deleted all the plugins from the /plugins folder.
Then I added back ios and android platforms. Then I added back the plugins. Then I followed the steps found in these 2 blog posts modifying your app to be ios 9 compliment.
http://blog.ionic.io/ios-9-potential-breaking-change/
http://blog.ionic.io/preparing-for-ios-9/

Location of Iron Router Code

I want to verify that I am placing my Iron Router code in the correct location. Right now I have it stored in lib/router.js, which means the code is shared on the client and server. Is that correct?
Also, some of my routes require admin status, such as:
Router.route('/manage', function () {
if ($.inArray('admin', Meteor.user().roles) > -1) {
this.render('manage');
} else {
this.render('403_forbidden');
}
});
Is that code safe in its current location? I am also interested in knowing how I can test these kinds of security holes so that I don't have to ask in the future.
Thanks
As to location of router.js ...
Yes, you want it to be available on both the client and server. So putting inside the /lib directory is fine. Really, you can put it anywhere other the the /client or /server directories.
FWIW, in most projects I've looked at, router.js is stored in the top-level project directory. Possibly this is to avoid load order issues (i.e. if the router has some dependencies on files in /lib, /client, or /server, which will generally be loaded before top-level files), or possibly its because everyone I've looked at is working off the same boilerplate code. Check out the Meteor official docs if you want to know more about load order.
As for your admin question, that route should be OK. You can test it by opening up a client side console like firebug and trying something like :
Meteor.users.update(Meteor.userId(), {$set: {roles: ['admin']}});
I believe users can only update fields in the Meteor.users.profile, so this should fail. If it doesn't, you can just add the following deny rule (in client + server);
Meteor.users.deny({
update: function() {
return true;
}
});

Resources