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",

  "refresh_token": "REFRESH_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",

  "refresh_token": "REFRESH_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 生成元] フィールドに追加する必要があります。

実装の検証

You can validate your implementation by using the OAuth 2.0 Playground tool.

In the tool, do the following steps:

  1. Click Configuration to open the OAuth 2.0 Configuration window.
  2. In the OAuth flow field, select Client-side.
  3. In the OAuth Endpoints field, select Custom.
  4. Specify your OAuth 2.0 endpoint and the client ID you assigned to Google in the corresponding fields.
  5. In the Step 1 section, don't select any Google scopes. Instead, leave this field blank or type a scope valid for your server (or an arbitrary string if you don't use OAuth scopes). When you're done, click Authorize APIs.
  6. In the Step 2 and Step 3 sections, go through the OAuth 2.0 flow and verify that each step works as intended.

You can validate your implementation by using the Google Account Linking Demo tool.

In the tool, do the following steps:

  1. Click the Sign-in with Google button.
  2. Choose the account you'd like to link.
  3. Enter the service ID.
  4. Optionally enter one or more scopes that you will request access for.
  5. Click Start Demo.
  6. When prompted, confirm that you may consent and deny the linking request.
  7. Confirm that you are redirected to your platform.