Este documento descreve o OAuth 2.0, quando usá-lo, como adquirir IDs de cliente e como usá-lo com a biblioteca de cliente de APIs do Google para .NET.
Protocolo OAuth 2.0
O OAuth 2.0 é o protocolo de autorização usado pelas APIs do Google. Leia os seguintes links para se familiarizar com o protocolo:
Adquirir IDs e chaves secretas do cliente
É possível receber IDs e segredos de cliente no Console de APIs do Google. Há diferentes tipos de IDs do cliente. Confira o tipo correto para seu app:
- IDs de cliente do aplicativo da Web
- IDs de cliente do aplicativo instalado
- IDs de cliente da conta de serviço
Em cada um dos snippets de código mostrados (exceto o da conta de serviço), é necessário fazer o download do segredo do cliente e armazená-lo como client_secrets.json
no projeto.
Credenciais
Credenciais do usuário
UserCredential
é uma classe auxiliar segura para threads que usa um token de acesso para acessar recursos protegidos.
Um token de acesso normalmente expira após uma hora. Depois desse prazo, você vai receber um erro se tentar usá-lo.
UserCredential
e
AuthorizationCodeFlow
se encarregam de atualizar automaticamente o token, o que significa receber
um novo token de acesso.
Isso é feito usando um token de atualização de longa duração, que você recebe junto 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, é recomendável armazenar o token de acesso e de atualização da credencial no armazenamento persistente. Caso contrário, você vai precisar apresentar ao usuário final uma página de autorização no navegador a cada hora, porque o token de acesso irá expirar uma hora depois de você recebê-lo.
Para garantir que os tokens de acesso e de atualização persistam,
forneça sua própria implementação de
IDataStore
ou use uma das seguintes implementações fornecidas pela biblioteca:
-
FileDataStore
para .NET garante que a credencial será persistente em um arquivo.
ServiceAccountCredential
ServiceAccountCredential
é semelhante a UserCredential
, mas tem uma finalidade diferente.
O Google OAuth 2.0 aceita interações de servidor para servidor, como as que ocorrem entre um aplicativo da Web e o Google Cloud Storage.
O aplicativo solicitante precisa provar a própria identidade para conseguir acesso a uma API, e um usuário final não precisa estar envolvido.
ServiceAccountCredential
armazena uma chave privada, que é usada para assinar uma solicitação e receber um novo token de acesso.
UserCredential
e ServiceAccountCredential
implementam
IConfigurableHttpClientInitializer
para que você possa registrar cada um deles como:
- Um gerenciador de resposta sem sucesso,
então ele vai atualizar o token se receber um código de status HTTP
401
. - Um interceptor, para interceptar o cabeçalho
Authorization
em cada solicitação.
Aplicativos 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(); ... } } }
-
Neste exemplo de código, uma nova instância
UserCredential
é criada chamando o métodoGoogleWebAuthorizationBroker.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.
- O identificador do usuário.
- O token de cancelamento para cancelar uma operação.
- Um repositório de dados opcional. Se o repositório de dados não for especificado, o padrão será um
FileDataStore
com uma pastaGoogle.Apis.Auth
padrão. A pasta é criada emEnvironment.SpecialFolder.ApplicationData
.
-
O
UserCredential
retornado por esse método é definido como umHttpClientInitializer
noBooksService
(usando o inicializador). Como explicado anteriormente,UserCredential
implementa um inicializador de cliente HTTP. -
Observe que, no código de exemplo, as informações secretas do cliente são carregadas de 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 oferecem suporte a OAuth 2.0 para aplicativos de servidor da Web.
A
Google.Apis.Auth.AspNetCore3 é a biblioteca recomendada para a maioria dos cenários
OAuth 2.0 baseados no Google em aplicativos ASP.NET Core 3. Ele implementa um manipulador de autenticação OpenIdConnect
específico do Google. Ele oferece suporte à autenticação incremental e define um IGoogleAuthProvider
injetável para fornecer credenciais do Google que podem ser usadas com as APIs do Google.
Esta seção descreve 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 totalmente funcional.
Se você quiser seguir esta documentação como um tutorial, precisará ter seu próprio aplicativo do ASP.NET Core 3 e concluir estas etapas como pré-requisito.
Pré-requisitos
- Instale o pacote Google.Apis.Auth.AspNetCore3.
- Estamos usando a API Google Drive. Por isso, você também precisa instalar o pacote Google.Apis.Drive.v3.
- Crie um projeto do Google Cloud, se ainda não tiver um. Siga estas instruções para fazer isso. Esse será o projeto com que seu app será identificado.
- Ative a API Google Drive. Para ativar as APIs, siga estas instruções.
-
Crie credenciais de autorização que vão identificar 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ê possa estar usando. Os snippets a seguir são extraídos de
Startup.cs
no projeto Google.Apis.Auth.AspNetCore3.IntegrationTests.
-
Adicione a diretiva a seguir ao arquivo
Startup.cs
.using Google.Apis.Auth.AspNetCore3;
-
No método
Startup.ConfigureServices
, adicione o código abaixo, alterando os marcadores de posição do ID e da chave secreta do cliente pelos valores contidos no arquivoclient_secrets.json
. É possível carregar esses valores diretamente do arquivo JSON ou armazená-los de qualquer outra forma segura. Confira o métodoClientInfo.Load
no projeto Google.Apis.Auth.AspNetCore3.IntegrationTests para conferir um exemplo de como carregar esses valores diretamente do 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 os componentes de middleware de autenticação e autorização do ASP.NET Core 3 ao pipeline, além de 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ê pode adicionar métodos de ação aos controladores que exigem a credencial do usuário para acessar as APIs do Google em nome dele. O snippet a seguir mostra como listar os arquivos na conta do Google Drive do usuário autenticado. Observe duas coisas principalmente:
-
O usuário precisa ser autenticado e conceder o escopo
https://www.googleapis.com/auth/drive.readonly
ao seu aplicativo, o que é especificado usando o atributoGoogleScopedAuthorize
. -
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 as diretivas a seguir ao 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 acompanhe com uma visualização
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 o
HomeController.cs
do projeto Google.Apis.Auth.AspNetCore3.IntegrationTests
para saber como fazer o seguinte:
- Somente autenticação do usuário, sem escopos específicos
- Fazer logout do usuário
- Autorização incremental com código. O exemplo mostra a autorização incremental com atributos.
- Examinar os escopos concedidos
- Examinar tokens de acesso e de atualização
- Forçar a atualização do token de acesso. Não é necessário fazer isso manualmente, porque o Google.Apis.Auth.AspNetCore3 detecta se o token de acesso está expirado ou está prestes a expirar e o atualiza automaticamente.
Conta de serviço
As APIs do Google também oferecem suporte a contas de serviço. Ao contrário do cenário em que um aplicativo cliente solicita acesso aos dados de um usuário final, as contas de serviço fornecem acesso aos dados do próprio aplicativo cliente.
O aplicativo cliente assina a solicitação de um token de acesso usando uma chave privada baixada do Console de APIs do Google. Depois de criar um novo ID do cliente, escolha um tipo de aplicativo de conta de serviço e faça o download da chave privada. Confira 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(); } } }
Este exemplo cria um
ServiceAccountCredential
.
Os escopos necessários são definidos, e há uma chamada para FromCertificate
,
que carrega a chave privada do X509Certificate2
fornecido.
Como em todos os outros exemplos de código, a credencial é definida como HttpClientInitializer
.