Search code examples
c#.netoauth-2.0google-api-dotnet-client

Google.Apis.Auth.OAuth2.Mvc library and refresh token


I'm trying to retrieve the reviews from our business account.

For this I'm using the Google.Apis.Auth.OAuth2.Mvc library for .net https://www.nuget.org/packages/Google.Apis.Auth.MVC/ and following this example https://developers.google.com/api-client-library/dotnet/guide/aaa_oauth#web-applications-asp.net-mvc.

The library is supposed to use the refresh token automatically but for some reason after 1 hour when the access token expires we lost access to the reviews.

Here is my implementation:

public class AppFlowMetadata : FlowMetadata
{
    private static readonly IAuthorizationCodeFlow flow =
        new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
        {
            ClientSecrets = new ClientSecrets
            {
                ClientId = "xxxxxxxxxxxxxxxxxxxxxxxxx",
                ClientSecret = "xxxxxxxxxxxxxxxxxxxxx"
            },
            Scopes = new string[] { "https://www.googleapis.com/auth/plus.business.manage" },
            DataStore = new FileDataStore(System.Web.HttpContext.Current.Server.MapPath("/App_Data/MyGoogleStorage"), true)
            //DataStore = new FileDataStore(System.Web.HttpContext.Current.Server.MapPath("/App_Data/Drive.Api.Auth.Store"))
        });

    public override string GetUserId(Controller controller)
    {
        return "our email address";
    }

    public override IAuthorizationCodeFlow Flow
    {
        get { return flow; }
    }
}
public async Task<ActionResult> IndexAsync(CancellationToken cancellationToken)
{
    var result = await new AuthorizationCodeMvcApp(this, new AppFlowMetadata()).
        AuthorizeAsync(cancellationToken);

    if (result.Credential != null)
    {
        var accessToken = result.Credential.Token.AccessToken;
        var client = new RestClient("https://mybusiness.googleapis.com/v4/accounts/116326379071192580211/locations/6608127685860731136/reviews?access_token=" + accessToken);
        var request = new RestRequest(Method.GET);
        IRestResponse response = client.Execute(request);
        GoogleReviewsModel googleReviews = Newtonsoft.Json.JsonConvert.DeserializeObject<GoogleReviewsModel>(response.Content);

        return View("Index", googleReviews);
    }
    else
    {
        return new RedirectResult(result.RedirectUri);
    }
}
public class AuthCallbackController : Google.Apis.Auth.OAuth2.Mvc.Controllers.AuthCallbackController
{
    protected override Google.Apis.Auth.OAuth2.Mvc.FlowMetadata FlowData
    {
        get { return new AppFlowMetadata(); }
    }
}

The reviews are from our own company so we don't to logins from different users. What I want to achieve is to login the first time with our company logins and then automatically refresh the access token with the refresh token so the reviews are always visible in the website,

Thanks a lot!

EDIT:

After 1 hour the response I get from the following code is this:

var accessToken = result.Credential.Token.AccessToken;
var client = new RestClient("https://mybusiness.googleapis.com/v4/accounts/116326379071192580211/locations/6608127685860731136/reviews?access_token=" + accessToken);
var request = new RestRequest(Method.GET);
IRestResponse response = client.Execute(request);

""message": "Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.\",\n "status": "UNAUTHENTICATED"

result.Credential contains both the access token and the refresh token so it seems to read the file in app_data. But the access code seems to be expired and is not being refreshed at this point and is not asking to login again neither:

var result = await new AuthorizationCodeMvcApp(this, new AppFlowMetadata()).
        AuthorizeAsync(cancellationToken);

Solution

  • You are not actually "telling the library" to refresh the access token, you are directly using the token that had been stored, and is now expired. Your code that looks like this:

    var accessToken = result.Credential.Token.AccessToken;
    

    should look like this:

    var accessToekn = await result.Credential.GetAccessTokenForRequestAsync();
    

    The GetAccessTokenForRequestAsync method will check if the access token needs refreshing and do so when needed.