Google OAuth 2 - How to get refresh token using Xamarin.Auth?

0
down vote
favorite
I'm using Xamarin.Auth to authenticate the user and have access to the Google Calendar API. I'm able to get an access token and do whatever I need. The problem is the access_token I receive expires after one hour. I looked for examples and information about this in several posts but I'm still not able to ask for new tokens. Here is the code I'm using in my Xamarin.Droid Project:

public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsApplicationActivity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);

    global::Xamarin.Forms.Forms.Init(this, bundle);
    LoadApplication(new App());

    #region [GoogleAuthentication]

    if (App.auth == null)
    {
        App.auth = new GoogleAuthenticator(App.WebApplicationClientID,
        new Uri("https://www.googleapis.com/plus/v1/people/me"),
        CalendarService.Scope.Calendar);
    }

    // We don't want to have to login every time, so we'll use the Xamarin.Auth AccountStore
    AccountStore store = AccountStore.Create(this);
    Account savedAccount = store.FindAccountsForService("google").FirstOrDefault();
    if (savedAccount != null)
    {
        App.auth.Account = savedAccount;
    }
    else
    {
        App.auth.Completed += (sender, args) =>
        {
            if (args.IsAuthenticated)
            {
                // Save the account for the future
                store.Save(args.Account, "google");
            }
        };

        Intent loginIntent = App.auth.GetUI(this);
        StartActivity(loginIntent);
    }

    #endregion [GoogleAuthentication]
}

The GoogleAuthenticator class implements OAuth2Authenticator as seen below:

public class GoogleAuthenticator
: OAuth2Authenticator
{
public GoogleAuthenticator(string clientId, Uri callbackUri, params string[] scopes)
: base(
clientId,
(scopes ?? Enumerable.Empty()).Aggregate(string.Empty, (o, s) => o + " " + s),
new Uri("https://accounts.google.com/o/oauth2/auth"),
callbackUri,
null)
{
Completed += (sender, args) =>
{
Account = args.Account;
};
}

public Account Account
{
    get;
    set;
}

public void ApplyAuthenticationToRequest(HttpWebRequest request)
{
    if (Account == null)
        throw new InvalidOperationException("You must be authenticated to make requests");

    string token = Account.Properties["access_token"];
    string type = Account.Properties["token_type"];
    request.Headers[HttpRequestHeader.Authorization] = String.Format("{0} {1}", type, token);
}

}

I tried changing the authorizationUrl adding the parameters approval_prompt=force and access_type=offline but it didn't work too.

How can I get the refresh_token using Xamarin.Auth?

Thanks.

Posts

  • alberto.palberto.p ✭✭ ITMember ✭✭

    @dankern did you solve the problem ? I have the same problem me too.

  • dankerndankern BRMember

    Hi Alberto!

    I solved the problem using a WebView and requesting the information as a website. I couldn't find any other way.
    If you want I can send you some code.

    Good luck!

  • NinoPadrutt.7544NinoPadrutt.7544 ✭✭ CHMember ✭✭

    @dankern said:
    Hi Alberto!

    I solved the problem using a WebView and requesting the information as a website. I couldn't find any other way.
    If you want I can send you some code.

    Good luck!

    Hi

    I have the same issue. Is that offer with some code examples still available? :)

  • EncryptionEncryption USMember

    Hi,

    I plan to use Xamarin.Auth in my application as well. Is this a problem just when trying to reauthorize with the Googles? Or is this something that is an issue with using Xamarin.Auth?

    If so, what did you do for the work around? Can you provide a sample snippet?

    Thanks

  • ArnoHofmannArnoHofmann DEMember

    @dankern do you like to share your code?

  • NarendraSharmaNarendraSharma USMember

    hi i am facing problem in authenticating my google account in Xamarin.iOS using xamarin.Auth ,getting the error as shown in screen shot.can somebody help me?

    ss1.png 50.3K
  • D-DD-D USMember
    edited December 2016

    Did anyone solve the problem ? I have the same problem

    Thanks in Advance

  • gregory.bosquiergregory.bosquier FRMember

    For anyone looking for an answer, you should have a refresh token OAuth2Authenticator, example :

            var authenticator = new OAuth2Authenticator(
                clientId,
                null,
                Constants.Scope,
                new Uri(Constants.AuthorizeUrl),
                new Uri(redirectUri),
                new Uri(Constants.AccessTokenUrl),
                null,
                true);
    

    in Account.Properties["refresh_token"].

    Then you could do something like this to get a new access token based on refresh token :

                Dictionary<string, string> dictionary = new Dictionary<string, string>{ { "refresh_token", e.Account.Properties["refresh_token"] }, { "client_id", Constants.AndroidClientId }, {"grant_type", "refresh_token" } };
                var request = new OAuth2Request("POST", new Uri(Constants.AccessTokenUrl), dictionary, e.Account);
                var response = await request.GetResponseAsync();
    
  • MarissMariss USUniversity
    edited March 2018

    Use OAuth2Authenticator's extension method RequestRefreshTokenAsync from Xamarin.Auth.Extensions nuget package to exchange refresh token for a new access token.

    authenticator.Completed += OnAuthenticatorCompleted;
    
    await authenticator.RequestRefreshTokenAsync(refreshToken);
    
  • kirkmarplekirkmarple Member

    When using this extension method, I'm not seeing OnAuthenticatorCompleted called afterwards (or during) the call.

    I am getting a valid 'expires_in' time back of 3600 (from Google).

    Is there any other way to get back the new access token, without receiving the completed event?

    @Mariss said:
    Use OAuth2Authenticator's extension method RequestRefreshTokenAsync from Xamarin.Auth.Extensions nuget package to exchange refresh token for a new access token.

    authenticator.Completed += OnAuthenticatorCompleted;
    
    await authenticator.RequestRefreshTokenAsync(refreshToken);
    
  • MWolterbeekMWolterbeek Member

    @kirkmarple said:
    When using this extension method, I'm not seeing OnAuthenticatorCompleted called afterwards (or during) the call.

    I am getting a valid 'expires_in' time back of 3600 (from Google).

    Is there any other way to get back the new access token, without receiving the completed event?

    OnAuthenticatorCompleted is not triggered when you reuse the OAuth2Authenticator.

    Create a new OAuth2Authenticator to call the RequestRefreshTokenAsync method.

  • Jow_DowJow_Dow ✭✭ Member ✭✭

    Hello; did this solution is working.. I still have the same problem!
    after 3600sec my access_token is invalid; I have the refresh_token; but I do not know how to get a new access_token from the refresh_token?
    Does anybody have some an idea? Thanks

  • Jow_DowJow_Dow ✭✭ Member ✭✭

    @MWolterbeek said:

    @kirkmarple said:
    When using this extension method, I'm not seeing OnAuthenticatorCompleted called afterwards (or during) the call.

    I am getting a valid 'expires_in' time back of 3600 (from Google).

    Is there any other way to get back the new access token, without receiving the completed event?

    OnAuthenticatorCompleted is not triggered when you reuse the OAuth2Authenticator.

    Create a new OAuth2Authenticator to call the RequestRefreshTokenAsync method.

    Hello; is there a sample / snippet of code which is really working; I was looking around for a while but didn't find anything usable
    Thanks
    Jow

  • valentin358valentin358 Member
    edited March 4

    @Jow_Dow

    Here is how I do it :

    async void OnLoginClicked(object sender, EventArgs e)
    {
        var authenticator = new OAuth2Authenticator(
                                    Constants.CLIENT_KEY,
                                    Constants.CLIENT_SECRET,
                                    string.Empty,
                                    new Uri(Constants.OAUTH_AUTHORIZE_URL),
                                    new Uri(Constants.OAUTH_REDIRECT_URL),
                                    new Uri(Constants.OAUTH_ACCESS_TOKEN_URL),
                                    null,
                                    false);
        account = store.FindAccountsForService(Constants.APP_NAME).FirstOrDefault();
        if (account != null)
        {
            var token = account.Properties["refresh_token"];
            try
            {
                //TODO, run the following if the acces_token is expired
                authenticator.Completed += OnAuthCompleted; //This will be trigger when the request is done, with the new data
                var expiresIn = await authenticator.RequestRefreshTokenAsync(token);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }
    
    async void OnAuthCompleted(object sender, AuthenticatorCompletedEventArgs e)
    {
        var authenticator = sender as OAuth2Authenticator;
        if (authenticator != null)
        {
            authenticator.Completed -= OnAuthCompleted;
            authenticator.Error -= OnAuthError;
        }
    
        User user = null;
        if (e.IsAuthenticated)
        {
            //New refresh_token in
            Console.WriteLine(e.Account.Properties['refresh_token']);
        }
    }
    

    Regards,
    Valentin

  • Jow_DowJow_Dow ✭✭ Member ✭✭

    Dear Valentin,
    thank a lot; I used Your solution and.... it worked for me!
    Once again- thank You!

    Reagrds
    Jow_Dow

  • nefgrixisnefgrixis ✭✭ Member ✭✭

    Is anyone getting an invalid_grant exception when using the solution @valentin358 provided?

Sign In or Register to comment.