'Google Calendar V3 hangs when used outside local environment

I'm working on a wrapper for the .net version of the Google Calendar API. The authentication is rather simple and working fine locally (localhost:port).

UserCredential credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
               new ClientSecrets
               {
                   ClientId = "The id of my public website",
                   ClientSecret = "The secret of my public website",
               },
               new[] { CalendarService.Scope.Calendar },
               "user",
               CancellationToken.None).Result;

            // Create the service.
            var service = new CalendarService(new BaseClientService.Initializer()
            {
                HttpClientInitializer = credential
            });

return service.Events.List("primary").Execute().Items;

The issue is, though, when the same code is deployed to production (secrets changed, of course), the application just hangs whenever I try to access the data. There's no exception, no error code, it just keeps loading forever.

Has anyone experienced this?



Solution 1:[1]

I am with you. For whatever reason, that also had happen to me. It was a calvary. The code below works for me. It is my own revision of Google API Simple Task ASP.NET sample.

Add these usings...

using System.IO;
using System.Threading;

using Google.Apis.Calendar.v3;
using Google.Apis.Calendar.v3.Data;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Auth.OAuth2.Flows;
using Google.Apis.Auth.OAuth2.Web;
using Google.Apis.Services;
using Google.Apis.Util.Store;

And these...

CalendarService service;
static string gFolder = System.Web.HttpContext.Current.Server.MapPath("/App_Data/MyGoogleStorage");

protected void Page_Load(object sender, EventArgs e)
{
    IAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow(
        new GoogleAuthorizationCodeFlow.Initializer
        {
            ClientSecrets = GetClientConfiguration().Secrets,
            DataStore = new FileDataStore(gFolder),
            Scopes = new[] { CalendarService.Scope.Calendar }
        });

    var uri = Request.Url.ToString();
    var code = Request["code"];
    if (code != null)
    {
        var token = flow.ExchangeCodeForTokenAsync(UserId, code,
            uri.Substring(0, uri.IndexOf("?")), CancellationToken.None).Result;

        // Extract the right state.
        var oauthState = AuthWebUtility.ExtracRedirectFromState(
            flow.DataStore, UserId, Request["state"]).Result;
        Response.Redirect(oauthState);
    }
    else
    {
        var result = new AuthorizationCodeWebApp(flow, uri, uri).AuthorizeAsync(UserId,
            CancellationToken.None).Result;
        if (result.RedirectUri != null)
        {
            // Redirect the user to the authorization server.
            Response.Redirect(result.RedirectUri);
        }
        else
        {
            // The data store contains the user credential, so the user has been already authenticated.
            service = new CalendarService(new BaseClientService.Initializer
            {
                ApplicationName = "Calendar API Sample",
                HttpClientInitializer = result.Credential
            });
        }
    }

}

public static GoogleClientSecrets GetClientConfiguration()
{
    using (var stream = new FileStream(gFolder + @"\client_secrets.json", FileMode.Open, FileAccess.Read))
    {
        return GoogleClientSecrets.Load(stream);
    }
}

Hope that helps.

Solution 2:[2]

I suspect the problem is that you're using AuthorizeAsync(...).Result. Using the Result property blocks the current thread... and I suspect that ASP.NET is trying to schedule the continuation (when authentication completes) in the same thread, leading to a deadlock.

Given that you're blocking anyway, is there any particular reason you want to use the AuthorizeAsync call? You'd normally use that in the context of an async method, where you'd have:

var credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(...);

(Looking at GoogleWebAuthorizationBroker, I can't see a synchronous alternative at the moment, but that's what you should probably be looking for. My guess is that GoogleWebAuthorizationBroker isn't designed to be used in this situation.)

Admittedly I don't know nearly as much as I probably should about either the ASP.NET synchronization context or the Google auth API, but hopefully explaining why there's a problem might be a good starting point... you probably want to read Stephen Cleary's article about synchronization contexts, too.

Solution 3:[3]

First of the all: See Google MVC tutorial

Step by step

1) Create method in your controller:

 public async Task<ActionResult> Test()
    {
         //your code
    }

2) Create controller :

public class AuthCallbackController : Google.Apis.Auth.OAuth2.Mvc.Controllers.AuthCallbackController
    {
        protected override Google.Apis.Auth.OAuth2.Mvc.FlowMetadata FlowData
        {
            get { return new AppFlowMetadata(); }
        }
    }

3) Configure Google Console

Solution 4:[4]

Execute() is using .Result(). This is a very bad practice that is known to cause deadlocks.

enter image description here

Simply switch to the ExecuteAsync() method and await the result.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 hewbhurtgabon
Solution 2
Solution 3 David Abaev
Solution 4 Heinzlmaen