Web Design Blog / Programowanie:

Integracja logowania Google Sign-In w PHP

Data publikacji: 20 czerwca 2018

Dawno nie było tutorialu dla programistów PHP / JavaScript. W tym wpisie przebrniemy przez proces integracji Front-Endu i Back-Endu z Google API na przykładzie usługi Google Sign-In, czyli niesamowicie użytecznej i ułatwiającej życie funkcji „Zaloguj przez Google„Czyli logowania z wykorzystaniem zaufanej strony trzeciej (federated login).

Jakie są korzyści z posiadania funkcji „Zaloguj przez Google”?

Przeciętny użytkownik Internetu korzysta obecnie z coraz większej ilości aplikacji i usług internetowych, które wymagają logowania. To powoduje, że ustalane hasła są trudne zapamiętania lub co gorsza, użytkownicy ustalają kilka haseł, które są wykorzystywane na wielu różnych serwisach co jest ryzykowne z punktu widzenia bezpieczeństwa.

Kolejnym problemem jest User Experience. Jak wiadomo konieczność wprowadzania od nowa w każdej kolejnej aplikacji ciągle tych samych danych tj. imię, nazwisko, adres e-mail, data urodzenia przyprawia o co najmniej ból głowy lub w skrajnych przypadkach rezygnujemy z wypełniania formularza złożonego z kilkunastu pól.

Rozwiązaniem na takie problemy jest między innymi „Federated login” czyli uwierzytelnianie i uzupełnienie profilu użytkownika z wykorzystaniem tożsamości przechowywanej na koncie w zaufanym serwisie zewnętrznym. Jak widać, Federated login rozwiązuje problem zarówno użytkowników jak i deweloperów.

Zasada działania Google Sign-In

Jednym z możliwych sposobów integracji Google Sign-In we własnej aplikacji jest umieszczenie Biblioteki Platformy Google (Google Platform Library) i wygenerowanie za jego pomocą po stronie klienta przycisku, który prześle do back-endu niezbędne informacje do uwierzytelnienia użytkownika:

Przycisk Google Sign-In

Poprawna implementacja polega na wykorzystaniu otrzymywanego przy każdym logowaniu unikalnego ID_Token, który jest przesyłany do back-endu za pomocą bezpiecznego połączenia SSL.

Proces logowania za pomocą Google Sign-In

Otrzymany ID_Token jest rozszyfrowany za pomocą biblioteki Google, która trzeba zainstalować na serwerze. W aplikacji identyfikujemy użytkownika za pomocą pola „subject”, które jest unikalne dla każdego wiązania użytkownik – aplikacja.

Krok 1: Utworzenie nowego projektu w Konsoli „Google for Developers”

Wypełniamy wszystkie pola według etykiet. Nie zapomnijmy dodać jakieś polityki prywatności bo Google nie pozwoli nam na logowanie się użytkowników.

Nowy projekt w Konsoli dla deweloperów

 

Krok 2: Stworzenie identyfikatora OAuth

Następnym krokiem jest stworzenie identyfikatora OAuth. Do celów bezpieczeństwa należy określić źródło kodu JavaScript oraz podać URI do przekierowania.

Tworzenie identyfikatora i klucz OAuth

 

Po poprawnym uzupełnieniu formularza otrzymujemy identyfikator klienta, który jest wykorzystywany do komunikacji z Google API.

Identyfikator i klucz tajny OAuth

Krok 3: Integracja po stronie Front-Endu (HTML, JavaScript)

Integracja po stronie klienta polega na dodaniu kodu znacznika meta „google-signin-client_id” z otrzymanym identyfikatorem klienta OAuth oraz dodaniu wcześniej wspomnianej biblioteki Google Platform.

W sekcji head dodajemy:

<meta name="google-signin-scope" content="profile email">
<meta name="google-signin-client_id" content="*** ID otrzymany w poprzednim kroku ***.apps.googleusercontent.com">
<script src="https://apis.google.com/js/platform.js" async defer>
{lang:'pl'}
</script>

Linijka lang: ‚pl’ jak można się domyślić podmienia napis na przycisku z „Sign-In” na „Zaloguj się”.

A w miejscu gdzie chcemy wyświetlić przycisk:

<div class="g-signin2" data-onsuccess="onSignIn" data-theme="dark"></div>

Teraz jeszcze pozostaje nam pobrać id_token i przesłać go do back-endu. Dla wygody skorzystamy z metody post w jQuery:

<script>
  function onSignIn(googleUser) {
    // Przykładowe dane które możemy pobrać we frontendzie
    var profile = googleUser.getBasicProfile();
    console.log("ID: " + profile.getId()); // Pod żadnym pozorem nie wysyłany tego do back-endu!
    console.log('Nazwa: ' + profile.getName());
    console.log('Imię: ' + profile.getGivenName());
    console.log('Nazwisko: ' + profile.getFamilyName());
    console.log("Adres URL do zdjęcia profilowego: " + profile.getImageUrl());
    console.log("E-mail: " + profile.getEmail());
  
    // To w tym miejscu pobieramy id_token:
    var id_token = googleUser.getAuthResponse().id_token;
    console.log("ID Token: " + id_token);
    $.post("https://w3tech.pl/public/user/googlelogin", {token: id_token}, function(output){   
    //ścieżka do metody PHP obsługującej przesyłany id_token

    var data = $.parseJSON(output);

    if (data['status'] == "1"){
      window.location.href="https://[ścieżka docelowa po zalogowaniu]";
    }
    if (data['status'] == "2"){
      location.reload(); //Nieudane logowanie? Jeszcze raz...
    }
  });
  };
</script>

Dzięki temu możemy wywołać okno, przez które logują użytkownicy:

Okno logowania

 

Kod HTML i JavaScript na stronie do logowania / rejestracji

Na tym etapie decydujemy jakich uprawnień będziemy wymagać od użytkownika. Dla celów projektu  wystarczą nam podstawowe informacje o profilu oraz e-mail, co deklarujemy również w w znaczniku meta „google-sign-in-scope”. Użytkownik udziela je nam w następnym kroku jest to spełnienie zasady „incremental authorization” polegającej na tym aby aplikacja żądała konkretnych uprawnień wtedy kiedy to konieczne.

Tworzymy obiekt profilu za pomocą googleUser.getBasicProfile(); następnie pobieramy token za pomocą googleUser.getAuthResponse().id_token; Otrzymany token wysyłamy za pomocą jQuery.post.

Token przechowuje dane o użytkowniku w sposób niejawny co uniemożliwia podmianę danych po stronie klienta, przykładowy id_token wygląda mniej więcej tak:

OAuth id_token przykład

Krok 4: Integracja po stronie Back-Endu

!!! Ważna uwaga !!! Nie można identyfikować użytkowników za pomocą pola ID lub e-mail, do których mamy dostęp po zalogowaniu z poziomu klienta, ponieważ taki sposób identyfikacji były podatny na podmienianie przesyłanych do back-endu danych.

Aby zweryfikować integralność id_token musimy zainstalować na serwerze Google API Client Library za pomocą Composera.

Zainstaluj Bibliotekę Google API z poziomu composera:

composer require google/apiclient

Teraz przechodzimy do logiki naszej istniejącej aplikacji. Na początku warto byłoby dołączyć bibliotekę:

require_once '../../../vendor/autoload.php';

Pobieramy id_token z Front-Endu:

$id_token = $_POST["token"];

W miejscu gdzie jest nam to potrzebne, korzystamy z wbudowanej w bibliotece metod:

$client_id = '** ID otrzymany w poprzednim kroku **.apps.googleusercontent.com';

$client = new Google_Client(['client_id' => $client_id]);
$payload = $client->verifyIdToken($id_token);
if ($payload) {

$sub = $payload['sub'];
$aud = $payload['aud'];
$email = $payload['email'];
$nazwa = $payload['name'];

} else {
// Tutaj można zwrócić błąd braku weryfikacji i przerwać akcję (błąd w kodzie, kluczach lub atak)
}

Przesłany do back-endu token umożliwia otrzymanie niepublicznego pola „sub” dzieje się to za pomocą opisanych w dokumentacji metod verifyIdToken($id_token). Wartość „subject” będzie służyła nam do jednoznacznej identyfikacji użytkownika.

To tyle! W zmiennej $sub mamy unikalny klucz klienta, który na upartego można spokojnie przechowywać w bazie danych w jawnej postaci.

Krok 5: Dostosowanie back-endu istniejącej aplikacji

Reszta pracy będzie polegała na dostosowaniu wewnętrznych metod do współpracy z nową metodą logowania. Bazując na opisywanym już własnym Frameworku MVC dla PHP  po prostu dodałem dodatkową metodę w kontrolerze user.php:

public function googlelogin(){
$user = $this -> model(„Uzytkownicy”);
$user -> zalogujUzytkownikaGoogle($_POST[„token”]);

echo $output;
}

w modelu Uzytkownicy.php, metoda public function zalogujUzytkownikaGoogle($token = „”);

Ustawia sesję zalogowania lub zwraca ewentualny błąd.

Pole „sub” jest przechowywane może być przechowywane np. w polu „passsword” taki zabieg zwalnia z konieczności modyfikacji bazy danych. Przy każdym logowaniu aplikacja sprawdza czy konkretny użytkownik istnieje. Jeżeli nie, rejestruje nowego użytkownika, a jeżeli tak – loguje.

Podsumowanie

Jak widać integracja aplikacji internetowej z usługą Google Sign-In jest banalnie prosta. Pamiętaj aby do uwierzytelniania używać pola subject, które jest bezpiecznie wyciągane w back-endzie aplikacji.

Źródła

https://developers.google.com/identity/sign-in/web/sign-in

https://developers.google.com/identity/sign-in/web/backend-auth

 


Komentarze

Brak komentarzy.

Dodaj swój komentarz