Logowanie przez Google w aplikacjach po stronie serwera

Aby korzystać z usług Google w imieniu użytkownika, gdy jest on offline, musisz użyć hybrydowego procesu po stronie serwera, w którym użytkownik autoryzuje aplikację po stronie klienta za pomocą klienta JavaScript API, a Ty wysyłasz specjalny jednorazowy kod autoryzacji na swój serwer. Serwer wymienia ten jednorazowy kod, aby uzyskać od Google własny token dostępu i tokeny odświeżania, aby umożliwić serwerowi wykonywanie własnych wywołań interfejsu API, które mogą być wykonywane, gdy użytkownik jest offline. Ten jednorazowy przepływ kodu ma większe zalety w zakresie bezpieczeństwa niż zwykły przepływ po stronie serwera i w porównaniu z wysyłaniem tokenów dostępu do serwera.

Poniżej przedstawiono proces logowania się, który umożliwia uzyskanie tokena dostępu aplikacji po stronie serwera.

Kody jednorazowe mają kilka zalet dotyczących bezpieczeństwa. Dzięki kodom Google dostarcza tokeny bezpośrednio na Twój serwer, bez żadnych pośredników. Chociaż nie zalecamy ujawniania kodów, jest to bardzo trudne w użyciu bez tajnego klucza klienta. Nie ujawniaj swojego klienta.

Implementacja jednorazowego kodu

Przycisk logowania przez Google zapewnia zarówno token dostępu, jak i kod autoryzacji. Jest to jednorazowy kod, który serwer może wymieniać z serwerami Google, aby uzyskać token dostępu.

Poniższy przykładowy kod pokazuje, jak wykonać przepływ kodu jednorazowego.

Uwierzytelnianie logowania przez Google za pomocą jednorazowego kodu wymaga:

Krok 1. Utwórz identyfikator klienta i tajny klucz klienta

Aby utworzyć identyfikator klienta i tajny klucz klienta, utwórz projekt Konsoli interfejsów API Google, skonfiguruj identyfikator klienta OAuth i zarejestruj źródła JavaScript:

  1. Otwórz konsolę interfejsów API Google.

  2. Z menu projektu wybierz istniejący projekt lub utwórz nowy, klikając Utwórz nowy projekt.

  3. Na pasku bocznym w sekcji „Interfejsy API i usługi” wybierz Dane logowania, a następnie kliknij Skonfiguruj ekran akceptacji.

    Wybierz Adres e-mail, podaj nazwę produktu i kliknij Zapisz.

  4. Na karcie Dane logowania wybierz listę Utwórz dane logowania i wybierz Identyfikator klienta OAuth.

  5. W sekcji Typ aplikacji wybierz Aplikacja internetowa.

    Zarejestruj źródła, z których aplikacja może uzyskiwać dostęp do interfejsów API Google, w podany niżej sposób. Źródło to unikalna kombinacja protokołu, nazwy hosta i portu.

    1. W polu Autoryzowane źródła JavaScript wpisz źródło aplikacji. Możesz podać wiele źródeł, aby umożliwić uruchamianie aplikacji za pomocą różnych protokołów, domen i subdomen. Nie można używać symboli wieloznacznych. W przykładzie poniżej drugi URL może być adresem produkcyjnym.

      http://localhost:8080
      https://myproductionurl.example.com
      
    2. Pole Autoryzowany identyfikator URI przekierowania nie wymaga podania wartości. Identyfikatory URI przekierowania nie są używane z interfejsami API JavaScript.

    3. Kliknij przycisk Utwórz.

  6. W wyświetlonym oknie Klient OAuth skopiuj identyfikator klienta. Identyfikator klienta umożliwia aplikacji dostęp do włączonych interfejsów API Google.

Krok 2. Umieść na stronie bibliotekę platformy Google

Dołącz te skrypty przedstawiające anonimową funkcję, która wstawia skrypt do DOM tej strony internetowej 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 -->

Krok 3. Zainicjuj obiekt GoogleAuth

Wczytaj bibliotekę auth2 i wywołaj gapi.auth2.init(), aby zainicjować obiekt GoogleAuth. Określ identyfikator klienta i zakresy, które mają być żądane przy wywołaniu funkcji 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>

Krok 4. Dodaj do swojej strony przycisk logowania

Dodaj do swojej strony internetowej przycisk logowania i dołącz moduł obsługi kliknięć do wywołania metody grantOfflineAccess(), aby rozpocząć procedurę jednorazowego kodu.

<!-- 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>

Krok 5. Zaloguj się na konto użytkownika

Użytkownik klika przycisk logowania i przyznaje aplikacji dostęp do żądanych uprawnień. Następnie funkcja wywołania zwrotnego określona w metodzie grantOfflineAccess().then() przekazuje obiekt JSON z kodem autoryzacji. Na przykład:

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

Krok 6. Wyślij kod autoryzacji na serwer

code to Twój jednorazowy kod, który serwer może wymienić na własny token dostępu i token odświeżania. Token odświeżania możesz uzyskać dopiero wtedy, gdy użytkownikowi wyświetli się okno autoryzacji z prośbą o dostęp offline. Jeśli select-account prompt został określony wOfflineAccessOptions kroku 4, musisz zapisać pobrany token odświeżania do późniejszego użycia, ponieważ kolejne giełdy zwracają wartość null dla tokena odświeżania. Ten proces zapewnia większe bezpieczeństwo w porównaniu ze standardowym przepływem OAuth 2.0.

Tokeny dostępu są zawsze zwracane wraz z wymianą prawidłowego kodu autoryzacji.

Poniższy skrypt definiuje funkcję wywołania zwrotnego dla przycisku logowania. Gdy logowanie się powiedzie, funkcja zapisuje token dostępu do użycia po stronie klienta i wysyła jednorazowy kod do serwera w tej samej domenie.

<!-- 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>

Krok 7. Wymień kod autoryzacji na token dostępu

Wymieniaj kod autoryzacji na serwerze na tokeny dostępu i odśwież. Za pomocą tokena dostępu możesz wywoływać interfejsy API Google w imieniu użytkownika i opcjonalnie przechowywać token odświeżania, aby po jego wygaśnięciu uzyskać nowy token dostępu.

Jeśli poprosisz o dostęp do profilu, otrzymasz też token tożsamości zawierający podstawowe informacje o profilu użytkownika.

Na przykład:

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']