OAuth 2.0

Este documento descreve o OAuth 2.0, quando usá-lo, como adquirir IDs do cliente e como usá-lo com a biblioteca de cliente da API do Google para .NET.

Protocolo OAuth 2.0

OAuth 2.0 é o protocolo de autorização usado pelas APIs do Google. Para se familiarizar com o protocolo, acesse os links a seguir:

Aquisição de secrets e IDs do cliente

Acesse IDs do cliente e secrets no Console de APIs do Google. Há tipos diferentes de IDs de cliente. Por isso, verifique se está o tipo correto para o seu aplicativo:

Em cada um dos snippets de código abaixo (exceto o da conta de serviço), você precisa fazer o download da chave secreta do cliente e armazená-la como client_secrets.json no seu projeto.

Credenciais

Credenciais do usuário

A UserCredential é uma classe auxiliar thread-safe para usar um token de acesso para acessar recursos protegidos. Um token de acesso normalmente expira após uma hora. Após esse período, ocorrerá um erro se você tentar usá-lo.

UserCredential e AuthorizationCodeFlow cuidam automaticamente do token e atualizam o token, o que significa simplesmente receber um novo token de acesso. Isso é feito com um token de atualização de longa duração, que você receberá com o token de acesso se usar o parâmetro access_type=offline durante o fluxo do código de autorização.

Na maioria dos aplicativos, é aconselhável armazenar o token de acesso da credencial e atualizar o token no armazenamento permanente. Caso contrário, será necessário apresentar ao usuário final uma página de autorização no navegador a cada hora, porque o token de acesso expira uma hora após o recebimento.

Para garantir que os tokens de acesso e atualização persistam, você pode fornecer sua própria implementação de IDataStore ou usar uma das seguintes implementações fornecidas pela biblioteca:

  • FileDataStore para .NET garante que a credencial será permanente em um arquivo.

ServiceAccountCredential

ServiceAccountCredential é semelhante a UserCredential, mas tem uma finalidade diferente. O Google OAuth 2.0 é compatível com interações de servidor para servidor, como as entre um aplicativo da Web e o Google Cloud Storage. O aplicativo que fez a solicitação precisa comprovar a própria identidade para ter acesso a uma API, e o usuário final não precisa se envolver. ServiceAccountCredential armazena uma chave privada, que é usada para assinar uma solicitação de recebimento de um novo token de acesso.

Tanto UserCredential quanto ServiceAccountCredential implementam IConfigurableHttpClientInitializer, para que você possa registrar cada um deles como:

  • Um gerenciador de respostas com falha. Por isso, ele atualizará o token se receber um código de status HTTP 401.
  • Um interceptador para interceptar o cabeçalho Authorization em cada solicitação

Apps instalados

Exemplo de código usando a API Books:

using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

using Google.Apis.Auth.OAuth2;
using Google.Apis.Books.v1;
using Google.Apis.Books.v1.Data;
using Google.Apis.Services;
using Google.Apis.Util.Store;

namespace Books.ListMyLibrary
{
    /// <summary>
    /// Sample which demonstrates how to use the Books API.
    /// https://developers.google.com/books/docs/v1/getting_started
    /// <summary>
    internal class Program
    {
        [STAThread]
        static void Main(string[] args)
        {
            Console.WriteLine("Books API Sample: List MyLibrary");
            Console.WriteLine("================================");
            try
            {
                new Program().Run().Wait();
            }
            catch (AggregateException ex)
            {
                foreach (var e in ex.InnerExceptions)
                {
                    Console.WriteLine("ERROR: " + e.Message);
                }
            }
            Console.WriteLine("Press any key to continue...");
            Console.ReadKey();
        }

        private async Task Run()
        {
            UserCredential credential;
            using (var stream = new FileStream("client_secrets.json", FileMode.Open, FileAccess.Read))
            {
                credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
                    GoogleClientSecrets.Load(stream).Secrets,
                    new[] { BooksService.Scope.Books },
                    "user", CancellationToken.None, new FileDataStore("Books.ListMyLibrary"));
            }

            // Create the service.
            var service = new BooksService(new BaseClientService.Initializer()
                {
                    HttpClientInitializer = credential,
                    ApplicationName = "Books API Sample",
                });

            var bookshelves = await service.Mylibrary.Bookshelves.List().ExecuteAsync();
            ...
        }
    }
}
  
  • Nesta amostra de código, uma nova instância de UserCredential é criada chamando o método GoogleWebAuthorizationBroker.AuthorizeAsync. Esse método estático recebe o seguinte:

    • A chave secreta do cliente (ou um fluxo para a chave secreta do cliente).
    • Os escopos necessários
    • Identificador do usuário.
    • O token de cancelamento de uma operação.
    • Um armazenamento de dados opcional. Se o armazenamento de dados não for especificado, o padrão vai ser um FileDataStore com uma pasta Google.Apis.Auth padrão. A pasta é criada em Environment.SpecialFolder.ApplicationData.
  • O UserCredential retornado por esse método é definido como um HttpClientInitializer no BooksService (usando o inicializador). Conforme explicado acima, UserCredential implementa um inicializador do cliente HTTP.

  • Na amostra de código acima, as informações da chave secreta do cliente são carregadas em um arquivo, mas você também pode fazer o seguinte:

    credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
        new ClientSecrets
        {
            ClientId = "PUT_CLIENT_ID_HERE",
            ClientSecret = "PUT_CLIENT_SECRETS_HERE"
        },
        new[] { BooksService.Scope.Books },
        "user",
        CancellationToken.None,
        new FileDataStore("Books.ListMyLibrary"));
          

Confira nossa amostra de livros.

Aplicativos da Web (ASP.NET Core 3)

As APIs do Google são compatíveis com o OAuth 2.0 para aplicativos de servidor da Web.

A Google.Apis.Auth.AspNetCore3 é a biblioteca recomendada para a maioria dos cenários do OAuth 2.0 baseados no Google em aplicativos ASP.NET Core 3. Ele implementa um gerenciador de autenticação OpenIdConnect específico do Google. Ele é compatível com a autenticação incremental e define um IGoogleAuthProvider injetável para fornecer credenciais do Google que podem ser usadas com as APIs do Google.

Nesta seção, descrevemos como configurar e usar o Google.Apis.Auth.AspNetCore3. O código mostrado aqui é baseado em Google.Apis.Auth.AspNetCore3.integrationTests, que é um aplicativo ASP.NET Core 3 padrão e totalmente funcional.

Se você quiser acompanhar essa documentação como um tutorial, precisará do seu próprio aplicativo ASP.NET Core 3 e concluir estas etapas como um pré-requisito.

Pré-requisitos

  • Instale o pacote Google.Apis.Auth.AspNetCore3.
  • Estamos usando a API Google Drive. Por isso, você também precisará instalar o pacote Google.Apis.Drive.v3.
  • Crie um projeto do Google Cloud, caso ainda não tenha um. Siga estas instruções para fazer isso. Esse será o projeto com que seu app está identificado.
  • Ative a API Google Drive. Para ativar as APIs, siga estas instruções.
  • Crie credenciais de autorização que identifiquem seu app para o Google. Siga estas instruções para criar credenciais de autorização e fazer o download do arquivo client_secrets.json. Dois destaques:
    • O tipo de credenciais' precisa ser aplicativo da Web.
    • Para executar este app, o único URI de redirecionamento que você precisa adicionar é https://localhost:5001/signin-oidc.

Configurar o aplicativo para usar o Google.Apis.Auth.AspNetCore3

O Google.Apis.Auth.AspNetCore3 é configurado na classe Startup ou em uma alternativa semelhante que você usa. Os snippets a seguir foram extraídos de Startup.cs no projeto Google.Apis.Auth.AspNetCore3.IntegrationTests.

  • Adicione o item a seguir usando a diretiva para o arquivo Startup.cs.
    using Google.Apis.Auth.AspNetCore3;
  • No método Startup.ConfigureServices, adicione o seguinte código, incluindo os marcadores de posição do ID do cliente e da chave secreta do cliente com os valores contidos no arquivo client_secrets.json. Carregue esses valores diretamente no arquivo JSON ou armazene-os de outra maneira segura. Dê uma olhada no método ClientInfo.Load no projeto Google.Apis.Auth.AspNetCore3.IntegrationTests para ver um exemplo de como carregar esses valores diretamente no arquivo JSON.
    public void ConfigureServices(IServiceCollection services)
    {
        ...
    
        // This configures Google.Apis.Auth.AspNetCore3 for use in this app.
        services
            .AddAuthentication(o =>
            {
                // This forces challenge results to be handled by Google OpenID Handler, so there's no
                // need to add an AccountController that emits challenges for Login.
                o.DefaultChallengeScheme = GoogleOpenIdConnectDefaults.AuthenticationScheme;
                // This forces forbid results to be handled by Google OpenID Handler, which checks if
                // extra scopes are required and does automatic incremental auth.
                o.DefaultForbidScheme = GoogleOpenIdConnectDefaults.AuthenticationScheme;
                // Default scheme that will handle everything else.
                // Once a user is authenticated, the OAuth2 token info is stored in cookies.
                o.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            })
            .AddCookie()
            .AddGoogleOpenIdConnect(options =>
            {
                options.ClientId = {YOUR_CLIENT_ID};
                options.ClientSecret = {YOUR_CLIENT_SECRET};
            });
    }
          
  • No método Startup.Configure, adicione componentes de middleware de autenticação e autorização do ASP.NET Core 3 ao pipeline, bem como redirecionamentos HTTPS:
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        ...
        app.UseHttpsRedirection();
        ...
    
        app.UseAuthentication();
        app.UseAuthorization();
    
        ...
    }
          

Usar a credencial do usuário para acessar as APIs do Google em nome dele

Agora você está pronto para adicionar métodos de ação aos seus controladores, que exigem que a credencial do usuário acesse as APIs do Google em nome deles. O snippet a seguir mostra como listar os arquivos na conta autenticada do Google Drive do usuário autenticado. Observe duas coisas, principalmente:

  • O usuário não apenas precisa ser autenticado, mas também precisa conceder o escopo https://www.googleapis.com/auth/drive.readonly ao aplicativo, que você especifica no atributo GoogleScopedAuthorize.
  • Estamos usando o mecanismo de injeção de dependência padrão do ASP.NET Core 3' para receber um IGoogleAuthProvider que usamos para receber as credenciais do usuário.

O código:

  • Primeiro, adicione o código a seguir usando diretivas do seu controlador.
    using Google.Apis.Auth.AspNetCore3;
    using Google.Apis.Auth.OAuth2;
    using Google.Apis.Drive.v3;
    using Google.Apis.Services;
          
  • Adicione a ação do controlador da seguinte maneira (e a acompanhe com uma visualização simples que recebe um modelo IList<string>):
    /// <summary>
    /// Lists the authenticated user's Google Drive files.
    /// Specifying the <see cref="GoogleScopedAuthorizeAttribute"> will guarantee that the code
    /// executes only if the user is authenticated and has granted the scope specified in the attribute
    /// to this application.
    /// </summary>
    /// <param name="auth">The Google authorization provider.
    /// This can also be injected on the controller constructor.</param>
    [GoogleScopedAuthorize(DriveService.ScopeConstants.DriveReadonly)]
    public async Task<IActionResult> DriveFileList([FromServices] IGoogleAuthProvider auth)
    {
        GoogleCredential cred = await auth.GetCredentialAsync();
        var service = new DriveService(new BaseClientService.Initializer
        {
            HttpClientInitializer = cred
        });
        var files = await service.Files.List().ExecuteAsync();
        var fileNames = files.Files.Select(x => x.Name).ToList();
        return View(fileNames);
    }
          

E estes são os princípios básicos. Confira HomeController.cs no projeto Google.Apis.Auth.AspNetCore3.IntegrationTests para saber como você pode alcançar:

  • Apenas autenticação do usuário, sem escopos específicos
  • Funcionalidade de saída
  • Autorização incremental por código. O snippet acima mostra autorização incremental usando atributos.
  • Examinar os escopos concedidos atualmente
  • Examinar tokens de acesso e atualização
  • Force a atualização do token de acesso. Não é necessário fazer isso, porque o Google.Apis.Auth.AspNetCore3 detectará se o token de acesso expirou ou está prestes a expirar e o atualizará automaticamente.

Aplicativos da Web (ASP.NET MVC)

As APIs do Google são compatíveis com o OAuth 2.0 para aplicativos de servidor da Web. Para executar o código a seguir com sucesso, você precisa primeiro adicionar um URI de redirecionamento ao seu projeto no Console de APIs do Google. Como você usará o FlowMetadata e as configurações padrão dele, defina o URI de redirecionamento como your_site/AuthCallback/IndexAsync.

Para encontrar os URIs de redirecionamento para suas credenciais do OAuth 2.0, faça o seguinte:

  1. Abra a página Credenciais no Console da API.
  2. Se você ainda não tiver feito isso, crie suas credenciais do OAuth 2.0 clicando em Criar credenciais > ID do cliente OAuth.
  3. Depois de criar suas credenciais, visualize ou edite os URLs de redirecionamento clicando no ID do cliente (para um aplicativo da Web) na seção IDs do cliente OAuth 2.0.

Depois de criar um novo projeto de aplicativo da Web no seu ambiente de desenvolvimento integrado, adicione o pacote NuGet Google.Apis correto para Drive, YouTube ou outro serviço que você quer usar. Em seguida, adicione o pacote Google.Apis.Auth.MVC. O código a seguir demonstra um aplicativo ASP.NET MVC que consulta um serviço de API do Google.

  1. Adicione sua própria implementação de FlowMetadata.
    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.v2;
    using Google.Apis.Util.Store;
    
    namespace Google.Apis.Sample.MVC4
    {
        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; }
            }
        }
    }
          

    FlowMetadata é uma classe abstrata que contém sua própria lógica para recuperar o identificador do usuário e o IAuthorizationCodeFlow que você está usando.

    Na amostra de código acima, um novo GoogleAuthorizationCodeFlow é criado com os escopos corretos, chaves secretas do cliente e armazenamento de dados. Adicione sua própria implementação de IDataStore. Por exemplo, escreva uma que use EntityFramework.

  2. Implemente seu próprio controlador que usa um serviço de API do Google. O exemplo a seguir usa um DriveService:
    using System.Linq;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Web.Mvc;
    
    using Google.Apis.Auth.OAuth2.Mvc;
    using Google.Apis.Drive.v2;
    using Google.Apis.Services;
    
    using Google.Apis.Sample.MVC4;
    
    namespace Google.Apis.Sample.MVC4.Controllers
    {
        public class HomeController : Controller
        {
            public async Task<ActionResult> IndexAsync(CancellationToken cancellationToken)
            {
                var result = await new AuthorizationCodeMvcApp(this, new AppFlowMetadata()).
                    AuthorizeAsync(cancellationToken);
    
                if (result.Credential != null)
                {
                    var service = new DriveService(new BaseClientService.Initializer
                        {
                            HttpClientInitializer = result.Credential,
                            ApplicationName = "ASP.NET MVC Sample"
                        });
    
                    // YOUR CODE SHOULD BE HERE..
                    // SAMPLE CODE:
                    var list = await service.Files.List().ExecuteAsync();
                    ViewBag.Message = "FILE COUNT IS: " + list.Items.Count();
                    return View();
                }
                else
                {
                    return new RedirectResult(result.RedirectUri);
                }
            }
        }
    }
          
  3. Implemente seu próprio controlador de callback. A implementação precisa ser assim:
    using Google.Apis.Sample.MVC4;
    
    namespace Google.Apis.Sample.MVC4.Controllers
    {
        public class AuthCallbackController : Google.Apis.Auth.OAuth2.Mvc.Controllers.AuthCallbackController
        {
            protected override Google.Apis.Auth.OAuth2.Mvc.FlowMetadata FlowData
            {
                get { return new AppFlowMetadata(); }
            }
        }
    }
          

Conta de serviço

As APIs do Google também são compatíveis com contas de serviço. Ao contrário do que acontece quando um aplicativo cliente solicita acesso aos dados do usuário final, as contas de serviço dão acesso aos dados do próprio aplicativo cliente.

O aplicativo cliente assina a solicitação de um token de acesso usando uma chave privada salva no Console de APIs do Google. Depois de criar um novo ID do cliente, escolha um tipo de aplicativo "Conta de serviço" e faça o download da chave privada. Veja nosso exemplo de conta de serviço usando a API Google Plus.

using System;
using System.Security.Cryptography.X509Certificates;

using Google.Apis.Auth.OAuth2;
using Google.Apis.Plus.v1;
using Google.Apis.Plus.v1.Data;
using Google.Apis.Services;

namespace Google.Apis.Samples.PlusServiceAccount
{
    /// <summary>
    /// This sample demonstrates the simplest use case for a Service Account service.
    /// The certificate needs to be downloaded from the Google API Console
    /// <see cref="https://console.cloud.google.com/">
    ///   "Create another client ID..." -> "Service Account" -> Download the certificate,
    ///   rename it as "key.p12" and add it to the project. Don't forget to change the Build action
    ///   to "Content" and the Copy to Output Directory to "Copy if newer".
    /// </summary>
    public class Program
    {
        // A known public activity.
        private static String ACTIVITY_ID = "z12gtjhq3qn2xxl2o224exwiqruvtda0i";

        public static void Main(string[] args)
        {
            Console.WriteLine("Plus API - Service Account");
            Console.WriteLine("==========================");

            String serviceAccountEmail = "SERVICE_ACCOUNT_EMAIL_HERE";

            var certificate = new X509Certificate2(@"key.p12", "notasecret", X509KeyStorageFlags.Exportable);

            ServiceAccountCredential credential = new ServiceAccountCredential(
               new ServiceAccountCredential.Initializer(serviceAccountEmail)
               {
                   Scopes = new[] { PlusService.Scope.PlusMe }
               }.FromCertificate(certificate));

            // Create the service.
            var service = new PlusService(new BaseClientService.Initializer()
            {
                HttpClientInitializer = credential,
                ApplicationName = "Plus API Sample",
            });

            Activity activity = service.Activities.Get(ACTIVITY_ID).Execute();
            Console.WriteLine("  Activity: " + activity.Object.Content);
            Console.WriteLine("  Video: " + activity.Object.Attachments[0].Url);

            Console.WriteLine("Press any key to continue...");
            Console.ReadKey();
        }
    }
}

O exemplo de código acima cria um ServiceAccountCredential. Os escopos necessários são definidos e há uma chamada para FromCertificate, que carrega a chave privada da X509Certificate2 especificada. Como em todos os outros exemplos de código, a credencial é definida como HttpClientInitializer.