Search code examples
azuregoogle-apigoogle-drive-apigoogle-oauth

Using Google Apis from Azure Functions


I'm trying to access Google Apis using Azure functions. The goal is to connect to load the contents of multiple Google Sheets into an Azure SQL database. I've been looking at the sample code here but I can't figure out how to make it work using Azure Functions.

When I create an AppFlowMetadata.csx file and use reference it in my run.csx using '#load "AppFlowMetadata.csx"', I get multiple compile errors:

error CS0234: The type or namespace name 'Mvc' does not exist in the namespace 'System.Web' (are you missing an assembly reference?)

error CS0234: The type or namespace name 'Mvc' does not exist in the namespace 'Google.Apis.Auth.OAuth2' (are you missing an assembly reference?)

error CS0246: The type or namespace name 'FlowMetadata' could not be found (are you missing a using directive or an assembly reference?)

error CS0246: The type or namespace name 'Controller' could not be found (are you missing a using directive or an assembly reference?)

error CS0115: 'AppFlowMetadata.GetUserId(Controller)': no suitable method found to override

error CS0115: 'AppFlowMetadata.Flow': no suitable method found to override

AppFlwMetadata.csx:

using System;
using System.Web.Mvc;

using Google.Apis.Auth.OAuth2;
using Google.Apis.Auth.OAuth2.Flows;
using Google.Apis.Auth.OAuth2.Mvc;
using Google.Apis.Drive.v3;
using Google.Apis.Util.Store;

public class AppFlowMetadata : FlowMetadata
{
private static readonly IAuthorizationCodeFlow flow =
    new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
        {
            ClientSecrets = new ClientSecrets
            {
                ClientId = "PUT_CLIENT_ID_HERE",
                ClientSecret = "PUT_CLIENT_SECRET_HERE"
            },
            Scopes = new[] { DriveService.Scope.Drive },
            DataStore = new FileDataStore("Drive.Api.Auth.Store")
        });

public override string GetUserId(Controller controller)
{
    // In this sample we use the session to store the user identifiers.
    // That's not the best practice, because you should have a logic to identify
    // a user. You might want to use "OpenID Connect".
    // You can read more about the protocol in the following link:
    // https://developers.google.com/accounts/docs/OAuth2Login.
    var user = controller.Session["user"];
    if (user == null)
    {
        user = Guid.NewGuid();
        controller.Session["user"] = user;
    }
    return user.ToString();

}

public override IAuthorizationCodeFlow Flow
{
    get { return flow; }
}
}

project.json:

{
"frameworks": {
"net46":{
  "dependencies": {
    "Google.Apis.Drive.v3": "1.25.0.834",
    "Google.Apis.Sheets.v4": "1.25.0.841",
    "Google.Apis.Auth":"1.25.0"
  }
}
}
}

Has anyone successfully used Azure functions to authenticate with Google Apis? Any guidance on how to apply the Google example to Azure?


Solution

  • I haven't exactly tried this. IMO your not going to be able to use Oauth2 i would recommend trying with a service account.

    Your going to have to figure out how to upload the service account key file.

    /// <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 DriveService 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 DriveService(new BaseClientService.Initializer()
                    {
                        HttpClientInitializer = credential,
                        ApplicationName = "Drive 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  Drive service.
                    return new DriveService(new BaseClientService.Initializer()
                    {
                        HttpClientInitializer = credential,
                        ApplicationName = "Drive Authentication Sample",
                    });
                }
                else
                {
                    throw new Exception("Unsupported Service accounts credentials.");
                }
    
            }
            catch (Exception ex)
            {
                Console.WriteLine("Create service account DriveService failed" + ex.Message);
                throw new Exception("CreateServiceAccountDriveFailed", ex);
            }
        }
    }
    

    Code ripped from My GitHub sample project for google drive. Serviceaccount.cs I also have a tutorial about Drive API with service accounts

    If you cant get it working let me know i may have to try myself it sounds like an interesting idea.