Facebook access token not persisted after reastart in v4a - bug?

Hi,

Using the facebook plugin v4, the access token that you get from facebook when logging in is persisted so that the user remains logged in even after app restart. The access token is then available by calling facebook.getCurrentAccessToken()

In v4a, this does not work. If the user logs in, he still get an access token but if the app is restarted,  calling facebook.getCurrentAccessToken() returns nil. I have tested this by using the same code and just switching back and forth between the two plugin versions. 

Has the way the facebook access token is persisted changed from v4 to v4a? I cannot find anything in the docs that would suggest that. Or is this a bug in v4a? 

Can you put together a simple demo doc that shows this issue and use the Report a bug link at the top of the page?

The project should be a .zip file with at least a main.lua, config.lua and build.settings and any other files/images, etc. needed to build and run the project.

Thanks

Rob

@Rob: I have created a bug report and submitted it.

Question: after clikcing on submit, I was just redirected to the Account page but there was no confirmation or anything that the bug report was submitted. Could you please just check that there is a bug report with the same name as this thread submitted about the same time as this post?

How will I know when it is fixed? Do I have to look through the release notes of all daily build from now on?

Thanks!

I saw ticket go into our support system. If you linked to this thread, we will try to update here after it’s fixed, but release notes are the most likely where it would be made public.

Rob

I use this exact schema without any issues using v4a in my games.  In fact, I pushed for the changes in the first place as the v4 plugin was causing me a massive amount of ANRs.

Facebook access tokens now last for 60 days so you should get the cached access token from Facebook SSO.

I can confirm this works on iOS, Android and Kindle devices.

@Sphere Game Studios: that really interesting! Actually I was wondering how such a basic feature could be a bug in a module as popular as the facebook plugin. Below, you basically have the code that I attached in the bug report in order to reproduce the problem. Can you see what I am doing wrong?

local Main = {} local facebook = require("plugin.facebook.v4a") local json = require("json") local accessToken local facebookListener = {} local loginFacebook = {} function Main() facebook.init(loginFacebook) -- Testing v4a -- timer.performWithDelay(2000, function() loginFacebook() end, 1) -- Testing v4 end function loginFacebook() if (facebook.isActive) then print("Start log in sequence") accessToken = facebook.getCurrentAccessToken() if (accessToken == nil) then print("Access token is nil, logging in") facebook.login(facebookListener) else print("Already logged in") end end end function facebookListener(event) if (event.type == "session") then if (event.phase == "login") then print("Login successful") accessToken = event.token if (accessToken ~= nil) then print("Access token after logging in: " .. accessToken) facebook.request("me") end elseif (event.phase == "logout") then print("Logged out") elseif ((event.phase == "loginFailed") or (event.phase == "loginCancelled")) then print("Login failed or cancelled") end elseif (event.type == "request") then if (not event.isError) then local response = json.decode(event.response) if (response.id ~= nil) then print("Request ok") else print("No id received") end else print("Error at request") end end end Main()

I’m wondering if you have a timing issue…  You are checking the access token in the init() callback - which I do not do.

I call init() and in the callback for that I check for event.name == “fbinit”.  Once I get that I set a flag to indicate that the Facebook library has initialised.

A few seconds later I then check for a valid access token and if there isn’t one I then call facebook.login().

Try recoding to split the init() and login() events instead of chaining them and see if you have more luck that way.

@Sphere Game Studios:

To test your theory, I wrapped the contents of the function loginFacebook() inside a timer set to 10 seconds. In other words, from the moment the facebook plugin has been initialized, it takes 10 seconds before an attempt to retrieve the access token to check if the user is logged in or not. And it worked!

Still, I not sure that this is “correct”… The whole point of facebook.init() seems to be that a specific function is called after initialization is complete (the exact word used in the docs). However, it now seems that this does not mean at all that the facebook plugin is 100% ready to use since e.g. the access token still is not accessible. To me, complete means that “all systems are go”, not that some features still need some time before they are ready to use. I understand if there are technical reasons for this, but from a logical standpoint, I would argue that this is wrong…

Since there are features of the app that are visible from start and which are dependent on the logged in status of facebook, I need to know as soon as possible if the user is logged in or not. How many seconds should I wait (you mention “a few”)?

@Rob

Given my argumentation above, I still consider this a bug but if you or your engineers can confirm that this is by design and not a bug, please let me know so that I don’t wait for a bugfix for this.

Maybe I should clarify that I also added a check for the "fbinit"event in the login function but that did not change anything. The only thing that made it work was to add the delay .

Prior to .v4a, there was no .init() API call. We started the initialization call when you required the plugin. On iOS this completed in a synchronous manner, that is before the require finished, the initialization was complete. However on Android, in particular with slow network connections, since we were waiting for it to complete, Google would see those delays and decide that your Android app was Not Responding (ANR).

Because fixing this was going to be a breaking change, we decided to leave .v4 alone and create a new .v4a plugin.  This plugin now explicitly requires you to call a .init() function.  On iOS, it’s pretty much a no-op because it still completes its process during the require and is there for code consistency with Android. On Android, we still start the initialization during the require. The .init() process lets you set up a call back listener that lets you know when it’s done.  

So the results of this is that it’s safe to call .login() on iOS immediately, but on Android, you should wait until the listener function is called indicating that the initialization is complete.

Now, all that said, on iOS, Apple highly recommends that you delay logins to things like GameCenter, Facebook, etc. until you actually need it. The reason for this is when a user is hit with multiple logins before they ever get to experience the app they are likely to close it and never use it again. This makes sense on Android too. Most logins are not silent and are disruptive.  If you follow this recommended process, you won’t run into timing issues since your init should be done before a user can ever interact with a “Login” button.

I concur with Apple’s philosophy. Even if you need a unique ID and you want to use the Facebook UserID for this, you can get them through a tutorial or something before you require the login. Once you have it, store it and then you will have it when you need it.

Rob

@Rob:

I’m not sure that I understand fully…  You say that it i safe to call .login() on iOS immediatelly but that is exactly the opposite of what I see when running the code above. In fact, I argue that it is not even safe to call .login() after the function specified in .init() has been called and if you run the sample code in the bug report you will see what I mean. It only works when you add a slight delay of a few seconds. This is also what Sphere Game Studios argues (unless I have misunderstood that post). So again, even when after the callback funtion specified in init() has been called, not all functions in the facebook plugin are ready to be used (like .getCurrentAccessToken()) . That is the (possible) bug that I have reported in my bug report.

The reason that I need to know directly in the app whether the user is logged in or not is that I need to fetch several items from a backend service in the cloud . That service only allows requests if the user is logged in. To log in, I need the facebook access token and that is only accessible by calling .getCurrentAccessToken(). Sure, I can change the “anatomy” of the app so that the user sees something else first, but that seems more like a workaround than a solution. I could also store the access token manually in the app (in a db) but then I might run into problems when the cloud service talks to facebook and sees that the accessToken is no longer valid.

Just to make it clear: is the call .getCurrentAccessToken() supposed to be ready to use directly after the callback function specified in .init() has been called or not?  If it is, then the current behavior (as experienced by both Sphere Game Studios and me) is a bug. Otherwise, please explain what it means in the docs when it says that the callback function specified in .init() is called “once initialization of the Facebook SDK is complete” when in fact it is not entirely complete. 

I do agree with you that the callback listener from the facebook.init() call should only fire when the entire plugin is “ready for action” and a delay should not have to be implemented in our code.

After all, you wouldn’t have a transition onComplete() firing if the transition was not complete. 

Once the fbinit event fires, you should be able to access any Facebook plugin API. If that’s not happening, we need a filed bug report to get an engineer to look into it.

Rob

@Rob: I submitted a bug report five days ago, please see my post above. Any estimate how long it might take before a fix is released?

@Rob:

Since my bug report concerns a plugin and not Corona itself, I am not sure if I can see in the release notes if/when this bug has been fixed. Also, in many cases it just says “Maintenance”, which I guess can mean anything. In another post concerning another bug report that I filed, you informed me that you try to send an email to the person who filed a bug report when it is done but that this was not always the case and that the release notes was the best way to find out.

To make the whole “lifecycle of bug reporting” more transparent and more predictable, would it be possible to have a more structured approach to this? Leaving developers to browse through release notes or even just let them download new versions every now and then to test if it has been fixed may not be an optimal solution. I definitely understand stand there are financial contraints to what kind of services you can offer but I think this could perhaps be handled more professionally without having to implement costly solutions. All I am asking for is some “formalized” way of providing simple feedback to bug submitters (or everyone) about their bug reports, perhaps even a precursory estimation of how long it will take. I definitely think that it could lift Corona even higher up the “maturity ladder” and it would definitely make it easier for the developers to plan their work. Some quick alternatives from the top of my head:

  • Commit to always sending emails to the bug reporter the bug has been fixed or rejected. If possible, also an email a few days after submission to give an estimated time frame.
  • Maintain a list on your website of all submitted bugs together with estimated “fixed dates” and current status (unless of course the bug report is considered sensitive e.g. due to security concerns etc.) 

I’m not criticizing, just trying to be constructive making Corona and the eco system of products and services around it even better than they already are.  :slight_smile:

Can you put together a simple demo doc that shows this issue and use the Report a bug link at the top of the page?

The project should be a .zip file with at least a main.lua, config.lua and build.settings and any other files/images, etc. needed to build and run the project.

Thanks

Rob

@Rob: I have created a bug report and submitted it.

Question: after clikcing on submit, I was just redirected to the Account page but there was no confirmation or anything that the bug report was submitted. Could you please just check that there is a bug report with the same name as this thread submitted about the same time as this post?

How will I know when it is fixed? Do I have to look through the release notes of all daily build from now on?

Thanks!

I saw ticket go into our support system. If you linked to this thread, we will try to update here after it’s fixed, but release notes are the most likely where it would be made public.

Rob

I use this exact schema without any issues using v4a in my games.  In fact, I pushed for the changes in the first place as the v4 plugin was causing me a massive amount of ANRs.

Facebook access tokens now last for 60 days so you should get the cached access token from Facebook SSO.

I can confirm this works on iOS, Android and Kindle devices.

@Sphere Game Studios: that really interesting! Actually I was wondering how such a basic feature could be a bug in a module as popular as the facebook plugin. Below, you basically have the code that I attached in the bug report in order to reproduce the problem. Can you see what I am doing wrong?

local Main = {} local facebook = require("plugin.facebook.v4a") local json = require("json") local accessToken local facebookListener = {} local loginFacebook = {} function Main() facebook.init(loginFacebook) -- Testing v4a -- timer.performWithDelay(2000, function() loginFacebook() end, 1) -- Testing v4 end function loginFacebook() if (facebook.isActive) then print("Start log in sequence") accessToken = facebook.getCurrentAccessToken() if (accessToken == nil) then print("Access token is nil, logging in") facebook.login(facebookListener) else print("Already logged in") end end end function facebookListener(event) if (event.type == "session") then if (event.phase == "login") then print("Login successful") accessToken = event.token if (accessToken ~= nil) then print("Access token after logging in: " .. accessToken) facebook.request("me") end elseif (event.phase == "logout") then print("Logged out") elseif ((event.phase == "loginFailed") or (event.phase == "loginCancelled")) then print("Login failed or cancelled") end elseif (event.type == "request") then if (not event.isError) then local response = json.decode(event.response) if (response.id ~= nil) then print("Request ok") else print("No id received") end else print("Error at request") end end end Main()