3

I am using the latest version of the Google .NET Client API (v1.81). I am trying to connect to the Calendar Service using the following code

    var calendarService = new CalendarService(new BaseClientService.Initializer
    {
        HttpClientInitializer = GetCredential(),
        ApplicationName = "MyApp"
    });

    public UserCredential GetCredential()
    {
        UserCredential credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
            new ClientSecrets { ClientId = _clientId, ClientSecret = _clientSecret },
            new[] { CalendarService.Scope.Calendar }.ToList(), "user", CancellationToken.None,
            new FileDataStore("Drive.Auth.Store")).Result;

        return credential;
    }

When I do this it opens a new Browser Window and asks for me to authenticate. I have previously authenticated and the key is stored and retrieved via FileDataStore.

Why would it be asking me to authenticate again? The problem is because this code needs to run in a background service so it can't open browser tabs.

Another issue I now am getting, when I try and authorise it opens up a new browser window, I select my credentials but the login page then tells me

"The redirect URI in the request: http://localhost:51773/authorize/ did not match a registered redirect URI"

Why is it trying to use the Authorize url and not the AuthCallbackController. It also seems to randomly change the port number of the redirect uri making registering it impossible.

The ultimate question probably is, if I have a web app and I want the users to sign in using their credentials via the web page and then reuse those credentials at a later date in a background task on the server, how do I go about that? I couldn't find any relevant sample app to show how. It appears the FileDataStore does not store the credentials so they can be reused. I have also implemented my own DataStore which saves them in the database which doesn't seem to work either.

Digging further into this it appears I need offline_access. I have implemented this the same as Google Analytics OAuth with AccessType = Offline in C# but it still prompts. How to I reuse the offline_access refresh token?

Community
  • 1
  • 1
Craig
  • 36,306
  • 34
  • 114
  • 197
  • Does it happen every time you call GetCredential? Are you sure that the credential was saved in the datastore? Can you check that the file exists in explorer? – peleyal May 15 '14 at 12:19
  • Yes, I am sure it is saved. – Craig May 15 '14 at 20:52
  • "The redirect URI in the request: http://localhost:51773/authorize/ did not match a registered redirect URI" - If you use Web Application, Take a look at: https://developers.google.com/api-client-library/dotnet/guide/aaa_oauth#web_applications, did you set the redirect URI to your_site/AuthCallback/IndexAsync on the developers console. If not, you need to create a native application on the developers console (and not a web application). – peleyal May 16 '14 at 17:23
  • I will check the developers console to make sure it is set. I did try the console apps in the demos but they still opened up a web browser. This would not be good if the app is running as a background task (no UI). – Craig May 17 '14 at 07:37

1 Answers1

2

You are correct you need to create your own imp implementation of Idatastore. This will allow you to save your refresh token to the database and use it later.

Here is a very rough example

/// 
 /// Saved data store that implements . 
 /// This Saved data store stores a StoredResponse object.
 /// 
    class SavedDataStore : IDataStore
    {
        public StoredResponse _storedResponse { get; set; }
        /// 
        /// Constructs Load previously saved StoredResponse.
        /// 
        ///Stored response
        public SavedDataStore(StoredResponse pResponse)
        {
            this._storedResponse = pResponse;
        }
        public SavedDataStore()
        {
            this._storedResponse = new StoredResponse();
        }
        /// 
        /// Stores the given value. into storedResponse
        /// .
        /// 
        ///The type to store in the data store
        ///The key
        ///The value to store in the data store
        public Task StoreAsync(string key, T value)
        {
            var serialized = NewtonsoftJsonSerializer.Instance.Serialize(value);
            JObject jObject = JObject.Parse(serialized);
            // storing access token
            var test = jObject.SelectToken("access_token");
            if (test != null)
            {
                this._storedResponse.access_token = (string)test;
            }
            // storing token type
            test = jObject.SelectToken("token_type");
            if (test != null)
            {
                this._storedResponse.token_type = (string)test;
            }
            test = jObject.SelectToken("expires_in");
            if (test != null)
            {
                this._storedResponse.expires_in = (long?)test;
            }
            test = jObject.SelectToken("refresh_token");
            if (test != null)
            {
                this._storedResponse.refresh_token = (string)test;
            }
            test = jObject.SelectToken("Issued");
            if (test != null)
            {
                this._storedResponse.Issued = (string)test;
            }
            return TaskEx.Delay(0);
        }

        /// 
        /// Deletes StoredResponse.
        /// 
        ///The key to delete from the data store
        public Task DeleteAsync(string key)
        {
            this._storedResponse = new StoredResponse();
            return TaskEx.Delay(0);
        }

        /// 
        /// Returns the stored value for_storedResponse      
        ///The type to retrieve
        ///The key to retrieve from the data store
        /// The stored object
        public Task GetAsync(string key)
        {
            TaskCompletionSource tcs = new TaskCompletionSource();
            try
            {
                string JsonData = Newtonsoft.Json.JsonConvert.SerializeObject(this._storedResponse);
                tcs.SetResult(Google.Apis.Json.NewtonsoftJsonSerializer.Instance.Deserialize(JsonData));
            }
            catch (Exception ex)
            {
                tcs.SetException(ex);
            }
            return tcs.Task;
        }

        /// 
        /// Clears all values in the data store. 
        /// 
        public Task ClearAsync()
        {
            this._storedResponse = new StoredResponse();
            return TaskEx.Delay(0);
        }

        ///// Creates a unique stored key based on the key and the class type.
        /////The object key
        /////The type to store or retrieve
        //public static string GenerateStoredKey(string key, Type t)
        //{
        //    return string.Format("{0}-{1}", t.FullName, key);
        //}
    }

now instead of calling filedatastore you will call savedDataStore

 //Now we load our saved refreshToken. Probably from the DB someplace but this works
  StoredResponse myStoredResponse = new StoredResponse(tbRefreshToken.Text);
 // Now we pass a SavedDatastore with our StoredResponse.

credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
             new ClientSecrets { ClientId = "YourClientId", ClientSecret = "YourClientSecret" },
              new[] { DriveService.Scope.Drive,
                DriveService.Scope.DriveFile },
              "user",
              CancellationToken.None,
               new SavedDataStore(myStoredResponse)).Result; }

I have a tutorial and sample project for this. Currently works with the Google Drive scopes you can change those easily, sample project is at the bottom. Google Oauth2 C#

Linda Lawton - DaImTo
  • 106,405
  • 32
  • 180
  • 449
  • @DalmTo I created windows service using c# to sync events to Google Calendar. Before windows service creation, I created windows form application and executed. It was worked well. Through windows service, it is not working. I posted the question http://stackoverflow.com/questions/30936438/google-calendar-sync-using-windows-service – Jesuraja Jun 19 '15 at 11:31