Руководство по внеполосной миграции потока (OOB)

Обзор

16 февраля 2022 года мы объявили о планах сделать взаимодействие с Google OAuth более безопасным за счет использования более защищенных потоков OAuth. Это руководство поможет вам понять необходимые изменения и шаги для успешного перехода от внеполосного (OOB) потока OAuth к поддерживаемым альтернативам.

Эти меры направлены на защиту от фишинговых атак и атак с использованием поддельных приложений при взаимодействии с точками авторизации Google OAuth 2.0.

Что такое OOB?

Внеполосная авторизация OAuth (OOB) , также известная как ручное копирование/вставка, — это устаревший процесс, разработанный для поддержки нативных клиентов, у которых отсутствует URI перенаправления для приема учетных данных после того, как пользователь подтвердит запрос на согласие OAuth. Внеполосная авторизация представляет собой риск удаленного фишинга, и клиентам необходимо перейти на альтернативный метод для защиты от этой уязвимости.

Использование стандартного потока обработки данных прекращается для всех типов клиентов, включая веб-приложения, Android, iOS, универсальную платформу Windows (UWP), приложения Chrome, телевизоры и устройства с ограниченным набором входных данных, а также настольные приложения.

Ключевые сроки соблюдения требований

  • 28 февраля 2022 г. — блокируется новое использование OAuth для потока OOB.
  • 5 сентября 2022 г. — при запросах OAuth, не соответствующих требованиям, может отображаться предупреждающее сообщение для пользователя.
  • 3 октября 2022 г. — стандартный процесс аутентификации (OOB) устарел для клиентов OAuth, созданных до 28 февраля 2022 г.
  • 31 января 2023 г. - все существующие клиенты заблокированы (включая клиентов, на которых распространяется исключение).

При несоответствии запросам пользователю будет отображаться сообщение об ошибке. В сообщении будет указано, что приложение заблокировано, при этом будет отображаться адрес электронной почты службы поддержки, указанный в настройках согласия OAuth в консоли Google API .

Процесс миграции состоит из двух основных этапов:
  1. Определите, затрагивает ли это вас.
  2. Если вас это затронуло, перейдите на более безопасный вариант.

Определите, затрагивает ли это вас.

Данное предупреждение относится только к приложениям, находящимся в производственной среде (т. е. приложениям со статусом публикации « В производственной среде» ). Процесс будет продолжать работать для приложений со статусом публикации «Тестирование» .

Проверьте статус публикации на странице «Брендирование OAuth» в консоли Google Cloud и перейдите к следующему шагу, если вы используете стандартный процесс в проекте со статусом публикации «В производстве».

Как определить, использует ли ваше приложение стандартный поток обработки данных (OOB flow)

Проверьте код вашего приложения или исходящий сетевой вызов (если ваше приложение использует библиотеку OAuth), чтобы определить, использует ли запрос авторизации Google OAuth, который отправляет ваше приложение, значение URI перенаправления, недопустимое в исходном коде.

Проверьте код вашего приложения.

Просмотрите раздел кода вашего приложения, где вы обращаетесь к конечным точкам авторизации Google OAuth, и определите, принимает ли параметр redirect_uri какое-либо из следующих значений:
  • redirect_uri=urn:ietf:wg:oauth:2.0:oob
  • redirect_uri=urn:ietf:wg:oauth:2.0:oob:auto
  • redirect_uri=oob
Пример запроса на перенаправление по умолчанию будет выглядеть следующим образом:
https://accounts.google.com/o/oauth2/v2/auth?
response_type=code&
scope=<SCOPES>&
state=<STATE>&
redirect_uri=urn:ietf:wg:oauth:2.0:oob&
client_id=<CLIENT_ID>

Проверьте исходящий сетевой вызов.

Метод проверки сетевых вызовов будет различаться в зависимости от типа клиентского приложения.
При анализе сетевых вызовов найдите запросы, отправленные на конечные точки авторизации Google OAuth, и определите, имеет ли параметр redirect_uri какое-либо из следующих значений:
  • redirect_uri=urn:ietf:wg:oauth:2.0:oob
  • redirect_uri=urn:ietf:wg:oauth:2.0:oob:auto
  • redirect_uri=oob
Пример запроса на перенаправление по умолчанию будет выглядеть следующим образом:
https://accounts.google.com/o/oauth2/v2/auth?
response_type=code&
scope=<SCOPES>&
state=<STATE>&
redirect_uri=urn:ietf:wg:oauth:2.0:oob&
client_id=<CLIENT_ID>

Перейдите на безопасную альтернативу

Мобильные клиенты (Android / iOS)

Если вы обнаружите, что ваше приложение использует стандартный поток аутентификации (OOB) с клиентом OAuth для Android или iOS, вам следует перейти на использование рекомендуемых SDK ( Android , iOS ).

SDK упрощает доступ к API Google и обрабатывает все вызовы к конечным точкам авторизации Google OAuth 2.0.

Приведенные ниже ссылки на документацию содержат информацию о том, как использовать рекомендуемые SDK для доступа к API Google без использования стандартного URI перенаправления.

Доступ к API Google на Android

Доступ со стороны клиента

В следующем примере показано, как получить доступ к API Google на стороне клиента на Android, используя рекомендуемую библиотеку Google Identity Services для Android.

  List requestedScopes = Arrays.asList(DriveScopes.DRIVE_APPDATA);
    AuthorizationRequest authorizationRequest = AuthorizationRequest.builder().setRequestedScopes(requestedScopes).build();
    Identity.getAuthorizationClient(activity)
            .authorize(authorizationRequest)
            .addOnSuccessListener(
                authorizationResult -> {
                  if (authorizationResult.hasResolution()) {
                    // Access needs to be granted by the user
                    PendingIntent pendingIntent = authorizationResult.getPendingIntent();
                    try {
    startIntentSenderForResult(pendingIntent.getIntentSender(),
    REQUEST_AUTHORIZE, null, 0, 0, 0, null);
                    } catch (IntentSender.SendIntentException e) {
                    Log.e(TAG, "Couldn't start Authorization UI: " + e.getLocalizedMessage());
                    }
                  } else {
                    // Access already granted, continue with user action
                    saveToDriveAppFolder(authorizationResult);
                  }
                })
            .addOnFailureListener(e -> Log.e(TAG, "Failed to authorize", e));

Передайте объект authorizationResult в определенный вами метод, чтобы сохранить содержимое в папку на диске пользователя. authorizationResult имеет метод getAccessToken() , который возвращает токен доступа.

Доступ на стороне сервера (в автономном режиме)
В следующем примере показано, как получить доступ к API Google на стороне сервера в Android.
  List requestedScopes = Arrays.asList(DriveScopes.DRIVE_APPDATA);
    AuthorizationRequest authorizationRequest = AuthorizationRequest.builder()
    .requestOfflineAccess(webClientId)
            .setRequestedScopes(requestedScopes)
            .build();
    Identity.getAuthorizationClient(activity)
            .authorize(authorizationRequest)
            .addOnSuccessListener(
                authorizationResult -> {
                  if (authorizationResult.hasResolution()) {
                    // Access needs to be granted by the user
                    PendingIntent pendingIntent = authorizationResult.getPendingIntent();
                    try {
    startIntentSenderForResult(pendingIntent.getIntentSender(),
    REQUEST_AUTHORIZE, null, 0, 0, 0, null);
                    } catch (IntentSender.SendIntentException e) {
                    Log.e(TAG, "Couldn't start Authorization UI: " + e.getLocalizedMessage());
                    }
                  } else {
                    String authCode = authorizationResult.getServerAuthCode();
                  }
                })
            .addOnFailureListener(e -> Log.e(TAG, "Failed to authorize", e));

authorizationResult имеет метод getServerAuthCode() , который возвращает код авторизации, который вы можете отправить на свой бэкэнд для получения токена доступа и обновления.

Получите доступ к API Google в iOS-приложении

Доступ со стороны клиента

В приведенном ниже примере показано, как получить доступ к API Google на стороне клиента в iOS .

user.authentication.do { authentication, error in
  guard error == nil else { return }
  guard let authentication = authentication else { return }
  
  // Get the access token to attach it to a REST or gRPC request.
  let accessToken = authentication.accessToken
  
  // Or, get an object that conforms to GTMFetcherAuthorizationProtocol for
  // use with GTMAppAuth and the Google APIs client library.
  let authorizer = authentication.fetcherAuthorizer()
}

Используйте токен доступа для вызова API, либо включив его в заголовок REST- или gRPC-запроса ( Authorization: Bearer ACCESS_TOKEN ), либо используя авторизатор fetcher ( GTMFetcherAuthorizationProtocol ) с клиентской библиотекой Google APIs для Objective-C for REST .

Ознакомьтесь с руководством по доступу на стороне клиента , чтобы узнать, как получить доступ к API Google на стороне клиента.

Доступ со стороны сервера (в автономном режиме)
В приведенном ниже примере показано, как получить доступ к API Google на стороне сервера для поддержки iOS-клиента.
GIDSignIn.sharedInstance.signIn(with: signInConfig, presenting: self) { user, error in
  guard error == nil else { return }
  guard let user = user else { return }
  
  // request a one-time authorization code that your server exchanges for
  // an access token and refresh token
  let authCode = user.serverAuthCode
}

Ознакомьтесь с руководством по доступу на стороне сервера , чтобы узнать, как получить доступ к API Google с серверной стороны.

Клиент приложения Chrome

Если вы обнаружите, что ваше приложение использует стандартный поток обработки данных в клиентском приложении Chrome, вам следует перейти на использование API Chrome Identity .

В приведенном ниже примере показано, как получить все контакты пользователей без использования стандартного URI перенаправления.

window.onload = function() {
  document.querySelector('button').addEventListener('click', function() {

  
  // retrieve access token
  chrome.identity.getAuthToken({interactive: true}, function(token) {
  
  // ..........


  // the example below shows how to use a retrieved access token with an appropriate scope
  // to call the Google People API contactGroups.get endpoint

  fetch(
    'https://people.googleapis.com/v1/contactGroups/all?maxMembers=20&key=API_KEY',
    init)
    .then((response) => response.json())
    .then(function(data) {
      console.log(data)
    });
   });
 });
};

Для получения дополнительной информации о том, как получить доступ к API Chrome Identity для аутентификации пользователей и вызывать конечные точки Google с помощью API Chrome Identity, ознакомьтесь с руководством .

Веб-приложение

Если вы обнаружите, что ваше приложение использует стандартный поток обработки данных для веб-приложений, вам следует перейти на использование одной из наших клиентских библиотек Google API. Клиентские библиотеки для различных языков программирования перечислены здесь .

Эти библиотеки упрощают доступ к API Google и обработку всех вызовов к конечным точкам Google.

Доступ со стороны сервера (в автономном режиме)
Для доступа со стороны сервера (в автономном режиме) необходимо выполнить следующие действия:

Приведённый ниже фрагмент кода демонстрирует пример использования API Google Drive на NodeJS для отображения списка файлов пользователя в Google Drive на стороне сервера без использования стандартного URI перенаправления.

async function main() {
  const server = http.createServer(async function (req, res) {

  if (req.url.startsWith('/oauth2callback')) {
    let q = url.parse(req.url, true).query;

    if (q.error) {
      console.log('Error:' + q.error);
    } else {
      
      // Get access and refresh tokens (if access_type is offline)
      let { tokens } = await oauth2Client.getToken(q.code);
      oauth2Client.setCredentials(tokens);

      // Example of using Google Drive API to list filenames in user's Drive.
      const drive = google.drive('v3');
      drive.files.list({
        auth: oauth2Client,
        pageSize: 10,
        fields: 'nextPageToken, files(id, name)',
      }, (err1, res1) => {
        // TODO(developer): Handle response / error.
      });
    }
  }
}

Ознакомьтесь с руководством по разработке веб-приложений на стороне сервера , чтобы узнать, как получить доступ к API Google с серверной стороны.

Доступ со стороны клиента

Приведённый ниже фрагмент кода на JavaScript демонстрирует пример использования API Google для доступа к событиям календаря пользователя на стороне клиента.


// initTokenClient() initializes a new token client with your
// web app's client ID and the scope you need access to

const client = google.accounts.oauth2.initTokenClient({
  client_id: 'YOUR_GOOGLE_CLIENT_ID',
  scope: 'https://www.googleapis.com/auth/calendar.readonly',
  
  // callback function to handle the token response
  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(...);
}

Ознакомьтесь с руководством по разработке веб-приложений на стороне клиента , чтобы узнать, как получить доступ к API Google с клиентской стороны.

Настольный клиент

Если вы обнаружите, что ваше приложение использует стандартный поток (OOB) на настольном клиенте, вам следует перейти на использование потока с использованием IP-адреса локальной сети ( localhost или 127.0.0.1 ) .