簡化 OAuth 和 Google 登入機制的連結程序

總覽

以 OAuth 為基礎的 Google 登入簡化連結會在 OAuth 連結上方加入 Google 登入功能。這可為 Google 使用者提供無縫連結體驗,並啟用帳戶建立功能,讓使用者可透過 Google 帳戶在您的服務中建立新帳戶。

如要使用 OAuth 和 Google 登入功能執行帳戶連結,請按照下列一般步驟操作:

  1. 首先,請使用者同意存取其 Google 個人資料。
  2. 使用設定檔中的資訊,確認使用者帳戶是否存在。
  3. 如果是現有使用者,請連結帳戶。
  4. 如果您在驗證系統中找不到 Google 使用者,請驗證從 Google 收到的 ID 權杖。接著,您可以根據 ID 權杖中包含的個人資料資訊建立使用者。
這張圖片顯示使用者如何透過簡化連結流程連結 Google 帳戶。第一張螢幕截圖顯示使用者如何選取要連結的應用程式。第二張螢幕截圖可讓使用者確認自己是否已在您的服務中建立帳戶。第三張螢幕截圖可讓使用者選取要連結的 Google 帳戶。第四張螢幕截圖顯示確認訊息,說明使用者已將 Google 帳戶連結至您的應用程式。第五張螢幕截圖顯示 Google 應用程式中已成功連結的使用者帳戶。

圖 1. 使用簡化連結功能在使用者手機上連結帳戶

簡化連結功能的相關規定

實作 OAuth 伺服器

您的 權杖交換端點必須支援 checkcreateget 意圖。下方顯示透過帳戶連結流程完成的步驟,並指出不同意圖的呼叫時機:

  1. 使用者是否在您的驗證系統中擁有帳戶?(使用者可選取「是」或「否」決定是否同意)
    1. 是:使用者是否會使用與 Google 帳戶相關聯的電子郵件地址登入平台?(使用者可選取「是」或「否」決定是否同意)
      1. 是:使用者是否在您的驗證系統中擁有相符的帳戶?(系統會呼叫 check intent 進行確認)
        1. 是:如果 getIntent 成功傳回,系統會呼叫 get intent,並連結帳戶。
        2. 否:建立新帳戶?(使用者可選取「是」或「否」決定是否同意)
          1. 是:如果建立意圖的傳回值成功,系統會呼叫 create intent,並連結帳戶。
          2. 否:觸發 Web OAuth 流程,使用者會被導向瀏覽器,並提供連結其他電子郵件的選項。
      2. 否:系統會觸發 網路 OAuth 流程,將使用者導向瀏覽器,並提供連結其他電子郵件地址的選項。
    2. 否:使用者是否在您的驗證系統中擁有相符的帳戶?(系統會呼叫 check intent 進行確認)
      1. 是:如果 getIntent 成功傳回,系統會呼叫 get intent,並連結帳戶。
      2. 否:如果建立意圖的傳回值為成功,系統會呼叫 create intent,並連結帳戶。

Check for an existing user account (check intent)

After the user gives consent to access their Google profile, Google sends a request that contains a signed assertion of the Google user's identity. The assertion contains information that includes the user's Google Account ID, name, and email address. The token exchange endpoint configured for your project handles that request.

If the corresponding Google account is already present in your authentication system, your token exchange endpoint responds with account_found=true. If the Google account doesn't match an existing user, your token exchange endpoint returns an HTTP 404 Not Found error with account_found=false.

The request has the following form:

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

Your token exchange endpoint must be able to handle the following parameters:

Token endpoint parameters
intent For these requests, the value of this parameter is check.
grant_type The type of token being exchanged. For these requests, this parameter has the value urn:ietf:params:oauth:grant-type:jwt-bearer.
assertion A JSON Web Token (JWT) that provides a signed assertion of the Google user's identity. The JWT contains information that includes the user's Google Account ID, name, and email address.
client_id The client ID you assigned to Google.
client_secret The client secret you assigned to Google.

To respond to the check intent requests, your token exchange endpoint must perform the following steps:

  • Validate and decode the JWT assertion.
  • Check if the Google account is already present in your authentication system.
Validate and decode the JWT assertion

You can validate and decode the JWT assertion by using a JWT-decoding library for your language. Use Google's public keys, available in JWK or PEM formats, to verify the token's signature.

When decoded, the JWT assertion looks like the following example:

{
  "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
}

In addition to verifying the token's signature, verify that the assertion's issuer (iss field) is https://accounts.google.com, that the audience (aud field) is your assigned client ID, and that the token has not expired (exp field).

Using the email, email_verified and hd fields you can determine if Google hosts and is authoritative for an email address. In cases where Google is authoritative the user is currently known to be the legitimate account owner and you may skip password or other challenges methods. Otherwise, these methods can be used to verify the account prior to linking.

Cases where Google is authoritative:

  • email has a @gmail.com suffix, this is a Gmail account.
  • email_verified is true and hd is set, this is a G Suite account.

Users may register for Google Accounts without using Gmail or G Suite. When email does not contain a @gmail.com suffix and hd is absent Google is not authoritative and password or other challenge methods are recommended to verify the user. email_verified can also be true as Google initially verified the user when the Google account was created, however ownership of the third party email account may have since changed.

Check if the Google account is already present in your authentication system

Check whether either of the following conditions are true:

  • The Google Account ID, found in the assertion's sub field, is in your user database.
  • The email address in the assertion matches a user in your user database.

If either condition is true, the user has already signed up. In that case, return a response like the following:

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

{
  "account_found":"true",
}

If neither the Google Account ID nor the email address specified in the assertion matches a user in your database, the user hasn't signed up yet. In this case, your token exchange endpoint needs to reply with a HTTP 404 error that specifies "account_found": "false", as in the following example:

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

{
  "account_found":"false",
}

Handle automatic linking (get intent)

After the user gives consent to access their Google profile, Google sends a request that contains a signed assertion of the Google user's identity. The assertion contains information that includes the user's Google Account ID, name, and email address. The token exchange endpoint configured for your project handles that request.

If the corresponding Google Account is already present in your authentication system, your token exchange endpoint returns a token for the user. If the Google Account doesn't match an existing user, your token exchange endpoint returns a linking_error error and optional login_hint.

The request has the following form:

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

Your token exchange endpoint must be able to handle the following parameters:

Token endpoint parameters
intent For these requests, the value of this parameter is get.
grant_type The type of token being exchanged. For these requests, this parameter has the value urn:ietf:params:oauth:grant-type:jwt-bearer.
assertion A JSON Web Token (JWT) that provides a signed assertion of the Google user's identity. The JWT contains information that includes the user's Google Account ID, name, and email address.
scope Optional: Any scopes that you've configured Google to request from users.
client_id The client ID you assigned to Google.
client_secret The client secret you assigned to Google.

To respond to the get intent requests, your token exchange endpoint must perform the following steps:

  • Validate and decode the JWT assertion.
  • Check if the Google account is already present in your authentication system.
Validate and decode the JWT assertion

You can validate and decode the JWT assertion by using a JWT-decoding library for your language. Use Google's public keys, available in JWK or PEM formats, to verify the token's signature.

When decoded, the JWT assertion looks like the following example:

{
  "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
}

In addition to verifying the token's signature, verify that the assertion's issuer (iss field) is https://accounts.google.com, that the audience (aud field) is your assigned client ID, and that the token has not expired (exp field).

Using the email, email_verified and hd fields you can determine if Google hosts and is authoritative for an email address. In cases where Google is authoritative the user is currently known to be the legitimate account owner and you may skip password or other challenges methods. Otherwise, these methods can be used to verify the account prior to linking.

Cases where Google is authoritative:

  • email has a @gmail.com suffix, this is a Gmail account.
  • email_verified is true and hd is set, this is a G Suite account.

Users may register for Google Accounts without using Gmail or G Suite. When email does not contain a @gmail.com suffix and hd is absent Google is not authoritative and password or other challenge methods are recommended to verify the user. email_verified can also be true as Google initially verified the user when the Google account was created, however ownership of the third party email account may have since changed.

Check if the Google account is already present in your authentication system

Check whether either of the following conditions are true:

  • The Google Account ID, found in the assertion's sub field, is in your user database.
  • The email address in the assertion matches a user in your user database.

If an account is found for the user, issue an access token and return the values in a JSON object in the body of your HTTPS response, like in the following example:

{
  "token_type": "Bearer",
  "access_token": "ACCESS_TOKEN",
  "refresh_token": "REFRESH_TOKEN",
  "expires_in": SECONDS_TO_EXPIRATION
}

In some cases, account linking based on ID token might fail for the user. If it does so for any reason, your token exchange endpoint needs to reply with a HTTP 401 error that specifies error=linking_error, as the following example shows:

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

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

When Google receives a 401 error response with linking_error, Google sends the user to your authorization endpoint with login_hint as a parameter. The user completes account linking using the OAuth linking flow in their browser.

Handle account creation via Google Sign-In (create intent)

When a user needs to create an account on your service, Google makes a request to your token exchange endpoint that specifies intent=create.

The request has the following form:

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

Your token exchange endpoint must able to handle the following parameters:

Token endpoint parameters
intent For these requests, the value of this parameter is create.
grant_type The type of token being exchanged. For these requests, this parameter has the value urn:ietf:params:oauth:grant-type:jwt-bearer.
assertion A JSON Web Token (JWT) that provides a signed assertion of the Google user's identity. The JWT contains information that includes the user's Google Account ID, name, and email address.
client_id The client ID you assigned to Google.
client_secret The client secret you assigned to Google.

The JWT within the assertion parameter contains the user's Google Account ID, name, and email address, which you can use to create a new account on your service.

To respond to the create intent requests, your token exchange endpoint must perform the following steps:

  • Validate and decode the JWT assertion.
  • Validate user information and create new account.
Validate and decode the JWT assertion

You can validate and decode the JWT assertion by using a JWT-decoding library for your language. Use Google's public keys, available in JWK or PEM formats, to verify the token's signature.

When decoded, the JWT assertion looks like the following example:

{
  "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
}

In addition to verifying the token's signature, verify that the assertion's issuer (iss field) is https://accounts.google.com, that the audience (aud field) is your assigned client ID, and that the token has not expired (exp field).

Using the email, email_verified and hd fields you can determine if Google hosts and is authoritative for an email address. In cases where Google is authoritative the user is currently known to be the legitimate account owner and you may skip password or other challenges methods. Otherwise, these methods can be used to verify the account prior to linking.

Cases where Google is authoritative:

  • email has a @gmail.com suffix, this is a Gmail account.
  • email_verified is true and hd is set, this is a G Suite account.

Users may register for Google Accounts without using Gmail or G Suite. When email does not contain a @gmail.com suffix and hd is absent Google is not authoritative and password or other challenge methods are recommended to verify the user. email_verified can also be true as Google initially verified the user when the Google account was created, however ownership of the third party email account may have since changed.

Validate user information and create new account

Check whether either of the following conditions are true:

  • The Google Account ID, found in the assertion's sub field, is in your user database.
  • The email address in the assertion matches a user in your user database.

If either condition is true, prompt the user to link their existing account with their Google Account. To do so, respond to the request with an HTTP 401 error that specifies error=linking_error and gives the user's email address as the login_hint. The following is a sample response:

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

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

When Google receives a 401 error response with linking_error, Google sends the user to your authorization endpoint with login_hint as a parameter. The user completes account linking using the OAuth linking flow in their browser.

If neither condition is true, create a new user account with the information provided in the JWT. New accounts don't typically have a password set. It's recommended that you add Google Sign-In to other platforms to enable users to log in with Google across the surfaces of your application. Alternatively, you can email the user a link that starts your password recovery flow to allow the user to set a password to sign in on other platforms.

When the creation is completed, issue an access token and refresh token and return the values in a JSON object in the body of your HTTPS response, like in the following example:

{
  "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. Go to the Credentials page.
  2. 建立或選取 Google API 專案。

    如果專案沒有網路應用程式類型的用戶端 ID,請按一下「Create Client」建立用戶端 ID。請務必在「已授權的 JavaScript 來源」方塊中加入網站的網域。執行本機測試或開發時,您必須將 http://localhosthttp://localhost:<port_number> 都新增至「已授權的 JavaScript 來源」欄位。

驗證實作

您可以使用 OAuth 2.0 Playground 工具驗證實作結果。

請在工具中按照下列步驟操作:

  1. 點選「Configuration」圖示 ,開啟 OAuth 2.0 設定視窗。
  2. 在「OAuth 流程」欄位中,選取「用戶端」
  3. 在「OAuth 端點」欄位中,選取「自訂」
  4. 在對應的欄位中指定 OAuth 2.0 端點,以及您指派給 Google 的用戶端 ID。
  5. 在「步驟 1」部分中,請勿選取任何 Google 範圍。請改為將這個欄位留空,或輸入有效的伺服器範圍 (如果您不使用 OAuth 範圍,則輸入任意字串)。完成後,按一下「授權 API」
  6. 在「步驟 2」和「步驟 3」部分,請完成 OAuth 2.0 流程,並確認每個步驟都能正常運作。

您可以使用 Google 帳戶連結示範工具驗證實作成果。

在工具中執行下列步驟:

  1. 按一下「使用 Google 帳戶登入」按鈕。
  2. 選擇要連結的帳戶。
  3. 輸入服務 ID。
  4. 您可以選擇輸入一或多個要申請存取權的範圍。
  5. 按一下「開始試用」
  6. 系統顯示提示時,請確認您可以同意或拒絕連結要求。
  7. 確認系統是否會將你重新導向至平台。