伺服器端應用程式的 Google 登入

如要代表使用者離線使用 Google 服務,您必須 使用混合式伺服器端流程,讓使用者在用戶端授權您的應用程式 並透過 JavaScript API 用戶端傳送,且您傳送特殊的一次性 授權碼傳送至您的伺服器。你的伺服器會交換此一次性憑證 藉此向 Google 取得自身的存取權,並更新權杖,讓伺服器能 自行發出 API 呼叫,以便在使用者離線時進行。 與純伺服器端相比,這個一次性程式碼流程擁有安全性優勢 便持續將存取權杖傳送至伺服器

取得伺服器端存取權杖的登入流程 應用程式將如下所示。

一次性驗證碼具有多項安全優勢。輸入驗證碼後,Google 可以直接將權杖直接提供給伺服器,過程中完全不需要。 雖然我們不建議公開程式碼外洩,但這類程式碼非常難以使用 而無需用戶端密碼妥善保管用戶端密鑰!

實作一次性程式碼流程

Google 登入按鈕會同時提供存取權杖授權碼。驗證碼是一次性的驗證碼,可供伺服器交換 連線至 Google 伺服器

以下程式碼範例示範如何 一次性程式碼流程

使用一次性代碼的流程驗證 Google 登入流程的步驟如下:

步驟 1:建立用戶端 ID 與用戶端密鑰

如要建立用戶端 ID 和用戶端密鑰,請建立 Google API 控制台專案。 設定 OAuth 用戶端 ID,並註冊您的 JavaScript 來源:

  1. 前往 Google API 控制台

  2. 在專案下拉式選單中選取現有專案,或建立新專案 選取「建立新專案」

  3. 在側欄中「API 與「服務」下方,選取「憑證」,然後點選 設定同意畫面

    選擇電子郵件地址並指定產品名稱,然後按下「儲存」

  4. 在「Credentials」分頁中,選取「Create credentials」下拉式選單 然後選擇「OAuth 用戶端 ID」

  5. 在「Application type」(應用程式類型) 下方,選取 [Web application] (網頁應用程式)

    註冊應用程式可存取的 Google API來源是一組不重複的通訊協定組合 主機名稱和通訊埠

    1. 請在「授權的 JavaScript 來源」欄位中輸入來源 您可以輸入多個來源,讓應用程式執行 不同的通訊協定、網域或子網域無法使用萬用字元。 在以下範例中,第二個網址可以是實際網址。

      http://localhost:8080
      https://myproductionurl.example.com
      
    2. 「授權的重新導向 URI」欄位不需要輸入值。重新導向 URI 無法與 JavaScript API 搭配使用。

    3. 按下「Create」(建立) 按鈕,

  6. 在畫面上出現的「OAuth 用戶端」對話方塊中,複製用戶端 ID。 用戶端 ID 可讓應用程式存取已啟用的 Google API。

步驟 2:在網頁上加入 Google 平台程式庫

加入下列指令碼來示範匿名函式, 這個 index.html 網頁的 DOM 會插入指令碼。

<!-- The top of file index.html -->
<html itemscope itemtype="http://schema.org/Article">
<head>
  <!-- BEGIN Pre-requisites -->
  <script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js">
  </script>
  <script src="https://apis.google.com/js/client:platform.js?onload=start" async defer>
  </script>
  <!-- END Pre-requisites -->

步驟 3:初始化 GoogleAuth 物件

載入 auth2 程式庫,並呼叫 gapi.auth2.init() 來初始化 GoogleAuth 物件。指定用戶端 ID 和要求的範圍 當你呼叫 init() 時。

<!-- Continuing the <head> section -->
  <script>
    function start() {
      gapi.load('auth2', function() {
        auth2 = gapi.auth2.init({
          client_id: 'YOUR_CLIENT_ID.apps.googleusercontent.com',
          // Scopes to request in addition to 'profile' and 'email'
          //scope: 'additional_scope'
        });
      });
    }
  </script>
</head>
<body>
  <!-- ... -->
</body>
</html>

步驟 4:在頁面中新增登入按鈕

在網頁中加入登入按鈕,然後附加點擊處理常式以呼叫 grantOfflineAccess()敬上 開始進行一次性程式碼流程。

<!-- Add where you want your sign-in button to render -->
<!-- Use an image that follows the branding guidelines in a real app -->
<button id="signinButton">Sign in with Google</button>
<script>
  $('#signinButton').click(function() {
    // signInCallback defined in step 6.
    auth2.grantOfflineAccess().then(signInCallback);
  });
</script>

步驟 5:讓使用者登入

使用者按一下登入按鈕,並授權應用程式存取該權限 您的要求。接著,您在 grantOfflineAccess().then() 方法會傳遞含有 授權碼。例如:

{"code":"4/yU4cQZTMnnMtetyFcIWNItG32eKxxxgXXX-Z4yyJJJo.4qHskT-UtugceFc0ZRONyF4z7U4UmAI"}

步驟 6:將授權碼傳送至伺服器

code 是你的伺服器可自行交換的一次性代碼 存取權杖並更新您只能在 使用者看到授權對話方塊,要求離線存取。 如果您已在select-account prompt OfflineAccessOptions敬上 在步驟 4 中,您必須儲存擷取的更新權杖供日後使用 因為後續的廣告交易平台將針對更新權杖傳回 null。這個流程 ,安全性比標準 OAuth 2.0 流程更加安全。

一律透過有效的授權交換後,一律傳回存取權杖 再也不是件繁重乏味的工作

下列指令碼定義登入按鈕的回呼函式。時間 登入成功,函式就會儲存用戶端端的存取權杖 使用並傳送一次性程式碼到相同網域的伺服器。

<!-- Last part of BODY element in file index.html -->
<script>
function signInCallback(authResult) {
  if (authResult['code']) {

    // Hide the sign-in button now that the user is authorized, for example:
    $('#signinButton').attr('style', 'display: none');

    // Send the code to the server
    $.ajax({
      type: 'POST',
      url: 'http://example.com/storeauthcode',
      // Always include an `X-Requested-With` header in every AJAX request,
      // to protect against CSRF attacks.
      headers: {
        'X-Requested-With': 'XMLHttpRequest'
      },
      contentType: 'application/octet-stream; charset=utf-8',
      success: function(result) {
        // Handle or verify the server response.
      },
      processData: false,
      data: authResult['code']
    });
  } else {
    // There was an error.
  }
}
</script>

步驟 7:以授權碼交換存取權杖

在伺服器上交換驗證碼以取得存取權和更新權杖。使用 以便代表使用者呼叫 Google API 更新憑證,取得新的存取權杖。

如果你要求存取設定檔,也會獲得含有基本資料存取權的 ID 權杖 使用者個人資料中的資訊

例如:

Java
// (Receive authCode via HTTPS POST)


if (request.getHeader("X-Requested-With") == null) {
  // Without the `X-Requested-With` header, this request could be forged. Aborts.
}

// Set path to the Web application client_secret_*.json file you downloaded from the
// Google API Console: https://console.cloud.google.com/apis/credentials
// You can also find your Web application client ID and client secret from the
// console and specify them directly when you create the GoogleAuthorizationCodeTokenRequest
// object.
String CLIENT_SECRET_FILE = "/path/to/client_secret.json";

// Exchange auth code for access token
GoogleClientSecrets clientSecrets =
    GoogleClientSecrets.load(
        JacksonFactory.getDefaultInstance(), new FileReader(CLIENT_SECRET_FILE));
GoogleTokenResponse tokenResponse =
          new GoogleAuthorizationCodeTokenRequest(
              new NetHttpTransport(),
              JacksonFactory.getDefaultInstance(),
              "https://oauth2.googleapis.com/token",
              clientSecrets.getDetails().getClientId(),
              clientSecrets.getDetails().getClientSecret(),
              authCode,
              REDIRECT_URI)  // Specify the same redirect URI that you use with your web
                             // app. If you don't have a web version of your app, you can
                             // specify an empty string.
              .execute();

String accessToken = tokenResponse.getAccessToken();

// Use access token to call API
GoogleCredential credential = new GoogleCredential().setAccessToken(accessToken);
Drive drive =
    new Drive.Builder(new NetHttpTransport(), JacksonFactory.getDefaultInstance(), credential)
        .setApplicationName("Auth Code Exchange Demo")
        .build();
File file = drive.files().get("appfolder").execute();

// Get profile info from ID token
GoogleIdToken idToken = tokenResponse.parseIdToken();
GoogleIdToken.Payload payload = idToken.getPayload();
String userId = payload.getSubject();  // Use this value as a key to identify a user.
String email = payload.getEmail();
boolean emailVerified = Boolean.valueOf(payload.getEmailVerified());
String name = (String) payload.get("name");
String pictureUrl = (String) payload.get("picture");
String locale = (String) payload.get("locale");
String familyName = (String) payload.get("family_name");
String givenName = (String) payload.get("given_name");
Python
from apiclient import discovery
import httplib2
from oauth2client import client

# (Receive auth_code by HTTPS POST)


# If this request does not have `X-Requested-With` header, this could be a CSRF
if not request.headers.get('X-Requested-With'):
    abort(403)

# Set path to the Web application client_secret_*.json file you downloaded from the
# Google API Console: https://console.cloud.google.com/apis/credentials
CLIENT_SECRET_FILE = '/path/to/client_secret.json'

# Exchange auth code for access token, refresh token, and ID token
credentials = client.credentials_from_clientsecrets_and_code(
    CLIENT_SECRET_FILE,
    ['https://www.googleapis.com/auth/drive.appdata', 'profile', 'email'],
    auth_code)

# Call Google API
http_auth = credentials.authorize(httplib2.Http())
drive_service = discovery.build('drive', 'v3', http=http_auth)
appfolder = drive_service.files().get(fileId='appfolder').execute()

# Get profile info from ID token
userid = credentials.id_token['sub']
email = credentials.id_token['email']