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:
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.
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.
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.
Po poprawnym uzupełnieniu formularza otrzymujemy identyfikator klienta, który jest wykorzystywany do komunikacji z Google API.
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:
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:
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
Odpowiedz lub skomentuj