Search code examples
c#azuregoogle-apigoogle-oauthgoogle-api-dotnet-client

Using OAuth2 to Authenticate with a Google API in C#


I have created an console application that uses OAuth2 to authenticate with the GoogleAnalyticsApiV4 to query some data. The application works as intended but we would like to automate the process so the application can be scheduled to run once a day. The problem here is the application would be hosted on azure and there is no way for a user to accept the authentication request with google that pops up in a browser the first time the application runs.

Following posts online and googles documentation my current solution to authenticate is this

     try
        {
            var credential = GetCredential().Result;

            using (var svc = new AnalyticsReportingService(
            new BaseClientService.Initializer
            {
                HttpClientInitializer = credential,
                ApplicationName = "Google Analytics API Console"
            }))
        {
           ///// Query some data/////
        } 



static async Task<UserCredential> GetCredential()
{
    using (var stream = new FileStream("client_secret.json",
         FileMode.Open, FileAccess.Read))
    {
        string loginEmailAddress = ConfigurationManager.AppSettings["GoogleUsername"];
        return await GoogleWebAuthorizationBroker.AuthorizeAsync(
            GoogleClientSecrets.Load(stream).Secrets,
            new[] { AnalyticsReportingService.Scope.Analytics },
            loginEmailAddress, CancellationToken.None,
            new FileDataStore("GoogleAnalyticsApiConsole"));
    }
}

This solution works perfectly well to authenticate with Google as long as a user is available to input credentials and accept the authentication request. Unfortunately as soon as the application is moved to another machine it needs to re-authenticate and a user needs to input credentials again and accept the request.

I have been searching for a way to take the User out of the process so the application can run on azure but have not found anything clear on how to do this in c#.

Please can someone either describe how i can authenticate my application with google without a user, or point me in the direction of documentation that accurately covers the process.

An help or examples would be greatly appreciated.


Solution

  • You have a couple of options.

    Is this an account you have access to. If it is then you can use a service account. Service accounts are preauthorized the you take the service account email address and add it as a user in Google analytics admin at the account level and the service account will be able to access the account for as long as it is valid. No pop up window is required. I have some sample code on how to authenticate with a service account here

    /// <summary>
        /// Authenticating to Google using a Service account
        /// Documentation: https://developers.google.com/accounts/docs/OAuth2#serviceaccount
        /// </summary>
        /// <param name="serviceAccountEmail">From Google Developer console https://console.developers.google.com</param>
        /// <param name="serviceAccountCredentialFilePath">Location of the .p12 or Json Service account key file downloaded from Google Developer console https://console.developers.google.com</param>
        /// <returns>AnalyticsService used to make requests against the Analytics API</returns>
        public static AnalyticsReportingService AuthenticateServiceAccount(string serviceAccountEmail, string serviceAccountCredentialFilePath)
        {
            try
            {
                if (string.IsNullOrEmpty(serviceAccountCredentialFilePath))
                    throw new Exception("Path to the service account credentials file is required.");
                if (!File.Exists(serviceAccountCredentialFilePath))
                    throw new Exception("The service account credentials file does not exist at: " + serviceAccountCredentialFilePath);
                if (string.IsNullOrEmpty(serviceAccountEmail))
                    throw new Exception("ServiceAccountEmail is required.");
    
                // These are the scopes of permissions you need. It is best to request only what you need and not all of them
                string[] scopes = new string[] { AnalyticsReportingService.Scope.Analytics };             // View your Google Analytics data
    
                // For Json file
                if (Path.GetExtension(serviceAccountCredentialFilePath).ToLower() == ".json")
                {
                    GoogleCredential credential;
                    using (var stream = new FileStream(serviceAccountCredentialFilePath, FileMode.Open, FileAccess.Read))
                    {
                        credential = GoogleCredential.FromStream(stream)
                             .CreateScoped(scopes);
                    }
    
                    // Create the  Analytics service.
                    return new AnalyticsReportingService(new BaseClientService.Initializer()
                    {
                        HttpClientInitializer = credential,
                        ApplicationName = "AnalyticsReporting Service account Authentication Sample",
                    });
                }
                else if (Path.GetExtension(serviceAccountCredentialFilePath).ToLower() == ".p12")
                {   // If its a P12 file
    
                    var certificate = new X509Certificate2(serviceAccountCredentialFilePath, "notasecret", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable);
                    var credential = new ServiceAccountCredential(new ServiceAccountCredential.Initializer(serviceAccountEmail)
                    {
                        Scopes = scopes
                    }.FromCertificate(certificate));
    
                    // Create the  AnalyticsReporting service.
                    return new AnalyticsReportingService(new BaseClientService.Initializer()
                    {
                        HttpClientInitializer = credential,
                        ApplicationName = "AnalyticsReporting Authentication Sample",
                    });
                }
                else
                {
                    throw new Exception("Unsupported Service accounts credentials.");
                }
    
            }
            catch (Exception ex)
            {
                Console.WriteLine("Create service account AnalyticsReportingService failed" + ex.Message);
                throw new Exception("CreateServiceAccountAnalyticsReportingFailed", ex);
            }
        }
    

    If this isn't something you can do. Then you should be aware of the fact that filedatastore() by default stores your credentials in %appData% you could simply copy that file onto the new server along with the code.

    You can also move the location to some were other then %appData% by using the following code:

    new FileDataStore(@"c:\datastore",true)     
    

    I have a tutorial on how filedatastore works. here File datastore demystified

    Preauthorizing service account to Google Analytics. Admin section of the Google analytics website. Grant it read access should be more then enough.

    enter image description here