Login do Google para apps do lado do servidor

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

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

Os códigos únicos têm várias vantagens de segurança. Com os códigos, o Google fornece tokens diretamente para seu servidor, sem intermediários. Não recomendamos o vazamento de códigos, mas eles são muito difíceis de usar sem a chave secreta do cliente. Mantenha a chave secreta do cliente.

Implementar o fluxo de código único

O botão de Login do Google fornece um token de acesso e um código de autorização. Ele é um código único que pode ser trocado pelo seu servidor 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 do Console de APIs do Google, configure um ID do cliente OAuth e registre as origens do JavaScript:

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

  2. Na lista suspensa 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 o nome do produto e pressione Save.

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

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

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

    1. No campo Origens JavaScript autorizadas, insira a origem do seu app. É 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 pode 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 o app acesse APIs do Google ativadas.

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

Inclua os scripts a seguir, que demonstram uma função anônima que insere um script no DOM da 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 o 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 login do usuário

O usuário clica no botão de login e concede ao app acesso às permissões que você solicitou. 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 é um código único que o servidor pode trocar pelo próprio token de acesso e de atualização. Só é possível receber um token de atualização depois que o usuário recebe uma caixa de diálogo de autorização solicitando acesso off-line. Se você especificou o select-account prompt no OfflineAccessOptions na etapa 4, armazene 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 no 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 autenticação por tokens de acesso e de atualização. Use o token de acesso para chamar APIs do Google em nome do usuário e, como opção, armazene o token de atualização para receber um novo token de acesso quando ele expirar.

Se você solicitou acesso ao perfil, também receberá um token de ID com as 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']