OAuth 2.0 サーバーを実装する

すべての Cloud-to-cloud 統合には、ユーザーを認証するための仕組みを組み込む必要があります。

認証を使用すると、ユーザーの Google アカウントと認証システム内のユーザー アカウントを関連付けることができます。これにより、フルフィルメントでスマートホーム インテントを受け取ったときにユーザーを識別できます。Google スマートホームは、認証コードフローを使用した OAuth のみに対応します。

このページでは、OAuth 2.0 サーバーを設定して Cloud-to-cloud 統合と連携させる方法について説明します。

OAuth による Google アカウントのリンク

認可コードフローでは、次の 2 つのエンドポイントが必要です。

  • 認可エンドポイント。まだログインしていないユーザーにログイン UI を表示します。認可エンドポイントは、リクエストされたアクセスに対するユーザーの同意を記録する有効期間の短い認可コードも作成します。

  • トークン交換エンドポイント。次の 2 種類の交換を処理します。

    1. 長期の更新トークンと短期のアクセス トークンの認可コードを交換します。この交換は、ユーザーがアカウント リンクのフローを完了したときに行われます。
    2. 有効期間の長い更新トークンを有効期間の短いアクセス トークンと交換します。この交換は、アクセス トークンの有効期限が切れたために Google が新しいアクセス トークンを必要とする場合に発生します。

設計ガイドライン

このセクションでは、OAuth リンクフロー向けにホストするユーザー画面の設計要件と推奨事項について説明します。Google のアプリから呼び出されると、プラットフォームは Google へのログインページとアカウントのリンクの同意画面をユーザーに表示します。ユーザーがアカウントのリンクに同意すると、Google のアプリに戻ります。

この図は、ユーザーが Google アカウントを認証システムにリンクする手順を示しています。最初のスクリーンショットは、プラットフォームからユーザーが開始するリンクを示しています。2 つ目の画像は Google へのユーザーのログインを示し、3 つ目の画像は Google アカウントとアプリのリンクに関するユーザーの同意と確認を示しています。最後のスクリーンショットは、Google アプリでユーザー アカウントが正常にリンクされていることを示しています。
図 1.アカウント リンクのユーザーによる Google へのログインと同意画面。

要件

  1. ユーザーのアカウントが、Google Home や Google アシスタントなどの特定の Google サービスではなく Google にリンクされることを伝える必要があります。
  2. 「ログインすることで、Google によるデバイスの管理を承認したことになります」といった Google の文言が必要です。詳しくは、Google Home デベロッパー ポリシーの Google Device Control の承認をご覧ください。
  3. ユーザーがリンクをしなかった場合は、戻るまたはキャンセルできる方法を提供する必要があります。
  4. ウェブ OAuth リンクページを開いて、ユーザーが明確な方法(ユーザー名とパスワードのフィールドなど)で Google アカウントにログインできることを確認する必要があります。[ウェブ OAuth リンク] ページに移動せずにユーザーがリンクできる Google ログイン(GSI)メソッドは使用しないでください。このような行為は Google ポリシー違反に該当します。

推奨事項

次の手順を行うことをおすすめします。

  1. Google のプライバシー ポリシーを表示します。同意画面に Google のプライバシー ポリシーへのリンクを含めます。

  2. 共有されるデータ。明確で簡潔な表現を使用して、Google が必要とするデータとその理由をユーザーに伝えます。

  3. 行動を促す明確なフレーズ。同意画面には、「同意してリンクする」など、行動を促す明確なフレーズを明記します。これは、アカウントをリンクするために Google と共有する必要があるデータをユーザーが理解する必要があるためです。

  4. リンク解除できます。プラットフォーム上のアカウント設定への URL など、ユーザーがリンクを解除するためのメカニズムを提供します。また、リンクされたアカウントを管理できる Google アカウントへのリンクを含めることもできます。

  5. ユーザー アカウントを変更できます。ユーザーがアカウントを切り替える方法を提案する。これは、ユーザーが複数のアカウントを持つ傾向がある場合に特に便利です。

    • ユーザーがアカウントを切り替えるために同意画面を閉じる必要がある場合は、回復可能なエラーを Google に送信します。これにより、ユーザーは OAuth リンクを使用して目的のアカウントにログインできます。
  6. ロゴを含める。同意画面に会社のロゴを表示する。 スタイル ガイドラインに沿ってロゴを配置します。Google のロゴも表示する場合は、ロゴと商標をご覧ください。

認証コードのフロー

認可コードフローの OAuth 2.0 サーバーは 2 つのエンドポイントで構成されます。これらのエンドポイントには HTTPS 経由でアクセスできます。最初のエンドポイントは認可エンドポイントで、これはデータアクセスに対するユーザーの同意を検索または取得する処理を担当します。認可エンドポイントは、ログインしていないユーザーにログイン用の UI を表示し、リクエストされたアクセスへの同意を記録します。2 つ目のエンドポイントはトークン交換エンドポイントで、トークンという暗号化された文字列を取得し、ユーザーにサービスへのアクセスを許可します。

Google アプリケーションでサービスの API を呼び出す必要がある場合、Google はこれらのエンドポイントを使用して、API の呼び出し許可をユーザーから取得します。

Google が開始する OAuth 2.0 認可コードフローのセッションは次のような流れになります。

  1. Google がユーザーのブラウザで認可エンドポイントを開きます。アクションのフローが音声専用デバイスで開始された場合、Google は実行をスマートフォンに転送します。
  2. ユーザーがログインし(ログインしていない場合)、Google が API を使用してデータにアクセスすることを承諾します(まだ許可していない場合)。
  3. サービスが認可コードを作成して Google に返します。これを行うには、認可コードをリクエストに付加してユーザーのブラウザを Google にリダイレクトします。
  4. Google が認可コードをトークン交換エンドポイントに送信します。トークン交換エンドポイントはコードの真正性を検証し、アクセス トークンと更新トークンを返します。アクセス トークンは、API にアクセスするための認証情報としてサービスが受け入れる短期のトークンです。更新トークンは長期のトークンで、アクセス トークンが期限切れになったときに新しいトークンを取得するために使用されます。
  5. ユーザーがアカウント リンクフローを完了すると、Google から送信されるすべてのリクエストにアクセス トークンが含まれます。

認可リクエストの処理

OAuth 2.0 認可コードフローを使用してアカウント リンクを行う必要がある場合、Google は、次のパラメータを含むリクエストを使用して、ユーザーを認可エンドポイントに送信します。

認可エンドポイントのパラメータ
client_id Google に割り当てたクライアント ID。
redirect_uri このリクエストに対するレスポンスを送信する宛先 URL。
state リダイレクト URL で変更されずに Google に返される会計上の値。
scope 省略可: Google が許可を求めているデータ範囲を表す、スペースで区切られた文字列。
response_type レスポンスで返される値のタイプ。OAuth 2.0 認可コードフローの場合、レスポンス タイプは常に code です。
user_locale Google アカウントの言語設定(RFC5646 形式)。ユーザーの優先言語でコンテンツをローカライズするために使用されます。

たとえば、認可エンドポイントが https://myservice.example.com/auth にある場合、リクエストは次のようになります。

GET https://myservice.example.com/auth?client_id=GOOGLE_CLIENT_ID&redirect_uri=REDIRECT_URI&state=STATE_STRING&scope=REQUESTED_SCOPES&response_type=code&user_locale=LOCALE

認可エンドポイントでログイン リクエストを処理する場合は、次の手順に従います。

  1. client_id が Google に割り当てたクライアント ID と一致し、redirect_uri が Google からサービスに提供されたリダイレクト URL と一致することを確認します。これらのチェックは、意図しないクライアント アプリや誤って構成されたクライアント アプリによるアクセスを防ぐために重要です。複数の OAuth 2.0 フローをサポートしている場合は、response_typecode であることも確認してください。
  2. ユーザーがサービスにログインしているかどうか確認します。ユーザーがログインしていない場合は、サービスのログインまたは登録フローを完了します。
  3. Google が API へのアクセスに使用する認可コードを生成します。認可コードには任意の文字列値を設定できますが、トークンを使用するユーザーとクライアント、コードの有効期限を一意に表す必要があります。また、簡単に推測されない文字列にする必要があります。通常、約 10 分後に期限切れになる認可コードを発行します。
  4. redirect_uri パラメータで指定された URL が次の形式になっていることを確認します。
      https://oauth-redirect.googleusercontent.com/r/YOUR_PROJECT_ID
      https://oauth-redirect-sandbox.googleusercontent.com/r/YOUR_PROJECT_ID
      
  5. redirect_uri パラメータで指定された URL にユーザーのブラウザをリダイレクトします。code パラメータと state パラメータを追加してリダイレクトする場合は、生成された認可コードと元の未変更の state 値を含めます。結果の URL の例を次に示します。
    https://oauth-redirect.googleusercontent.com/r/YOUR_PROJECT_ID?code=AUTHORIZATION_CODE&state=STATE_STRING

トークン交換リクエストの処理

サービスのトークン交換エンドポイントは、次の 2 種類のトークン交換を処理します。

  • 認可コードとアクセス トークンおよび更新トークンとの交換
  • 更新トークンとアクセス トークンの交換

トークン交換リクエストには、次のパラメータを使用します。

トークン交換エンドポイントのパラメータ
client_id リクエスト元を Google として識別する文字列。この文字列は、Google の一意の識別子としてシステムに登録されている必要があります。
client_secret Google に登録したサービスのシークレット文字列。
grant_type 交換されるトークンの種類。authorization_code または refresh_token のいずれかです。
code grant_type=authorization_code の場合、このパラメータは、Google がログインまたはトークン交換エンドポイントから受け取ったコードです。
redirect_uri grant_type=authorization_code の場合、このパラメータは最初の認可リクエストで使用される URL です。
refresh_token grant_type=refresh_token の場合、このパラメータは Google がトークン交換エンドポイントから受け取った更新トークンです。

Google がサーバーに認証情報を送信する方法を設定する

実装に応じて、認可サーバーはリクエスト本文またはリクエスト ヘッダーでクライアント認証情報を受信することを想定しています。

デフォルトでは、認証情報はリクエスト本文で送信されます。認可サーバーでリクエスト ヘッダーにクライアント認証情報を含める必要がある場合は、それに応じて Cloud-to-cloud 統合を構成する必要があります。

Developer Console に移動する

  1. プロジェクトのリストで、操作するプロジェクトの横にある [開く] をクリックします。

  2. [Cloud-to-Cloud] で [Develop] を選択します。

  3. 統合の横にある [開く] をクリックします。

  4. [権限(省略可)] までスクロールし、[Google が HTTP 基本認証ヘッダーを介してクライアント ID とシークレットを送信する] チェックボックスをオンにします。

  5. [保存] をクリックして、変更を保存します。

認可コードとアクセス トークンおよび更新トークンとの交換

ユーザーがログインして認可エンドポイントが短期の認可コードを Google に返すと、Google がリクエストをトークン交換エンドポイントに送信し、アクセス トークンと更新トークンの認可コードを交換します。

このリクエストの場合、grant_type の値は authorization_code で、code の値は以前に Google に付与した認可コードの値になります。認可コードをアクセス トークンと更新トークンと交換するリクエストは次のようになります。

POST /token HTTP/1.1
Host: oauth2.example.com
Content-Type: application/x-www-form-urlencoded

client_id=GOOGLE_CLIENT_ID&client_secret=GOOGLE_CLIENT_SECRET&grant_type=authorization_code&code=AUTHORIZATION_CODE&redirect_uri=REDIRECT_URI

認可コードをアクセス トークンと更新トークンと交換するため、トークン交換エンドポイントは次の処理を行い、POST リクエストに応答します。

  1. client_id がリクエスト元を認証済みオリジンとして識別し、client_secret が期待値と一致していることを確認します。
  2. 認可コードが有効で、期限が切れていないことを確認します。さらに、リクエストで指定されたクライアント ID が、認可コードに関連付けられたクライアント ID と一致することを確認します。
  3. redirect_uri パラメータで指定された URL が、最初の認可リクエストで使用された値と一致していることを確認します。
  4. 上記の条件のすべて満たしていない場合、本文で HTTP 400 Bad Request エラーと {"error": "invalid_grant"} を返します。
  5. それ以外の場合は、認可コードのユーザー ID を使用して、更新トークンとアクセス トークンを生成します。これらのトークンには任意の文字列値を設定できますが、トークンを使用するユーザーとクライアントを一意に表す必要があります。また、簡単に推測されない文字列にする必要があります。アクセス トークンには、トークンの有効期限(通常はトークンを発行してから 1 時間)も記録します。更新トークンに有効期限はありません。
  6. HTTPS レスポンスの本文で次の JSON オブジェクトを返します。
    {
    "token_type": "Bearer",
    "access_token": "ACCESS_TOKEN",
    "refresh_token": "REFRESH_TOKEN",
    "expires_in": SECONDS_TO_EXPIRATION
    }
    

Google は、ユーザーのアクセス トークンと更新トークンを保存し、アクセス トークンの有効期限を記録します。アクセス トークンの有効期限が切れると、Google は更新トークンを使用してトークン交換エンドポイントから新しいアクセス トークンを取得します。

更新トークンとアクセス トークンの交換

アクセス トークンが期限切れになると、Google は、更新トークンを新しいアクセス トークンと交換するためのリクエストをトークン交換エンドポイントに送信します。

このリクエストの場合、grant_type の値は refresh_token で、refresh_token の値は以前に Google に付与した更新トークンの値になります。更新トークンとアクセス トークンの交換リクエストの例を次に示します。

POST /token HTTP/1.1
Host: oauth2.example.com
Content-Type: application/x-www-form-urlencoded

client_id=GOOGLE_CLIENT_ID&client_secret=GOOGLE_CLIENT_SECRET&grant_type=refresh_token&refresh_token=REFRESH_TOKEN

更新トークンとアクセス トークンを交換するため、トークン交換エンドポイントが次の処理を行い、POST リクエストに応答します。

  1. client_id がリクエスト元を Google として識別し、client_secret が期待値と一致していることを確認します。
  2. 更新トークンが有効で、リクエストで指定されたクライアント ID が更新に関連付けられたクライアント ID と一致していることを確認します。
  3. 上記の条件のすべて満たしていない場合、本文で HTTP 400 Bad Request エラーと {"error": "invalid_grant"} を返します。
  4. それ以外の場合は、更新トークンのユーザー ID を使用してアクセス トークンを生成します。これらのトークンには任意の文字列値を設定できますが、トークンを使用するユーザーとクライアントを一意に表し、簡単に推測されない文字列にする必要があります。アクセス トークンには、トークンの有効期限(通常はトークンを発行してから 1 時間)も記録します。
  5. HTTPS レスポンスの本文で次の JSON オブジェクトを返します。
    {
    "token_type": "Bearer",
    "access_token": "ACCESS_TOKEN",
    "expires_in": SECONDS_TO_EXPIRATION
    }

userinfo リクエストを処理する

userinfo エンドポイントは、OAuth 2.0 で保護されたリソースで、リンクされたユーザーに関するクレームを返します。userinfo エンドポイントの実装とホストは任意ですが、以下のユースケースを除きます。

トークン エンドポイントからアクセス トークンが正常に取得されると、Google は、リンクされたユーザーに関する基本的なプロフィール情報を取得するためのリクエストを userinfo エンドポイントに送信します。

userinfo エンドポイント リクエスト ヘッダー
Authorization header Bearer タイプのアクセス トークン。

たとえば、userinfo エンドポイントが https://myservice.example.com/userinfo の場合、リクエストは次のようになります。

GET /userinfo HTTP/1.1
Host: myservice.example.com
Authorization: Bearer ACCESS_TOKEN

userinfo エンドポイントでリクエストを処理するには、次の手順を行います。

  1. Authorization ヘッダーからアクセス トークンを抽出し、そのアクセス トークンに関連付けられたユーザーの情報を返します。
  2. アクセス トークンが無効な場合は、WWW-Authenticate レスポンス ヘッダーを使用して HTTP 401 Unauthorized エラーを返します。userinfo エラー レスポンスの例を次に示します。
    HTTP/1.1 401 Unauthorized
    WWW-Authenticate: error="invalid_token",
    error_description="The Access Token expired"
    
    リンク処理中に 401 Unauthorized またはその他の失敗したエラー レスポンスが返された場合、そのエラーは修復不能となり、取得したトークンは破棄されるため、ユーザーはリンク処理をやり直す必要があります。
  3. アクセス トークンが有効な場合は、HTTPS の本文に次の JSON オブジェクトを含む HTTP 200 レスポンスを返します。 レスポンス:

    {
    "sub": "USER_UUID",
    "email": "EMAIL_ADDRESS",
    "given_name": "FIRST_NAME",
    "family_name": "LAST_NAME",
    "name": "FULL_NAME",
    "picture": "PROFILE_PICTURE",
    }
    
    userinfo エンドポイントが HTTP 200 成功レスポンスを返すと、取得したトークンとクレームがユーザーの Google アカウントに登録されます。

    userinfo エンドポイント レスポンス
    sub システム内でユーザーを識別する一意の ID。
    email ユーザーのメールアドレス。
    given_name 省略可: ユーザーの名。
    family_name 省略可: ユーザーの姓。
    name 省略可: ユーザーの氏名。
    picture 省略可: ユーザーのプロフィール写真。