How to construct GmailService in web app when using .AddGoogle() in Startup.cs?

Viewed 610

The goal: generate a draft email with body content and attachments and drop it into my current user's GMail account.

The environment: ASP.NET Core 3.1

I'm already using Google Authentication in my web app and am successfully requesting the Gmail Compose scope:

services
    .AddAuthentication(options => { options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; })
    .AddCookie()
    .AddGoogle(options =>
    {
        IConfigurationSection googleAuthSection = Configuration.GetSection("Authentication:Google");
        options.ClientId = googleAuthSection["ClientId"];
        options.ClientSecret = googleAuthSection["ClientSecret"];
        options.Scope.Add(Google.Apis.Gmail.v1.GmailService.Scope.GmailCompose);
    });

From the various samples online, this is what I've found I need to do to generate a draft and save it to a gmail account:

var draft = new Draft();
draft.Message = new Message();
draft.Message.Payload = new MessagePart { Body = new MessagePartBody { Data = "hello world".ToBase64Url() } };

var clientSecrets = new ClientSecrets
{
    ClientId = _googleAuthSettings.ClientId,
    ClientSecret = _googleAuthSettings.ClientSecret
};
UserCredential credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
    clientSecrets,
    new[] { Google.Apis.Gmail.v1.GmailService.Scope.GmailCompose },
    user.Data.EmailAddress,
    CancellationToken.None);

var gservice = new Google.Apis.Gmail.v1.GmailService(
    new BaseClientService.Initializer
    {
        HttpClientInitializer = credential,
        ApplicationName = "Aeon"
    });

var createRequest = gservice.Users.Drafts.Create(draft, user.Data.EmailAddress);

The problem is that the call to GoogleWebAuthorizationBroker.AuthorizeAsync is a brand new auth request that pops a new browser window to auth against Google (which doesn't work because redirect urls and such). I've already authed the user through my existing startup/login process.

The two GmailService constructors take either no arguments or this BaseClientService.Initializer class, which itself takes a Google.Apis.Services.IConfigurableHttpClientInitializer object. In the sample above, the UserCredential class implements this interface. How do I use my existing login process' credentials to construct a gmail service? Alternatively, is there a way to do what I'm looking to do without having to construct a GmailService instance?

1 Answers

Answer:

You need to use Google.Apis.Auth.Mvc for web services, as GoogleWebAuthorizationBroker.AuthorizeAsync can not be used to create the authorization flow for web apps.

More Information:

As per the documentation on OAuth in Web Applications:

After creating a new web application project in your IDE, add the right Google.Apis NuGet package for Drive, YouTube, or the other service you want to use. Then, add the Google.Apis.Auth.MVC package.

The page also demonstrates an ASP.NET MVC application that queries a Google API service.

If you want to deploy GmailService on the web you need to make use of the Google.Apis.Auth.MVC package to make an implementation of the controllers.

Also from the source for the GoogleWebAuthorizationBroker class:

/// This class is only suitable for client-side use, as it starts a local browser 
/// that requires user interaction.
/// Do not use this class when executing on a web server, or any cases where the 
/// authenticating end-user is not able to do directly interact with a launched 
/// browser.

References:

Related