Xamarin.Auth with Google APIs: Renew credentials?

DanBelcherDanBelcher USMember ✭✭

I'm trying to use Xamarin.Auth with the Google-APIs to login to Google and access Drive. I've managed to get nearly everything working, but the authentication tokens seem to expire after about an hour. Everything works great for awhile, but after about an hour, when I attempt access, I get an Invalid Credentials [401] error and a leak:

Google.Apis.Requests.RequestError
Invalid Credentials [401]
Errors [
    Message[Invalid Credentials] Location[Authorization - header] Reason[authError] Domain[global]
]
: GoogleDriveAgent: FetchRemoteFileList() Failed! with Exception: {0}
[0:] Google.Apis.Requests.RequestError
Invalid Credentials [401]
Errors [
    Message[Invalid Credentials] Location[Authorization - header] Reason[authError] Domain[global]
]
: GoogleDriveAgent: FetchRemoteFileList() Failed! with Exception: {0}

objc[37488]: Object 0x7f1530c0 of class __NSDate autoreleased with no pool in place - just leaking - break on objc_autoreleaseNoPool() to debug
objc[37488]: Object 0x7f151e50 of class __NSCFString autoreleased with no pool in place - just leaking - break on objc_autoreleaseNoPool() to debug
...more leaks.

I'd like to make sure I'm using Xamarin.Auth and the Google APIs as intended, so here is my code:

In my GoogleDriveService class, I've got an account store and a saved account:

        AccountStore Store { 
            get {
                if (m_store == null)
                    m_store = AccountStore.Create ();

                return m_store;
            }
        }

        Account SavedAccount { 
            get {
                var savedAccounts = Store.FindAccountsForService ("google");
                m_savedAccount = (savedAccounts as List<Account>).Count > 0 ? (savedAccounts as List<Account>) [0] : null;

                return m_savedAccount;
            }
        }

I initialize a session and start the service:

        void InitializeSession ()
        {
            Authenticator = new GoogleAuthenticator (ClientID, new Uri (RedirectUrl), GoogleDriveScope);
            Authenticator.Completed += HandleAuthenticationCompletedEvents;

            if (SavedAccount != null) {
                Authenticator.Account = SavedAccount;
                StartService ();
            }

            UpdateSignInStatus ();
        }

        bool StartService ()
        {
            try {
                Service = new DriveService (Authenticator);
                return true;
            } catch (Exception ex) {
                // Log exception
                return false;
            }
        }

...and respond to authentication completed events:

        void HandleAuthenticationCompletedEvents (object sender, AuthenticatorCompletedEventArgs e)
        {
            if (e.IsAuthenticated) {  // Success
                UpdateSignInStatus();
                Store.Save (e.Account, "google");
                Authenticator.Account = e.Account;
                StartService();
                LoginController.DismissViewController(true, null);
            } else {                                    // Cancelled or no success
                UpdateSignInStatus();
                LoginController.DismissViewController(true, null);
                LoginController = null;
                InitializeSession ();  // Start a new session
            }
        }

Again, everything works fine, for awhile, but then the authentication expires. I understand that it should, but I thought the credentials saved in the AccountStore ought to still work.

In the Xamarin.Auth getting started docs, it says that calling Save again will overwrite the credentials and that "This is convenient for services that expire the credentials stored in the account object." Sounds promising...

So I tried another approach: having an IsSignedIn property that always overwrites the credentials in the getter...

        public bool IsSignedIn { 
            get {
                if (Authenticator == null) {
                    m_isSignedIn = false;
                    return m_isSignedIn;
                }

                if (Authenticator.Account != null) {
                    Store.Save (Authenticator.Account, "google"); // refresh the account store
                    Authenticator.Account = SavedAccount;
                    m_isSignedIn = StartService ();
                } else {
                    m_isSignedIn = false;
                }

                return m_isSignedIn;
            }
        }

...and then I access IsSignedIn before any API calls (Fetching metadata, downloading, etc). It doesn't work: I'm still getting expired credentials errors shown above.

Is this a case of needing to refresh the token? What am I doing wrong? I must be confused. Any help would be appreciated.

Posts

Sign In or Register to comment.