帯域外(OOB)フロー移行ガイド

コレクションでコンテンツを整理 必要に応じて、コンテンツの保存と分類を行います。

概要

2022 年 2 月 16 日、Google は、より安全な OAuth フローを使用して Google OAuth の操作をより安全にする計画を発表しました。このガイドでは、OAuth 帯域外(OOB)フローからサポートされている代替サービスに正常に移行するために必要な変更と手順について説明します。

これは Google の OAuth 2.0 認証エンドポイントとのインタラクションにおいて、フィッシングやアプリなりすましの攻撃を防ぐ手段です。

OOB とは

OAuth 帯域外(OOB)は手動コピー/貼り付けオプションとも呼ばれるレガシー フローで、ユーザーが OAuth 同意リクエストを承認した後に認証情報を受け取るリダイレクト URI を持たないネイティブ クライアントをサポートするために開発されました。OOB フローはリモート フィッシングのリスクをもたらし、クライアントはこの脆弱性から保護するために別の方法に移行する必要があります。

OOB フローは、ウェブ アプリケーション、Android、iOS、ユニバーサル Windows プラットフォーム(UWP)、Chrome アプリ、テレビ、制限入力デバイス、デスクトップ アプリなど、すべての種類のクライアントで非推奨となっています。

主要なコンプライアンス日程

  • 2022 年 2 月 28 日 - OOB フローにおける新しい OAuth の使用のブロック
  • 2022 年 9 月 5 日 - 遵守していない OAuth リクエストに対して、ユーザー向けの警告メッセージが表示されることがあります。
  • 2022 年 10 月 3 日 - 2022 年 2 月 28 日より前に作成された OAuth クライアントの OOB フローは非推奨になりました
  • 2023 年 1 月 31 日 - 既存のすべてのクライアントがブロックされ、クライアントは除外されます。クライアントは、影響を受けたクライアントに送信されるメール メッセージの指示に従って、2023 年 1 月 31 日まで OOB フローを引き続き使用するよう 1 回限りの延長をリクエストできます。

1 か月前、つまり 2022 年 9 月 5 日に、OOB フローが完全に非推奨になると、ユーザー向けの警告メッセージが表示されることがあります。このメッセージは、Google API Console の OAuth 同意画面で登録したサポート メールが表示されている間、アプリがまもなくブロックされる可能性があることをユーザーに知らせます。

承認の呼び出しでクエリ パラメータを渡すことで、ユーザー向けの警告メッセージを確認し、抑制できます。次に例を示します。
  • アプリ内のコードに移動して、Google の OAuth 2.0 認証エンドポイントにリクエストを送信します。
  • 適用日の値 2022-10-03 の ack_oob_shutdown パラメータをリダイレクト フロー リクエストに追加します。例:
    ack_oob_shutdown=2022-10-03
移行プロセスを完了するには、主に次の 2 つの手順を行います。
  1. 影響を受けているかどうかを確認します。
  2. 影響を受ける場合は、より安全な代替手段に移行してください。

影響を受けているかどうかの確認

このサポート終了は、本番環境用アプリ(公開ステータスが [本番環境] に設定されたアプリ)にのみ適用されます。フローは、公開ステータスのテストを行うアプリでも引き続き機能します。

Google API Console の OAuth Consent Screen pageで公開ステータスを確認します。公開ステータスが「本番環境」のプロジェクトで OOB フローを使用している場合は、次の手順に進みます。

アプリが OOB フローを使用しているかどうかを判断する方法

アプリのコードを調べるか、送信ネットワーク呼び出し(アプリが OAuth ライブラリを使用している場合)で、アプリが Google OAuth の承認リクエストで OOB リダイレクト 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
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
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)

Android または iOS の OAuth クライアント タイプで OOB フローを使用していると判断した場合は、Google ログイン モバイル SDK(AndroidiOS)を使用するように移行する必要があります。

この SDK を利用すると、Google API へのアクセスが容易になり、Google の OAuth 2.0 認証エンドポイントへのすべての呼び出しを処理できます。

以下のドキュメントのリンクでは、Google ログイン SDK を使用して、OOB リダイレクト URI を使用せずに Google API にアクセスする方法について説明します。

Android で Google API にアクセスする

サーバーサイド(オフライン)アクセス
以下の例は、Android でサーバーサイドから Google API にアクセスする方法を示しています。
Task<GoogleSignInAccount> task = GoogleSignIn.getSignedInAccountFromIntent(data);
try {
  GoogleSignInAccount account = task.getResult(ApiException.class);
  
  // request a one-time authorization code that your server exchanges for an
  // access token and sometimes refresh token
  String authCode = account.getServerAuthCode();
  
  // Show signed-in UI
  updateUI(account);

  // TODO(developer): send code to server and exchange for access/refresh/ID tokens
} catch (ApiException e) {
  Log.w(TAG, "Sign-in failed", e);
  updateUI(null);
}

サーバー側から Google API にアクセスする方法については、サーバー側のアクセスガイドをご覧ください。

iOS アプリで Google API にアクセスする

クライアントサイドのアクセス

以下の例は、iOS のクライアント側で Google API にアクセスする方法を示しています。

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)、フェッチャー認証者(GTMFetcherAuthorizationProtocol)を Objective-C for REST 用の Google API クライアント ライブラリとともに使用します。

クライアント側で Google API にアクセスする方法については、クライアントサイド アクセス ガイドをご覧ください。クライアントサイドで Google API にアクセスする方法を説明します。

サーバーサイド(オフライン)アクセス
以下の例は、iOS のクライアントをサポートするために、サーバー側で Google API にアクセスする方法を示しています。
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
}

サーバー側から Google API にアクセスする方法については、サーバー側のアクセスガイドをご覧ください。

Chrome アプリ クライアント

アプリが Chrome アプリ クライアントで OOB フローを使用していると判断した場合は、Chrome Identity API を使用するように移行する必要があります。

次の例は、OOB リダイレクト 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)
    });
   });
 });
};

認証されたユーザーにアクセスして、Chrome Identity API で Google エンドポイントを呼び出す方法について詳しくは、Chrome Identity API ガイドをご覧ください。

ウェブ アプリケーション

アプリがウェブ アプリケーションで OOB フローを使用していると判断した場合は、いずれかの Google API クライアント ライブラリを使用するように移行する必要があります。さまざまなプログラミング言語用のクライアント ライブラリについては、こちらをご覧ください。

このライブラリを使用すると、Google API へのアクセスや、Google エンドポイントへのすべての呼び出しを簡単に行うことができます。

サーバーサイド(オフライン)アクセス
サーバーサイド(オフライン)アクセスモードでは、次の操作を行う必要があります。

次のコード スニペットは、Google Drive API を使用して、OOB リダイレクト URI を使用せずにサーバー側でユーザーの Google ドライブ ファイルを一覧表示する NodeJS の例を示しています。

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.
      });
    }
  }
}

サーバー側から Google API にアクセスする方法については、サーバー側ウェブアプリ ガイドをご覧ください。

クライアントサイドのアクセス

次のコード スニペットは、JavaScript で、Google API を使用してクライアント側でユーザーのカレンダーの予定にアクセスする例を示しています。


// 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(...);
}

クライアント側から Google API にアクセスする方法については、クライアント側ウェブアプリ ガイドをご覧ください。

デスクトップ クライアント

アプリがデスクトップ クライアントで OOB フローを使用していると判断した場合は、ループバック IP アドレス(localhost または 127.0.0.1)フローに移行する必要があります。