Korzystanie z modelu tokena

Biblioteka JavaScript google.accounts.oauth2 pomaga uzyskać zgodę użytkownika i pozyskać token dostępu do pracy z danymi użytkownika. Jest ono oparte na procesie domyślnego udzielania uprawnień w protokole OAuth 2.0 i ma na celu umożliwienie bezpośredniego wywoływania interfejsów API Google za pomocą protokołów REST i CORS lub korzystania z naszej biblioteki klienta interfejsów API Google na potrzeby JavaScriptu (znanej też jako gapi.client) w celu prostego i elastycznego uzyskiwania dostępu do bardziej złożonych interfejsów API.

Zanim użytkownicy uzyskają dostęp do chronionych danych w przeglądarce, w Twojej witrynie uruchamiają procesy wyboru konta Google w przeglądarce, logowania i wyrażania zgody. Na koniec serwery OAuth Google wydają i zwracają token dostępu do Twojej aplikacji internetowej.

W przypadku modelu autoryzacji opartego na tokenach nie trzeba przechowywać na serwerze backendu tokenów odświeżania dla poszczególnych użytkowników.

Zalecamy stosowanie opisanej tutaj metody zamiast technik opisanych w starszym przewodniku OAuth 2.0 w przypadku aplikacji internetowych działających po stronie klienta.

Konfiguracja

Aby znaleźć lub utworzyć identyfikator klienta, wykonaj czynności opisane w tym przewodniku: Uzyskiwanie identyfikatora klienta Google API. Następnie dodaj bibliotekę klienta do stron w witrynie, które będą wywoływać interfejsy API Google. Na koniec zainicjuj klienta tokena. Zwykle odbywa się to w ramach obsługi onload w bibliotece klienta.

Inicjowanie klienta tokenów

Wywołaj funkcję initTokenClient(), aby zainicjować nowego klienta tokena za pomocą identyfikatora klienta Twojej aplikacji internetowej. Opcjonalnie możesz podać listę co najmniej 1 zakresu, do którego użytkownik musi mieć dostęp:

const client = google.accounts.oauth2.initTokenClient({
  client_id: 'YOUR_GOOGLE_CLIENT_ID',
  scope: 'https://www.googleapis.com/auth/calendar.readonly',
  callback: (response) => {
    ...
  },
});

Uruchamianie procesu uzyskiwania tokena OAuth 2.0

Aby wywołać proces UX tokenu i uzyskać token dostępu, użyj metody requestAccessToken(). Google prosi użytkownika o:

  • Wybierz konto,
  • zalogować się na konto Google, jeśli nie masz jeszcze zalogowanego konta;
  • wyrazić zgodę na dostęp aplikacji internetowej do każdego żądanego zakresu.

Gest użytkownika uruchamia proces generowania tokena: <button onclick="client.requestAccessToken();">Authorize me</button>

Google zwróci wtedy do Twojego obsługiwana wywołania zwrotnego TokenResponse z tokenem dostępu i listą zakresów, do których użytkownik przyznał dostęp, lub błąd.

Użytkownicy mogą zamknąć okna wyboru konta lub logowania, w którym przypadku funkcja wywołania zwrotnego nie zostanie wywołana.

Projekt i wygodę aplikacji należy wdrożyć dopiero po dokładnym zapoznaniu się z zasadami OAuth 2.0 Google. Te zasady obejmują m.in. pracę z wieloma zakresami oraz sposób uzyskiwania i przetwarzania zgody użytkownika.

Autoryzacja stopniowa to metoda projektowania aplikacji i zasady, która służy do żądania dostępu do zasobów z użyciem zakresów tylko w miarę potrzeby, a nie od razu i jednocześnie. Użytkownicy mogą zaakceptować lub odrzucić udostępnianie poszczególnych zasobów, o które prosi aplikacja. Nazywamy to uprawnieniami szczegółowymi.

W trakcie tego procesu Google prosi o zgodę użytkownika, wymieniając poszczególne żądane zakresy uprawnień. Użytkownicy wybierają zasoby, które mają być udostępniane aplikacji. Następnie Google wywołuje Twoją funkcję wywołania zwrotnego, aby zwrócić token dostępu i zakresy uprawnień zatwierdzone przez użytkownika. Dzięki szczegółowym uprawnieniom aplikacja może bezpiecznie obsługiwać różne możliwe wyniki.

Są jednak wyjątki. Aplikacje Google Workspace Enterprise z delegacją uprawnień na poziomie całej domeny lub aplikacje oznaczone jako Zaufane pomijają ekran zgody na szczegółowe uprawnienia. W przypadku tych aplikacji użytkownicy nie zobaczą szczegółowego ekranu zgody. Zamiast tego aplikacja otrzyma wszystkie żądane zakresy uprawnień lub żadne.

Więcej informacji znajdziesz w artykule Zarządzanie szczegółowymi uprawnieniami.

Autoryzacja przyrostowa

W przypadku aplikacji internetowych te 2 ogólne scenariusze pokazują stopniowe udzielanie uprawnień za pomocą:

  • Jednostronna aplikacja Ajax, która często korzysta z biblioteki XMLHttpRequest i ma dynamiczny dostęp do zasobów.
  • wiele stron internetowych, zasoby są rozdzielone i zarządzane na poziomie poszczególnych stron;

Te 2 scenariusze mają na celu zilustrowanie kwestii związanych z projektowaniem i metodologii, ale nie są wyczerpującymi zaleceniami dotyczącymi uzyskiwania zgody w aplikacji. W rzeczywistych aplikacjach można stosować odmiany lub kombinacje tych technik.

Ajax

Dodaj do aplikacji obsługę stopniowego autoryzowania, wykonując wiele wywołań do interfejsu requestAccessToken() i korzystając z parametru scope obiektu OverridableTokenClientConfig, aby żądać poszczególnych zakresów uprawnień w chwili, gdy są potrzebne, i tylko wtedy. W tym przykładzie zasoby będą żądane i widoczne tylko wtedy, gdy użytkownik rozwinie złożoną sekcję treści.

Aplikacja Ajax
Inicjalizacja klienta tokenów podczas wczytywania strony:
        const client = google.accounts.oauth2.initTokenClient({
          client_id: 'YOUR_GOOGLE_CLIENT_ID',
          callback: "onTokenResponse",
        });
      
Wyświetlaj prośby o zgodę i pobieraj tokeny dostępu za pomocą gestów użytkownika. Kliknij „+”, aby otworzyć:

Dokumenty do przeczytania

Pokaż ostatnie dokumenty

          client.requestAccessToken(
            overrideConfig = ({
               scope = 'https://www.googleapis.com/auth/documents.readonly'
             })
           );
        

Nadchodzące wydarzenia

Pokaż informacje o kalendarzu

          client.requestAccessToken(
            overrideConfig = ({
               scope = 'https://www.googleapis.com/auth/calendar.readonly'
             })
           );
        

Wyświetlanie zdjęć

          client.requestAccessToken(
            overrideConfig = ({
               scope = 'https://www.googleapis.com/auth/photoslibrary.readonly'
             })
           );
        

Każde wywołanie funkcji requestAccessToken powoduje wyświetlenie użytkownikowi prośby o zgodę. Twoja aplikacja będzie mieć dostęp tylko do tych zasobów, które są wymagane w sekcji, którą użytkownik zdecyduje się rozwinąć. Ograniczy to udostępnianie zasobów na podstawie wyboru użytkownika.

Wiele stron internetowych

Podczas projektowania autoryzacji stopniowej stosuje się wiele stron, aby żądać tylko zakresów wymaganych do wczytania strony. Dzięki temu zmniejsza się złożoność i konieczność wykonywania wielu wywołań w celu uzyskania zgody użytkownika i pobrania tokena dostępu.

Aplikacja wielostronicowa
Strona internetowa Kod
Strona 1. Dokumenty do przeczytania
  const client = google.accounts.oauth2.initTokenClient({
    client_id: 'YOUR_GOOGLE_CLIENT_ID',
    callback: "onTokenResponse",
    scope: 'https://www.googleapis.com/auth/documents.readonly',
  });
  client.requestAccessToken();
          
Strona 2. Nadchodzące wydarzenia
  const client = google.accounts.oauth2.initTokenClient({
    client_id: 'YOUR_GOOGLE_CLIENT_ID',
    callback: "onTokenResponse",
    scope: 'https://www.googleapis.com/auth/calendar.readonly',
  });
  client.requestAccessToken();
          
Strona 3. Karuzela zdjęć
  const client = google.accounts.oauth2.initTokenClient({
    client_id: 'YOUR_GOOGLE_CLIENT_ID',
    callback: "onTokenResponse",
    scope: 'https://www.googleapis.com/auth/photoslibrary.readonly',
  });
  client.requestAccessToken();
          

Każda strona prosi o wyznaczony zakres i uzyskiwanie tokena dostępu przez wywołanie funkcji initTokenClient()requestAccessToken() w momencie wczytywania. W tym scenariuszu poszczególne strony internetowe służą do wyraźnego oddzielenia funkcji i zasobów użytkowników według zakresu. W rzeczywistych sytuacjach poszczególne strony mogą wymagać wielu powiązanych zakresów.

Szczegółowe uprawnienia

Szczegółowe uprawnienia są obsługiwane w ten sam sposób we wszystkich scenariuszach. Gdy funkcja requestAccessToken() wywoła Twoją funkcję wywołania zwrotnego i zwróci token dostępu, sprawdź, czy użytkownik zatwierdził wymagane zakresy za pomocą funkcji hasGrantedAllScopes() lub hasGrantedAnyScope(). Na przykład:

const client = google.accounts.oauth2.initTokenClient({
  client_id: 'YOUR_GOOGLE_CLIENT_ID',
  scope: 'https://www.googleapis.com/auth/calendar.readonly \
          https://www.googleapis.com/auth/documents.readonly \
          https://www.googleapis.com/auth/photoslibrary.readonly',
  callback: (tokenResponse) => {
    if (tokenResponse && tokenResponse.access_token) {
      if (google.accounts.oauth2.hasGrantedAnyScope(tokenResponse,
          'https://www.googleapis.com/auth/photoslibrary.readonly')) {
        // Look at pictures
        ...
      }
      if (google.accounts.oauth2.hasGrantedAllScopes(tokenResponse,
          'https://www.googleapis.com/auth/calendar.readonly',
          'https://www.googleapis.com/auth/documents.readonly')) {
        // Meeting planning and review documents
        ...
      }
    }
  },
});

W odpowiedzi uwzględnione zostaną też wszystkie wcześniej zaakceptowane dotacje z poprzednich sesji lub prośby. Informacje o zgodzie użytkownika są przechowywane dla każdego użytkownika i identyfikatora klienta oraz są zachowywane w przypadku wielu wywołań funkcji initTokenClient() lub requestAccessToken(). Domyślnie zgoda użytkownika jest wymagana tylko wtedy, gdy użytkownik po raz pierwszy odwiedza Twoją witrynę i prosi o nowy zakres, ale może być wymagana przy każdym wczytaniu strony za pomocą elementu prompt=consent w obiektach konfiguracji klienta tokena.

Praca z tokenami

W przypadku modelu tokenów token dostępu nie jest przechowywany przez system operacyjny ani przeglądarkę. Zamiast tego nowy token jest pobierany po raz pierwszy w momencie wczytywania strony lub później, gdy użytkownik wywoła funkcję requestAccessToken(), np. przez naciśnięcie przycisku.

Korzystanie z interfejsów API Google w ramach protokołu REST i zasobów wspólnych (CORS)

Token dostępu może służyć do wysyłania uwierzytelnionych żądań do interfejsów API Google za pomocą REST i CORS. Dzięki temu użytkownicy mogą się logować, wyrażać zgodę, a Google może wydawać tokeny dostępu, a Twoja witryna może pracować z danymi użytkownika.

W tym przykładzie wyświetla się nadchodzące wydarzenia w kalendarzu zalogowanego użytkownika przy użyciu tokena dostępu zwracanego przez funkcję tokenRequest():

var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://www.googleapis.com/calendar/v3/calendars/primary/events');
xhr.setRequestHeader('Authorization', 'Bearer ' + tokenResponse.access_token);
xhr.send();

Więcej informacji znajdziesz w artykule Jak korzystać z CORS do uzyskiwania dostępu do interfejsów API Google.

W następnej sekcji dowiesz się, jak łatwo integrować się z bardziej złożonymi interfejsami API.

Praca z biblioteką JavaScript interfejsów Google API

Klient tokenu współpracuje z biblioteką klienta interfejsu API Google dla języka JavaScript. Zapoznaj się z fragmentem kodu poniżej.

const client = google.accounts.oauth2.initTokenClient({
  client_id: 'YOUR_GOOGLE_CLIENT_ID',
  scope: 'https://www.googleapis.com/auth/calendar.readonly',
  callback: (tokenResponse) => {
    if (tokenResponse && tokenResponse.access_token) {
      gapi.client.setApiKey('YOUR_API_KEY');
      gapi.client.load('calendar', 'v3', listUpcomingEvents);
    }
  },
});

function listUpcomingEvents() {
  gapi.client.calendar.events.list(...);
}

Wygaśnięcie tokenu

Zgodnie z ich projektem tokeny dostępu mają krótki okres ważności. Jeśli token dostępu wygaśnie przed zakończeniem sesji użytkownika, pobierz nowy token, wywołując funkcję requestAccessToken() z zdarzenia wywołanego przez użytkownika, np. naciśnięcia przycisku.

Aby cofnąć zgodę użytkownika i dostęp do zasobów we wszystkich zakresach przyznanych aplikacji, wywołaj metodę google.accounts.oauth2.revoke. Aby cofnąć to uprawnienie, musisz mieć ważny token dostępu:

google.accounts.oauth2.revoke('414a76cb127a7ece7ee4bf287602ca2b56f8fcbf7fcecc2cd4e0509268120bd7', done => {
    console.log(done);
    console.log(done.successful);
    console.log(done.error);
    console.log(done.error_description);
  });