Logowanie przez Google w aplikacjach po stronie serwera

Aby korzystać z usług Google w imieniu użytkownika, gdy ten jest offline, musisz użyć hybrydowego procesu po stronie serwera, w którym użytkownik autoryzuje Twoją aplikację po stronie klienta za pomocą klienta interfejsu JavaScript API, a Ty wysyłasz na serwer specjalny jednorazowy kod autoryzacji. Serwer wymienia ten kod jednorazowego użytku na tokeny dostępu i odświeżania od Google, aby móc wykonywać własne wywołania interfejsu API, nawet gdy użytkownik jest offline. Ten proces generowania jednorazowego kodu ma zalety związane z bezpieczeństwem w porównaniu z procesem czysto serwerowym i wysyłaniem tokenów dostępu na serwer.

Poniżej przedstawiamy proces logowania się w celu uzyskania tokena dostępu do aplikacji po stronie serwera.

Jednorazowe kody mają kilka zalet związanych z bezpieczeństwem. W przypadku kodów Google dostarcza tokeny bezpośrednio na Twój serwer bez pośredników. Nie zalecamy udostępniania kodów, ale bez tajnego klucza klienta są one bardzo trudne do użycia. Trzymaj tajny klucz klienta w tajemnicy.

Implementacja procesu weryfikacji za pomocą kodu jednorazowego

Przycisk Zaloguj się przez Google udostępnia zarówno token dostępu, jak i kod autoryzacji. Kod jest kodem jednorazowym, który Twój serwer może wymienić z serwerami Google na token dostępu.

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

Aby uwierzytelnić logowanie Google za pomocą kodu jednorazowego:

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

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

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

  2. W menu Projekt 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” kliknij Dane logowania, a potem Skonfiguruj ekran zgody.

    Wybierz adres e-mail, określ nazwę produktu i kliknij Zapisz.

  4. Na karcie Dane logowania kliknij menu Utwórz dane logowania i wybierz Identyfikator klienta OAuth.

  5. W sekcji Typ aplikacji wybierz Aplikacja internetowa.

    W ten sposób zarejestruj źródła, z których Twoja aplikacja może uzyskiwać dostęp do interfejsów API Google: Źródło to niepowtarzalne połączenie protokołu, nazwy hosta i portu.

    1. W polu Authorized JavaScript origins (Autoryzowane źródła JavaScriptu) wpisz źródło aplikacji. Możesz podać wiele źródeł, aby umożliwić aplikacji działanie na różnych protokołach, domenach lub subdomenach. Nie możesz używać symboli wieloznacznych. W przykładzie poniżej drugi adres URL może być adresem URL wersji produkcyjnej.

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

    3. Naciśnij 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 Google API.

Krok 2. Uwzględnij bibliotekę platformy Google na swojej stronie

Umieść poniższe skrypty, które demonstrują anonimową funkcję, która wstawia skrypt do modelu 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. Inicjuj obiekt GoogleAuth

Wczytaj bibliotekę auth2 i wywołaj funkcję gapi.auth2.init(), aby zainicjować obiekt GoogleAuth. Podczas wywołania funkcji init() podaj identyfikator klienta i zakresy, których chcesz użyć.

<!-- 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 przycisk logowania na stronie

Dodaj przycisk logowania do swojej strony internetowej i dołącz do niego uchwyt kliknięcia, aby wywołać funkcję grantOfflineAccess(), która rozpocznie proces weryfikacji za pomocą kodu jednorazowego.

<!-- 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 użytkownika

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

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

Krok 6. Wyślij kod autoryzacji na serwer

code to 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 po wyświetleniu użytkownikowi okna autoryzacji z prośbą o dostęp offline. Jeśli w kroku 4 określisz wartość select-account promptOfflineAccessOptions, musisz przechowywać token odświeżania, który pobierzesz, aby użyć go później, ponieważ kolejne wymiany zwrócą wartość null dla tokena odświeżania. Ten proces zapewnia wyższy poziom zabezpieczeń niż standardowy proces OAuth 2.0.

Tokeny dostępu są zawsze zwracane w wyniku wymiany na prawidłowy kod autoryzacji.

Ten skrypt definiuje funkcję wywołania zwrotnego dla przycisku logowania. Po pomyślnym zalogowaniu funkcja przechowuje token dostępu do użycia po stronie klienta i wysyła kod jednorazowy na serwer 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

Na serwerze zamienić kod autoryzacji na tokeny dostępu i odświeżania. Używaj tokena dostępu do wywoływania interfejsów API Google w imieniu użytkownika i opcjonalnie przechowuj token odświeżania, aby po upływie jego ważności pobrać nowy token dostępu.

Jeśli żądasz dostępu do profilu, otrzymasz też token identyfikacyjny 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']