OAuth と Google ログインでのリンクを効率化

概要

OAuth ベースの Google ログインを効率化するリンクにより、OAuth リンクの上に Google ログインが追加されます。これにより、Google ユーザーはシームレスなリンクを設定できるようになります。また、アカウントの作成も可能になり、ユーザーは自身の Google アカウントを使用してサービス上で新しいアカウントを作成できるようになります。

OAuth と Google ログインを使用してアカウント リンクを行うには、次の一般的な手順に従います。

  1. まず、ユーザーの Google プロフィールにアクセスすることについてユーザーに同意を求めます。
  2. プロフィールの情報を使って、ユーザー アカウントが存在するかどうかを確認します。
  3. 既存のユーザーの場合は、アカウントをリンクします。
  4. 認証システムで Google ユーザーと一致するユーザーが見つからない場合は、Google から受け取った ID トークンを検証します。その後、ID トークンに含まれるプロファイル情報に基づいてユーザーを作成できます。
この図は、ユーザーが簡素化されたリンクフローを使用して Google アカウントをリンクするための手順を示しています。最初のスクリーンショットは、ユーザーがリンク対象のアプリを選択する方法を示しています。2 枚目のスクリーンショットでは、ユーザーがそのサービスの既存のアカウントを使用しているかどうかを確認できます。3 つ目のスクリーンショットでは、ユーザーはリンクする Google アカウントを選択できます。4 つ目のスクリーンショットは、Google アカウントとアプリをリンクした際の確認画面を示しています。5 つ目のスクリーンショットは、Google アプリで適切にリンクされたユーザー アカウントを示しています。

図 1. ユーザーのスマートフォンでのアカウントのリンクの簡素化

リンクを合理化するための要件

OAuth サーバーを実装する

トークン交換エンドポイントは、checkcreateget インテントをサポートしている必要があります。以下に、アカウント リンクのフローに従って完了した手順と、さまざまなインテントが呼び出されたタイミングを示します。

  1. ユーザーの認証システムにアカウントはありますか?(ユーザーが「はい」または「いいえ」を選択したとき)。
    1. はい : ユーザーが Google アカウントに関連付けられているメールアドレスを使用してプラットフォームにログインしていますか?(ユーザーが「はい」または「いいえ」を選択したとき)。
      1. はい : お客様の認証システムに一致するアカウントはありますか?(確認のために check intent が呼び出されます)
        1. YES : get intent が呼び出され、get インテントが正常に返された場合、アカウントはリンクされています。
        2. いいえ : 新しいアカウントを作成しますか?(ユーザーが「はい」または「いいえ」を選択したとき)。
          1. YES: create intent が呼び出され、作成インテントが正常に返された場合、アカウントはリンクされています。
          2. いいえ : ウェブ OAuth フローがトリガーされ、ユーザーはブラウザにリダイレクトされます。別のメールにリンクするオプションがユーザーに表示されます。
      2. いいえ : ウェブの OAuth フローがトリガーされ、ユーザーはブラウザにリダイレクトされます。別のメールにリンクするオプションがユーザーに表示されます。
    2. いいえ : 認証システムに登録されているユーザーと一致するアカウントはありますか?(確認のために check intent が呼び出されます)
      1. YES: get intent が呼び出され、get インテントが正常に返された場合、アカウントはリンクされています。
      2. × : create intent が呼び出され、作成インテントが正常に返された場合、アカウントはリンクされています。

既存のユーザー アカウントを確認する(インテントを確認する)

ユーザーが Google プロフィールへのアクセスに同意すると、Google は、Google ユーザー ID の署名付きアサーションを含むリクエストを送信します。アサーションには、ユーザーの Google アカウント ID、名前、メールアドレスなどの情報が含まれます。リクエストは、プロジェクト用に構成されたトークン交換エンドポイントによって処理されます。

対応する Google アカウントがすでに認証システムに存在する場合は、トークン交換エンドポイントから account_found=true が返されます。Google アカウントが既存のユーザーと一致しない場合、トークン交換エンドポイントは account_found=false を含む HTTP 404 Not Found エラーを返します。

リクエストは次のようになります。

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

grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&intent=check&assertion=JWT&scope=SCOPES&client_id=GOOGLE_CLIENT_ID&client_secret=GOOGLE_CLIENT_SECRET

トークン交換エンドポイントでは、以下のパラメータを処理する必要があります。

トークン交換エンドポイントのパラメータ
intent このリクエストの場合、このパラメータの値は check です。
grant_type 交換されるトークンの種類。これらのリクエストの場合、このパラメータの値は urn:ietf:params:oauth:grant-type:jwt-bearer になります。
assertion Google ユーザー ID の署名付きアサーションを提供する JSON Web Token(JWT)。JWT には、ユーザーの Google アカウント ID、名前、メールアドレスなどの情報が含まれています。
client_id Google に割り当てたクライアント ID。
client_secret Google に割り当てたクライアント シークレット。

check インテント リクエストに応答するには、トークン交換エンドポイントで次の手順を行う必要があります。

  • JWT アサーションを検証してデコードします。
  • 認証システムに Google アカウントがすでに存在するかどうかを確認します。
JWTアサーションを検証してデコードします

言語のJWTデコードライブラリを使用して、JWTアサーションを検証およびデコードできますJWKまたはPEM形式で利用可能なGoogleの公開鍵を使用して、トークンの署名を確認します。

デコードすると、JWTアサーションは次の例のようになります:

{
  "sub": "1234567890",      // The unique ID of the user's Google Account
  "iss": "https://accounts.google.com",        // The assertion's issuer
  "aud": "123-abc.apps.googleusercontent.com", // Your server's client ID
  "iat": 233366400,         // Unix timestamp of the assertion's creation time
  "exp": 233370000,         // Unix timestamp of the assertion's expiration time
  "name": "Jan Jansen",
  "given_name": "Jan",
  "family_name": "Jansen",
  "email": "jan@gmail.com", // If present, the user's email address
  "email_verified": true,   // true, if Google has verified the email address
  "hd": "example.com",      // If present, the host domain of the user's GSuite email address
                            // If present, a URL to user's profile picture
  "picture": "https://lh3.googleusercontent.com/a-/AOh14GjlTnZKHAeb94A-FmEbwZv7uJD986VOF1mJGb2YYQ",
  "locale": "en_US"         // User's locale, from browser or phone settings
}

トークンの署名の確認に加えて、アサーションの発行者( issフィールド)がhttps://accounts.google.comであること、オーディエンス( audフィールド)が割り当てられたクライアントIDであること、トークンの有効期限が切れていないこと( expフィールド)。

emailemail_verifiedhdフィールドを使用して、Googleがメールアドレスをホストしていて権限があるかどうかを判断できます。 Googleが信頼できる場合、ユーザーは現在正当なアカウント所有者であることがわかっており、パスワードやその他のチャレンジ方法をスキップできます。それ以外の場合は、これらの方法を使用して、リンクする前にアカウントを確認できます。

Googleが権威を持っている場合:

  • emailには@gmail.comサフィックスが付いています。これはGmailアカウントです。
  • email_verifiedがtrueで、 hdが設定されている場合、これはemail_verifiedアカウントです。

ユーザーはGmailやGSuiteを使用せずにGoogleアカウントに登録できます。 email @gmail.comサフィックスが含まれておらず、 hdがない場合、Googleは信頼できません。ユーザーを確認するには、パスワードまたはその他のチャレンジ方法をお勧めします。 email_verfiedは、Googleアカウントが作成されたときにGoogleが最初にユーザーを確認したため、trueになることもありますが、サードパーティの電子メールアカウントの所有権が変更された可能性があります。

Google アカウントが認証システムに存在するかどうかの確認

次のいずれかの条件を満たしていることを確認します。

  • アサーションの sub フィールドにある Google アカウント ID が、ユーザー データベースにあります。
  • アサーションに含まれているメールアドレスが、ユーザーのデータベースに登録されているユーザーと一致している。

いずれかの条件を満たしていれば、ユーザーはすでに登録しています。その場合、次のようなレスポンスが返されます。

HTTP/1.1 200 Success
Content-Type: application/json;charset=UTF-8

{
  "account_found":"true",
}

Google アカウント ID とアサーションで指定されたメールアドレスのいずれもデータベース内のユーザーと一致しない場合、ユーザーはまだ登録していません。その場合、トークン交換エンドポイントは "account_found": "false" を指定する HTTP 404 エラーを返す必要があります。次に例を示します。

HTTP/1.1 404 Not found
Content-Type: application/json;charset=UTF-8

{
  "account_found":"false",
}

自動リンクを処理する(インテントを取得)

ユーザーが Google プロフィールへのアクセスに同意すると、Google は、Google ユーザー ID の署名付きアサーションを含むリクエストを送信します。アサーションには、ユーザーの Google アカウント ID、名前、メールアドレスなどの情報が含まれます。リクエストは、プロジェクト用に構成されたトークン交換エンドポイントによって処理されます。

対応する Google アカウントがすでに認証システムに存在している場合は、トークン交換エンドポイントからユーザーのトークンが返されます。Google アカウントが既存のユーザーと一致しない場合、トークン交換エンドポイントは linking_error エラーとオプションの login_hint を返します。

リクエストは次のようになります。

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

grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&intent=get&assertion=JWT&scope=SCOPES&client_id=GOOGLE_CLIENT_ID&client_secret=GOOGLE_CLIENT_SECRET

トークン交換エンドポイントでは、以下のパラメータを処理する必要があります。

トークン交換エンドポイントのパラメータ
intent このリクエストの場合、このパラメータの値は get です。
grant_type 交換されるトークンの種類。これらのリクエストの場合、このパラメータの値は urn:ietf:params:oauth:grant-type:jwt-bearer になります。
assertion Google ユーザー ID の署名付きアサーションを提供する JSON Web Token(JWT)。JWT には、ユーザーの Google アカウント ID、名前、メールアドレスなどの情報が含まれています。
scope 省略可: ユーザーにリクエストするように Google が構成したスコープ。
client_id Google に割り当てたクライアント ID。
client_secret Google に割り当てたクライアント シークレット。

get インテント リクエストに応答するには、トークン交換エンドポイントで次の手順を行う必要があります。

  • JWT アサーションを検証してデコードします。
  • 認証システムに Google アカウントがすでに存在するかどうかを確認します。
JWTアサーションを検証してデコードします

言語のJWTデコードライブラリを使用して、JWTアサーションを検証およびデコードできますJWKまたはPEM形式で利用可能なGoogleの公開鍵を使用して、トークンの署名を確認します。

デコードすると、JWTアサーションは次の例のようになります:

{
  "sub": "1234567890",      // The unique ID of the user's Google Account
  "iss": "https://accounts.google.com",        // The assertion's issuer
  "aud": "123-abc.apps.googleusercontent.com", // Your server's client ID
  "iat": 233366400,         // Unix timestamp of the assertion's creation time
  "exp": 233370000,         // Unix timestamp of the assertion's expiration time
  "name": "Jan Jansen",
  "given_name": "Jan",
  "family_name": "Jansen",
  "email": "jan@gmail.com", // If present, the user's email address
  "email_verified": true,   // true, if Google has verified the email address
  "hd": "example.com",      // If present, the host domain of the user's GSuite email address
                            // If present, a URL to user's profile picture
  "picture": "https://lh3.googleusercontent.com/a-/AOh14GjlTnZKHAeb94A-FmEbwZv7uJD986VOF1mJGb2YYQ",
  "locale": "en_US"         // User's locale, from browser or phone settings
}

トークンの署名の確認に加えて、アサーションの発行者( issフィールド)がhttps://accounts.google.comであること、オーディエンス( audフィールド)が割り当てられたクライアントIDであること、トークンの有効期限が切れていないこと( expフィールド)。

emailemail_verifiedhdフィールドを使用して、Googleがメールアドレスをホストしていて権限があるかどうかを判断できます。 Googleが信頼できる場合、ユーザーは現在正当なアカウント所有者であることがわかっており、パスワードやその他のチャレンジ方法をスキップできます。それ以外の場合は、これらの方法を使用して、リンクする前にアカウントを確認できます。

Googleが権威を持っている場合:

  • emailには@gmail.comサフィックスが付いています。これはGmailアカウントです。
  • email_verifiedがtrueで、 hdが設定されている場合、これはemail_verifiedアカウントです。

ユーザーはGmailやGSuiteを使用せずにGoogleアカウントに登録できます。 email @gmail.comサフィックスが含まれておらず、 hdがない場合、Googleは信頼できません。ユーザーを確認するには、パスワードまたはその他のチャレンジ方法をお勧めします。 email_verfiedは、Googleアカウントが作成されたときにGoogleが最初にユーザーを確認したため、trueになることもありますが、サードパーティの電子メールアカウントの所有権が変更された可能性があります。

Google アカウントが認証システムに存在するかどうかの確認

次のいずれかの条件を満たしていることを確認します。

  • アサーションの sub フィールドにある Google アカウント ID が、ユーザー データベースにあります。
  • アサーションに含まれているメールアドレスが、ユーザーのデータベースに登録されているユーザーと一致している。

ユーザーのアカウントが見つかった場合は、アクセス トークンを発行して、HTTPS レスポンス本文の JSON オブジェクトに値を返します(例:

{
  "token_type": "Bearer",
  "access_token": "ACCESS_TOKEN",

  "expires_in": SECONDS_TO_EXPIRATION
}
)。

場合によっては、ID トークンに基づくアカウントのリンクが失敗することがあります。なんらかの理由で存在する場合、トークン交換エンドポイントは error=linking_error を指定する HTTP 401 エラーで応答する必要があります。次に例を示します。

HTTP/1.1 401 Unauthorized
Content-Type: application/json;charset=UTF-8

{
  "error":"linking_error",
  "login_hint":"foo@bar.com"
}

Google が linking_error で 401 エラー レスポンスを受け取ると、パラメータとして login_hint を使用してユーザーが認可エンドポイントに送信されます。ユーザーは、ブラウザで OAuth リンクフローを使用してアカウントのリンクを完了します。

Google ログインを使用してアカウントを処理する(インテントを作成する)

ユーザーがサービス上でアカウントを作成する必要がある場合、Google は intent=create を指定するトークン交換エンドポイントにリクエストを送信します。

リクエストは次のようになります。

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

response_type=token&grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&scope=SCOPES&intent=create&assertion=JWT&client_id=GOOGLE_CLIENT_ID&client_secret=GOOGLE_CLIENT_SECRET

トークン交換エンドポイントは次のパラメータを処理できる必要があります。

トークン交換エンドポイントのパラメータ
intent このリクエストの場合、このパラメータの値は create です。
grant_type 交換されるトークンの種類。これらのリクエストの場合、このパラメータの値は urn:ietf:params:oauth:grant-type:jwt-bearer になります。
assertion Google ユーザー ID の署名付きアサーションを提供する JSON Web Token(JWT)。JWT には、ユーザーの Google アカウント ID、名前、メールアドレスなどの情報が含まれています。
client_id Google に割り当てたクライアント ID。
client_secret Google に割り当てたクライアント シークレット。

assertion パラメータ内の JWT には、ユーザーの Google アカウント ID、名前、メールアドレスが含まれます。これらの ID を使用して、サービスで新しいアカウントを作成できます。

create インテント リクエストに応答するには、トークン交換エンドポイントで次の手順を行う必要があります。

  • JWT アサーションを検証してデコードします。
  • ユーザー情報を確認し、新しいアカウントを作成します。
JWTアサーションを検証してデコードします

言語のJWTデコードライブラリを使用して、JWTアサーションを検証およびデコードできますJWKまたはPEM形式で利用可能なGoogleの公開鍵を使用して、トークンの署名を確認します。

デコードすると、JWTアサーションは次の例のようになります:

{
  "sub": "1234567890",      // The unique ID of the user's Google Account
  "iss": "https://accounts.google.com",        // The assertion's issuer
  "aud": "123-abc.apps.googleusercontent.com", // Your server's client ID
  "iat": 233366400,         // Unix timestamp of the assertion's creation time
  "exp": 233370000,         // Unix timestamp of the assertion's expiration time
  "name": "Jan Jansen",
  "given_name": "Jan",
  "family_name": "Jansen",
  "email": "jan@gmail.com", // If present, the user's email address
  "email_verified": true,   // true, if Google has verified the email address
  "hd": "example.com",      // If present, the host domain of the user's GSuite email address
                            // If present, a URL to user's profile picture
  "picture": "https://lh3.googleusercontent.com/a-/AOh14GjlTnZKHAeb94A-FmEbwZv7uJD986VOF1mJGb2YYQ",
  "locale": "en_US"         // User's locale, from browser or phone settings
}

トークンの署名の確認に加えて、アサーションの発行者( issフィールド)がhttps://accounts.google.comであること、オーディエンス( audフィールド)が割り当てられたクライアントIDであること、トークンの有効期限が切れていないこと( expフィールド)。

emailemail_verifiedhdフィールドを使用して、Googleがメールアドレスをホストしていて権限があるかどうかを判断できます。 Googleが信頼できる場合、ユーザーは現在正当なアカウント所有者であることがわかっており、パスワードやその他のチャレンジ方法をスキップできます。それ以外の場合は、これらの方法を使用して、リンクする前にアカウントを確認できます。

Googleが権威を持っている場合:

  • emailには@gmail.comサフィックスが付いています。これはGmailアカウントです。
  • email_verifiedがtrueで、 hdが設定されている場合、これはemail_verifiedアカウントです。

ユーザーはGmailやGSuiteを使用せずにGoogleアカウントに登録できます。 email @gmail.comサフィックスが含まれておらず、 hdがない場合、Googleは信頼できません。ユーザーを確認するには、パスワードまたはその他のチャレンジ方法をお勧めします。 email_verfiedは、Googleアカウントが作成されたときにGoogleが最初にユーザーを確認したため、trueになることもありますが、サードパーティの電子メールアカウントの所有権が変更された可能性があります。

ユーザー情報を検証して新しいアカウントを作成する

次のいずれかの条件を満たしていることを確認します。

  • アサーションの sub フィールドにある Google アカウント ID が、ユーザー データベースにあります。
  • アサーションに含まれているメールアドレスが、ユーザーのデータベースに登録されているユーザーと一致している。

いずれかの条件に当てはまる場合は、既存のアカウントと Google アカウントをリンクするよう促します。これを行うには、error=linking_error を指定し、ユーザーのメールアドレスを login_hint として指定する HTTP 401 エラーを返します。レスポンスの例を次に示します。

HTTP/1.1 401 Unauthorized
Content-Type: application/json;charset=UTF-8

{
  "error":"linking_error",
  "login_hint":"foo@bar.com"
}

Google が linking_error で 401 エラー レスポンスを受け取ると、パラメータとして login_hint を使用してユーザーが認可エンドポイントに送信されます。ユーザーは、ブラウザで OAuth リンクフローを使用してアカウントのリンクを完了します。

どちらの条件も満たしない場合は、JWT で提供された情報を使用して新しいユーザー アカウントを作成します。新しいアカウントでは通常、パスワードは設定されません。ユーザーがアプリケーション全体で Google にログインできるように、Google ログインを他のプラットフォームに追加することをおすすめします。または、ユーザーにパスワード再設定フローを開始するリンクをメールで送付し、ユーザーが他のプラットフォームでログインできるようにパスワードを設定できるようにします。

作成が完了したら、アクセス トークン を発行し、HTTPS レスポンス本文の JSON オブジェクトに値を返します。例:

{
  "token_type": "Bearer",
  "access_token": "ACCESS_TOKEN",

  "expires_in": SECONDS_TO_EXPIRATION
}

Google API クライアント ID を取得する

アカウントのリンク登録プロセス中に、Google API クライアント ID を入力する必要があります。

OAuth リンクの手順を実行する際に作成したプロジェクトを使用して、API クライアント ID を取得すること。そのための手順は次のとおりです。

  1. Google API Console の [認証情報] ページを開きます。
  2. Google API プロジェクトを作成または選択します。

    プロジェクトにウェブ アプリケーション タイプのクライアント ID がない場合は、[認証情報を作成 & OAuth クライアント ID] をクリックして作成します。[承認済みの JavaScript 生成元] ボックスにサイトのドメインを含めてください。ローカルテストまたは開発を行う場合は、http://localhosthttp://localhost:<port_number> の両方を [承認済みの JavaScript 生成元] フィールドに追加する必要があります。

実装の検証

あなたは使用して実装を検証することができOAuth 2.0の遊び場のツールを。

ツールで、次の手順を実行します。

  1. 設定をクリックし OAuth 2.0の設定]ウィンドウを開きます。
  2. OAuthの流れ場では、クライアント側を選択します。
  3. OAuthのエンドポイント]フィールドで、[カスタム]を選択します。
  4. OAuth2.0エンドポイントとGoogleに割り当てたクライアントIDを対応するフィールドに指定します。
  5. ステップ1セクションでは、すべてのGoogleサービスのスコープを選択しないでください。代わりに、このフィールドを空白のままにするか、サーバーに有効なスコープ(または、OAuthスコープを使用しない場合は任意の文字列)を入力してください。設定が完了したら、承認のAPIをクリックします。
  6. ステップ2ステップ3のセクションでは、OAuth 2.0のフローを通過し、意図したように各ステップが動作することを確認。

あなたは使用して実装を検証することができ、Googleアカウントのリンクデモツールを。

ツールで、次の手順を実行します。

  1. Googleのボタンでサインインしをクリックしてください。
  2. リンクするアカウントを選択してください。
  3. サービスIDを入力します。
  4. オプションで、アクセスを要求する1つ以上のスコープを入力します。
  5. スタートデモをクリックしてください。
  6. プロンプトが表示されたら、リンク要求に同意して拒否できることを確認します。
  7. プラットフォームにリダイレクトされていることを確認します。