Login do Google para apps do lado do servidor

Para usar os Serviços do Google em nome de um usuário quando ele estiver off-line, será necessário usar um fluxo híbrido do lado do servidor, em que o usuário autorize o app no lado do cliente usando o cliente da API JavaScript e você envia um código de autorização único para seu servidor. Seu servidor troca esse código de uso único para adquirir os próprios tokens de acesso e atualização do Google para que ele possa fazer as próprias chamadas de API, o que pode ser feito enquanto o usuário está off-line. Esse fluxo de código único tem vantagens de segurança sobre um fluxo puro do lado do servidor e sobre o envio de tokens de acesso ao seu servidor.

O fluxo de login para receber um token de acesso para seu aplicativo do lado do servidor é ilustrado abaixo.

Os códigos de uso único têm várias vantagens de segurança. Com os códigos, o Google fornece tokens diretamente ao seu servidor, sem nenhum intermediário. Embora não seja recomendado vazar códigos, eles são muito difíceis de usar sem a chave secreta do cliente. Mantenha o segredo do cliente em segredo.

Como implementar o fluxo de código único

O botão do Login do Google fornece um token de acesso e um código de autorização. O código é único que seu servidor pode trocar com os servidores do Google por um token de acesso.

O exemplo de código a seguir demonstra como fazer o fluxo de código único.

Para autenticar o Login do Google com o fluxo de código único, você precisa:

Etapa 1: criar um ID e uma chave secreta do cliente

Para criar um ID e uma chave secreta do cliente, crie um projeto no Console de APIs do Google, configure um ID do cliente OAuth e registre suas origens do JavaScript:

  1. Vá para o Console de APIs do Google.

  2. No menu suspenso do projeto, selecione um projeto atual ou crie um novo selecionando Criar um novo projeto.

  3. Na barra lateral, em "APIs e serviços", selecione Credenciais e clique em Configurar tela de consentimento.

    Escolha um endereço de e-mail, especifique um nome de produto e clique em Salvar.

  4. Na guia Credenciais, selecione a lista suspensa Criar credenciais e escolha o ID do cliente do OAuth.

  5. Em Tipo de aplicativo, selecione Aplicativo da Web.

    Registre as origens das quais seu app tem permissão para acessar as APIs do Google da seguinte maneira. Uma origem é uma combinação exclusiva de protocolo, nome do host e porta.

    1. No campo Origens JavaScript autorizadas, insira a origem do aplicativo. É possível inserir várias origens para permitir que o app seja executado em diferentes protocolos, domínios ou subdomínios. Não é possível usar caracteres curinga. No exemplo abaixo, o segundo URL poderia ser um URL de produção.

      http://localhost:8080
      https://myproductionurl.example.com
      
    2. O campo URI de redirecionamento autorizado não requer um valor. Os URIs de redirecionamento não são usados com APIs JavaScript.

    3. Pressione o botão Criar.

  6. Na caixa de diálogo Cliente OAuth, copie o ID do cliente. O ID do cliente permite que seu app acesse as APIs ativadas do Google.

Etapa 2: incluir a biblioteca da Plataforma Google na sua página

Inclua os scripts abaixo que demonstram uma função anônima que insere um script no DOM desta página da Web index.html.

<!-- The top of file index.html -->
<html itemscope itemtype="http://schema.org/Article">
<head>
  <!-- BEGIN Pre-requisites -->
  <script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js">
  </script>
  <script src="https://apis.google.com/js/client:platform.js?onload=start" async defer>
  </script>
  <!-- END Pre-requisites -->

Etapa 3: inicializar o objeto GoogleAuth

Carregue a biblioteca auth2 e chame gapi.auth2.init() para inicializar o objeto GoogleAuth. Especifique seu ID do cliente e os escopos que você quer solicitar ao chamar init().

<!-- Continuing the <head> section -->
  <script>
    function start() {
      gapi.load('auth2', function() {
        auth2 = gapi.auth2.init({
          client_id: 'YOUR_CLIENT_ID.apps.googleusercontent.com',
          // Scopes to request in addition to 'profile' and 'email'
          //scope: 'additional_scope'
        });
      });
    }
  </script>
</head>
<body>
  <!-- ... -->
</body>
</html>

Etapa 4: adicionar o botão de login à sua página

Adicione o botão de login à sua página da Web e anexe um gerenciador de cliques para chamar grantOfflineAccess() e iniciar o fluxo de código único.

<!-- Add where you want your sign-in button to render -->
<!-- Use an image that follows the branding guidelines in a real app -->
<button id="signinButton">Sign in with Google</button>
<script>
  $('#signinButton').click(function() {
    // signInCallback defined in step 6.
    auth2.grantOfflineAccess().then(signInCallback);
  });
</script>

Etapa 5: fazer o login do usuário

O usuário clica no botão de login e concede ao app acesso às permissões solicitadas. Em seguida, a função de callback especificada no método grantOfflineAccess().then() recebe um objeto JSON com um código de autorização. Exemplo:

{"code":"4/yU4cQZTMnnMtetyFcIWNItG32eKxxxgXXX-Z4yyJJJo.4qHskT-UtugceFc0ZRONyF4z7U4UmAI"}

Etapa 6: enviar o código de autorização ao servidor

O code é o código único que o servidor pode trocar por token de acesso e de atualização próprios. Só é possível conseguir um token de atualização depois que o usuário vê uma caixa de diálogo de autorização solicitando acesso off-line. Se você especificou o select-account prompt em OfflineAccessOptions na etapa 4, é necessário armazenar o token de atualização recuperado para uso posterior, porque as trocas subsequentes retornarão null para o token de atualização. Esse fluxo proporciona maior segurança em relação ao fluxo padrão do OAuth 2.0.

Os tokens de acesso são sempre retornados com a troca de um código de autorização válido.

O script a seguir define uma função de callback para o botão de login. Quando o login é bem-sucedido, a função armazena o token de acesso para uso do lado do cliente e envia o código único para seu servidor no mesmo domínio.

<!-- Last part of BODY element in file index.html -->
<script>
function signInCallback(authResult) {
  if (authResult['code']) {

    // Hide the sign-in button now that the user is authorized, for example:
    $('#signinButton').attr('style', 'display: none');

    // Send the code to the server
    $.ajax({
      type: 'POST',
      url: 'http://example.com/storeauthcode',
      // Always include an `X-Requested-With` header in every AJAX request,
      // to protect against CSRF attacks.
      headers: {
        'X-Requested-With': 'XMLHttpRequest'
      },
      contentType: 'application/octet-stream; charset=utf-8',
      success: function(result) {
        // Handle or verify the server response.
      },
      processData: false,
      data: authResult['code']
    });
  } else {
    // There was an error.
  }
}
</script>

Etapa 7: trocar o código de autorização por um token de acesso

No servidor, troque o código de autorização por tokens de acesso e de atualização. Use o token de acesso para chamar as APIs do Google em nome do usuário e, opcionalmente, armazene o token de atualização para receber um novo token de acesso quando ele expirar.

Se você solicitou acesso ao perfil, também recebe um token de ID que contém informações básicas do perfil do usuário.

Exemplo:

Java
// (Receive authCode via HTTPS POST)


if (request.getHeader("X-Requested-With") == null) {
  // Without the `X-Requested-With` header, this request could be forged. Aborts.
}

// Set path to the Web application client_secret_*.json file you downloaded from the
// Google API Console: https://console.cloud.google.com/apis/credentials
// You can also find your Web application client ID and client secret from the
// console and specify them directly when you create the GoogleAuthorizationCodeTokenRequest
// object.
String CLIENT_SECRET_FILE = "/path/to/client_secret.json";

// Exchange auth code for access token
GoogleClientSecrets clientSecrets =
    GoogleClientSecrets.load(
        JacksonFactory.getDefaultInstance(), new FileReader(CLIENT_SECRET_FILE));
GoogleTokenResponse tokenResponse =
          new GoogleAuthorizationCodeTokenRequest(
              new NetHttpTransport(),
              JacksonFactory.getDefaultInstance(),
              "https://oauth2.googleapis.com/token",
              clientSecrets.getDetails().getClientId(),
              clientSecrets.getDetails().getClientSecret(),
              authCode,
              REDIRECT_URI)  // Specify the same redirect URI that you use with your web
                             // app. If you don't have a web version of your app, you can
                             // specify an empty string.
              .execute();

String accessToken = tokenResponse.getAccessToken();

// Use access token to call API
GoogleCredential credential = new GoogleCredential().setAccessToken(accessToken);
Drive drive =
    new Drive.Builder(new NetHttpTransport(), JacksonFactory.getDefaultInstance(), credential)
        .setApplicationName("Auth Code Exchange Demo")
        .build();
File file = drive.files().get("appfolder").execute();

// Get profile info from ID token
GoogleIdToken idToken = tokenResponse.parseIdToken();
GoogleIdToken.Payload payload = idToken.getPayload();
String userId = payload.getSubject();  // Use this value as a key to identify a user.
String email = payload.getEmail();
boolean emailVerified = Boolean.valueOf(payload.getEmailVerified());
String name = (String) payload.get("name");
String pictureUrl = (String) payload.get("picture");
String locale = (String) payload.get("locale");
String familyName = (String) payload.get("family_name");
String givenName = (String) payload.get("given_name");
Python
from apiclient import discovery
import httplib2
from oauth2client import client

# (Receive auth_code by HTTPS POST)


# If this request does not have `X-Requested-With` header, this could be a CSRF
if not request.headers.get('X-Requested-With'):
    abort(403)

# Set path to the Web application client_secret_*.json file you downloaded from the
# Google API Console: https://console.cloud.google.com/apis/credentials
CLIENT_SECRET_FILE = '/path/to/client_secret.json'

# Exchange auth code for access token, refresh token, and ID token
credentials = client.credentials_from_clientsecrets_and_code(
    CLIENT_SECRET_FILE,
    ['https://www.googleapis.com/auth/drive.appdata', 'profile', 'email'],
    auth_code)

# Call Google API
http_auth = credentials.authorize(httplib2.Http())
drive_service = discovery.build('drive', 'v3', http=http_auth)
appfolder = drive_service.files().get(fileId='appfolder').execute()

# Get profile info from ID token
userid = credentials.id_token['sub']
email = credentials.id_token['email']