Uwierzytelnianie za pomocą serwera backendu

Jeśli używasz Logowania przez Google w aplikacji lub witrynie, która komunikuje się z serwerem backendu, może być konieczne zidentyfikowanie aktualnie zalogowanego użytkownika na serwerze. Aby zrobić to w bezpieczny sposób, po zalogowaniu się użytkownika wyślij jego token identyfikatora na swój serwer przy użyciu protokołu HTTPS. Następnie sprawdź na serwerze integralność tokena tożsamości i wykorzystaj zawarte w nim informacje o użytkowniku do zainicjowania sesji lub utworzenia nowego konta.

Wyślij token identyfikatora na swój serwer

Gdy użytkownik się zaloguje, pobierz jego token identyfikatora:

Swift

GIDSignIn.sharedInstance.signIn(withPresenting: self) { signInResult, error in
    guard error == nil else { return }
    guard let signInResult = signInResult else { return }

    signInResult.user.refreshTokensIfNeeded { user, error in
        guard error == nil else { return }
        guard let user = user else { return }

        let idToken = user.idToken
        // Send ID token to backend (example below).
    }
}

Objective-C

[GIDSignIn.sharedInstance signInWithPresentingViewController:self
                                              completion:^(GIDSignInResult * _Nullable signInResult,
                                                           NSError * _Nullable error) {
      if (error) { return; }
      if (signInResult == nil) { return; }

      [signInResult.user refreshTokensIfNeededWithCompletion:^(GIDGoogleUser * _Nullable user,
                                                               NSError * _Nullable error) {
          if (error) { return; }
          if (user == nil) { return; }

          NSString *idToken = user.idToken;
          // Send ID token to backend (example below).
      }];
}];

Następnie wyślij token identyfikatora na swój serwer za pomocą żądania HTTPS POST:

Swift

func tokenSignInExample(idToken: String) {
    guard let authData = try? JSONEncoder().encode(["idToken": idToken]) else {
        return
    }
    let url = URL(string: "https://yourbackend.example.com/tokensignin")!
    var request = URLRequest(url: url)
    request.httpMethod = "POST"
    request.setValue("application/json", forHTTPHeaderField: "Content-Type")

    let task = URLSession.shared.uploadTask(with: request, from: authData) { data, response, error in
        // Handle response from your backend.
    }
    task.resume()
}

Objective-C

NSString *signinEndpoint = @"https://yourbackend.example.com/tokensignin";
NSDictionary *params = @{@"idtoken": idToken};

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:signinEndpoint];
[request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
[request setHTTPMethod:@"POST"];
[request setHTTPBody:[self httpBodyForParamsDictionary:params]];

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:request
                                   queue:queue
                       completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
                         if (error) {
                           NSLog(@"Error: %@", error.localizedDescription);
                         } else {
                           NSLog(@"Signed in as %@", data.bytes);
                         }
                       }];

Sprawdź integralność tokena tożsamości

Po otrzymaniu tokena identyfikatora za pomocą HTTPS POST musisz zweryfikować jego integralność.

Aby sprawdzić, czy token jest prawidłowy, sprawdź, czy są spełnione te kryteria:

  • Token tożsamości jest prawidłowo podpisany przez Google. Do weryfikacji podpisu tokena użyj kluczy publicznych Google (dostępnych w formacie JWK lub PEM). Te klucze są regularnie poddawane rotacji. Sprawdź nagłówek Cache-Control w odpowiedzi, aby określić, kiedy należy je ponownie pobrać.
  • Wartość aud w tokenie identyfikatora jest równa jednemu z identyfikatorów klienta Twojej aplikacji. Ta weryfikacja jest konieczna, by zapobiec używaniu tokenów tożsamości wydanych do szkodliwej aplikacji do uzyskiwania dostępu do danych o tym samym użytkowniku na serwerze backendu aplikacji.
  • Wartość iss w tokenie identyfikatora jest równa accounts.google.com lub https://accounts.google.com.
  • Data ważności (exp) tokena identyfikatora jeszcze nie upłynął.
  • Aby sprawdzić, czy token identyfikatora reprezentuje konto organizacji w Google Workspace lub Cloud, możesz zapoznać się z deklaracją hd, która wskazuje hostowaną domenę użytkownika. Tej opcji należy używać, gdy ograniczasz dostęp do zasobu tylko do użytkowników z określonych domen. Brak takiego roszczenia oznacza, że konto nie należy do domeny hostowanej przez Google.

Zamiast pisać własny kod do wykonywania tych czynności weryfikacyjnych, zdecydowanie zalecamy użycie na platformie biblioteki klienta interfejsu API Google lub ogólnej biblioteki JWT. Na potrzeby programowania i debugowania możesz wywołać nasz punkt końcowy weryfikacji tokeninfo.

Korzystanie z biblioteki klienta interfejsów API Google

Użycie jednej z bibliotek klienta interfejsu API Google (np. Java, Node.js, PHP lub Python) to zalecany sposób weryfikacji tokenów identyfikatorów Google w środowisku produkcyjnym.

Java

Aby zweryfikować token identyfikatora w Javie, użyj obiektu GoogleIdTokenVerifier. Na przykład:

import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken.Payload;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier;

...

GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(transport, jsonFactory)
    // Specify the CLIENT_ID of the app that accesses the backend:
    .setAudience(Collections.singletonList(CLIENT_ID))
    // Or, if multiple clients access the backend:
    //.setAudience(Arrays.asList(CLIENT_ID_1, CLIENT_ID_2, CLIENT_ID_3))
    .build();

// (Receive idTokenString by HTTPS POST)

GoogleIdToken idToken = verifier.verify(idTokenString);
if (idToken != null) {
  Payload payload = idToken.getPayload();

  // Print user identifier
  String userId = payload.getSubject();
  System.out.println("User ID: " + userId);

  // Get profile information from payload
  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");

  // Use or store profile information
  // ...

} else {
  System.out.println("Invalid ID token.");
}

Metoda GoogleIdTokenVerifier.verify() weryfikuje podpis JWT, deklaracje aud, iss i exp.

Jeśli chcesz sprawdzić, czy token identyfikatora reprezentuje konto organizacji w Google Workspace lub Cloud, możesz zweryfikować zgłoszenie hd, sprawdzając nazwę domeny zwracaną przez metodę Payload.getHostedDomain(). Domena w zgłoszeniu email nie jest wystarczająca, aby upewnić się, że konto jest zarządzane przez domenę lub organizację.

Node.js

Aby zweryfikować token identyfikatora w Node.js, użyj biblioteki uwierzytelniania Google dla Node.js. Zainstaluj bibliotekę:

npm install google-auth-library --save
Następnie wywołaj funkcję verifyIdToken(). Na przykład:

const {OAuth2Client} = require('google-auth-library');
const client = new OAuth2Client();
async function verify() {
  const ticket = await client.verifyIdToken({
      idToken: token,
      audience: CLIENT_ID,  // Specify the CLIENT_ID of the app that accesses the backend
      // Or, if multiple clients access the backend:
      //[CLIENT_ID_1, CLIENT_ID_2, CLIENT_ID_3]
  });
  const payload = ticket.getPayload();
  const userid = payload['sub'];
  // If request specified a G Suite domain:
  // const domain = payload['hd'];
}
verify().catch(console.error);

Funkcja verifyIdToken weryfikuje podpis JWT, aud, exp i iss.

Aby sprawdzić, czy token identyfikatora reprezentuje konto organizacji w Google Workspace lub Cloud, możesz zapoznać się z deklaracją hd, która wskazuje hostowaną domenę użytkownika. Tej opcji należy używać, gdy ograniczasz dostęp do zasobu tylko do użytkowników z określonych domen. Brak takiego roszczenia oznacza, że konto nie należy do domeny hostowanej przez Google.

PHP

Aby zweryfikować token identyfikatora w języku PHP, użyj biblioteki klienta interfejsu API Google dla języka PHP. Zainstaluj bibliotekę (na przykład za pomocą narzędzia Composer):

composer require google/apiclient
Następnie wywołaj funkcję verifyIdToken(). Na przykład:

require_once 'vendor/autoload.php';

// Get $id_token via HTTPS POST.

$client = new Google_Client(['client_id' => $CLIENT_ID]);  // Specify the CLIENT_ID of the app that accesses the backend
$payload = $client->verifyIdToken($id_token);
if ($payload) {
  $userid = $payload['sub'];
  // If request specified a G Suite domain:
  //$domain = $payload['hd'];
} else {
  // Invalid ID token
}

Funkcja verifyIdToken weryfikuje podpis JWT, aud, exp i iss.

Aby sprawdzić, czy token identyfikatora reprezentuje konto organizacji w Google Workspace lub Cloud, możesz zapoznać się z deklaracją hd, która wskazuje hostowaną domenę użytkownika. Tej opcji należy używać, gdy ograniczasz dostęp do zasobu tylko do użytkowników z określonych domen. Brak takiego roszczenia oznacza, że konto nie należy do domeny hostowanej przez Google.

Python

Aby zweryfikować token identyfikatora w Pythonie, użyj funkcji verify_oauth2_token. Na przykład:

from google.oauth2 import id_token
from google.auth.transport import requests

# (Receive token by HTTPS POST)
# ...

try:
    # Specify the CLIENT_ID of the app that accesses the backend:
    idinfo = id_token.verify_oauth2_token(token, requests.Request(), CLIENT_ID)

    # Or, if multiple clients access the backend server:
    # idinfo = id_token.verify_oauth2_token(token, requests.Request())
    # if idinfo['aud'] not in [CLIENT_ID_1, CLIENT_ID_2, CLIENT_ID_3]:
    #     raise ValueError('Could not verify audience.')

    # If auth request is from a G Suite domain:
    # if idinfo['hd'] != GSUITE_DOMAIN_NAME:
    #     raise ValueError('Wrong hosted domain.')

    # ID token is valid. Get the user's Google Account ID from the decoded token.
    userid = idinfo['sub']
except ValueError:
    # Invalid token
    pass

Funkcja verify_oauth2_token weryfikuje podpis JWT, deklarację aud i deklarację exp. Musisz też zweryfikować żądanie hd (jeśli dotyczy), sprawdzając obiekt zwracany przez verify_oauth2_token. Jeśli do serwera backendu uzyskuje dostęp wielu klientów, również ręcznie zweryfikuj deklarację aud.

Wywoływanie punktu końcowego tokeninfo

Łatwym sposobem na zweryfikowanie podpisu tokena identyfikatora na potrzeby debugowania jest użycie punktu końcowego tokeninfo. Wywołanie tego punktu końcowego obejmuje dodatkowe żądanie sieciowe, które wykonuje większość weryfikacji za Ciebie, a jednocześnie testujesz prawidłową weryfikację i wyodrębnianie ładunków we własnym kodzie. Nie można go stosować w kodzie produkcyjnym, ponieważ żądania mogą być ograniczane lub w inny sposób powodować przejściowe błędy.

Aby zweryfikować token identyfikatora za pomocą punktu końcowego tokeninfo, wyślij żądanie HTTPS POST lub GET do punktu końcowego i przekaż token identyfikatora w parametrze id_token. Aby na przykład zweryfikować token „XYZ123”, wyślij następujące żądanie GET:

https://oauth2.googleapis.com/tokeninfo?id_token=XYZ123

Jeśli token jest prawidłowo podpisany, a żądania iss i exp mają oczekiwane wartości, otrzymasz odpowiedź HTTP 200, w której treść zawiera żądania tokena identyfikatora w formacie JSON. Przykładowa odpowiedź:

{
 // These six fields are included in all Google ID Tokens.
 "iss": "https://accounts.google.com",
 "sub": "110169484474386276334",
 "azp": "1008719970978-hb24n2dstb40o45d4feuo2ukqmcc6381.apps.googleusercontent.com",
 "aud": "1008719970978-hb24n2dstb40o45d4feuo2ukqmcc6381.apps.googleusercontent.com",
 "iat": "1433978353",
 "exp": "1433981953",

 // These seven fields are only included when the user has granted the "profile" and
 // "email" OAuth scopes to the application.
 "email": "testuser@gmail.com",
 "email_verified": "true",
 "name" : "Test User",
 "picture": "https://lh4.googleusercontent.com/-kYgzyAWpZzJ/ABCDEFGHI/AAAJKLMNOP/tIXL9Ir44LE/s99-c/photo.jpg",
 "given_name": "Test",
 "family_name": "User",
 "locale": "en"
}

Aby sprawdzić, czy token identyfikatora reprezentuje konto Google Workspace, możesz zapoznać się z deklaracją hd, która wskazuje domenę hostowaną użytkownika. Tej opcji należy używać na potrzeby ograniczania dostępu do zasobu tylko osobom z określonych domen. Brak takiego stwierdzenia oznacza, że konto nie należy do domeny hostowanej w Google Workspace.

Tworzenie konta lub sesji

Po zweryfikowaniu tokena sprawdź, czy użytkownik znajduje się już w bazie danych użytkowników. Jeśli tak, ustanowij sesję uwierzytelnioną dla użytkownika. Jeśli użytkownika nie ma jeszcze w bazie danych, utwórz nowy rekord użytkownika na podstawie informacji z ładunku tokena identyfikatora i załóż dla niego sesję. Po wykryciu nowo utworzonego użytkownika w aplikacji możesz poprosić użytkownika o podanie wszelkich dodatkowych informacji profilowych, których potrzebujesz.

Zabezpieczanie kont użytkowników za pomocą ochrony obejmującej wiele kont

Logując użytkowników przez Google, automatycznie korzystasz ze wszystkich funkcji zabezpieczeń i infrastruktury Google, które chronią dane tego użytkownika. Jednak w mało prawdopodobnym przypadku naruszenia bezpieczeństwa konta Google użytkownika lub wystąpienia innego ważnego zdarzenia związanego z bezpieczeństwem aplikacja też może być podatna na atak. Aby lepiej chronić swoje konta przed ważnymi zdarzeniami związanymi z bezpieczeństwem, korzystaj z Ochrony wszystkich kont, dzięki której możesz otrzymywać alerty zabezpieczeń od Google. Gdy otrzymasz takie zdarzenia, uzyskasz wgląd w ważne zmiany dotyczące bezpieczeństwa konta Google użytkownika i możesz podjąć odpowiednie działania w swojej usłudze, aby zabezpieczyć swoje konta.