程式碼研究室:針對 Google 工作區進行網域驗證

為什麼要進行網域驗證?

雖然 Cloud Channel API 允許您大規模佈建 Google 工作區,但客戶仍須採取以下行動才能啟用服務。

  1. 接受《服務條款》
  2. 驗證網域擁有權
  3. 設定 MX 記錄

新建立的客戶初次存取管理控制台 (admin.google.com) 時,請遵循引導式程序。

如果您透過程式存取網域的 DNS 記錄 (例如,您轉售網域給客戶),就能自動進行步驟 2 和 3 來提高啟用率,因為這些步驟通常需要有技術知識經銷商客戶。

本程式碼研究室說明如何使用網站驗證 API 實作這個自動化作業。

事前準備

  • 請務必完成 API 設定程式碼研究室,以設定 Google Cloud 專案並建立服務帳戶,以呼叫 Cloud Channel API。

  • 瞭解 Channel Services 作業

  • 建議您使用這個 Test Channel Services Console 來使用這個程式碼研究室。

這個程式碼研究室也會預期您完成工作區端對端佈建程式碼研究室

總覽

驗證 Google 工作區授權的網域涉及數種 API 呼叫。

自動化網域驗證步驟

Site Verification API 未開放給經銷商使用。網域驗證是用於各項 Google 產品 (Search Console、廣告等) 的信號。本文所述程序,將經銷商網域的超級管理員設為網域的「擁有者」,並將客戶的第一位管理員設為共同擁有者。如要進一步瞭解這些概念,請參閱開始使用 Site Verification API 頁面。

步驟 1:準備網站驗證 API

  • 前往 Google Cloud Console 的「API Library」(API 資料庫) 區段,並啟用 Site Verification API
  • 使用經銷商網域的超級管理員前往全網域委派頁面
    • 在服務帳戶所在的列中,按一下 [Edit] (編輯)
    • 在「OAuth Scopes」(OAuth 範圍) 欄位中輸入 https://www.googleapis.com/auth/siteverification
    • 按一下 [授權]
  • 安裝 Site Verification API 用戶端程式庫

步驟 2:取得驗證憑證

這個程式碼研究室會著重於驗證網域最常見的方法:使用 TXT DNS 記錄。Site Verification API 支援其他驗證方法

如要擷取您要以 TXT 記錄形式放入的憑證,您必須取得 type=INET_DOMAINverificationMethod=DNS_TXT 的憑證。

將下列程式碼中的資訊填入下列程式碼。

  • jsonKeyFile建立服務帳戶時產生的 JSON 金鑰檔案路徑。
  • resellerAdminUser:經銷商網域超級管理員的電子郵件地址。
  • customerDomain:客戶的網域。如果您在 Test Channel Services Console 上執行此程式碼研究室,請確保網域遵循網域命名慣例。

C#

使用以下匯入作業:

using Google.Apis.Auth.OAuth2;
using Google.Apis.Services;
using Google.Apis.SiteVerification.v1;
using Google.Apis.SiteVerification.v1.Data;

建立 API 用戶端並擷取憑證:

// Set up credentials with user impersonation
ICredential credential = GoogleCredential.FromFile(jsonKeyFile)
                             .CreateScoped("https://www.googleapis.com/auth/siteverification")
                             .CreateWithUser(resellerAdminUser);

// Create the API service
var verificationService = new SiteVerificationService(new BaseClientService.Initializer{
    HttpClientInitializer = credential,
});

// Fetch the token
var request = new SiteVerificationWebResourceGettokenRequest {
  VerificationMethod = "DNS_TXT",
  Site = new SiteVerificationWebResourceGettokenRequest.SiteData {
    Type = "INET_DOMAIN",
    Identifier = customerDomain
  }
};
var response = verificationService.WebResource.GetToken(request).Execute();
string token = response.Token;
Console.WriteLine("Site Verification token: " + token);

Go

使用以下匯入作業:

import (
  "context"
  "fmt"
  "io/ioutil"

  "golang.org/x/oauth2/google"
  "google.golang.org/api/option"
  "google.golang.org/api/siteverification/v1"
)

建立 API 用戶端並擷取憑證:

ctx := context.Background()

// Set up credentials with user impersonation
jsonKey, _ := ioutil.ReadFile(jsonKeyFile)
jwt, _ := google.JWTConfigFromJSON(jsonKey, "https://www.googleapis.com/auth/siteverification")
jwt.Subject = resellerAdminUser
tokenSource := jwt.TokenSource(ctx)

// Create the API Service
verificationService, _ := siteverification.NewService(ctx, option.WithTokenSource(tokenSource))

// Fetch the token
req := &siteverification.SiteVerificationWebResourceGettokenRequest{
  Site: &siteverification.SiteVerificationWebResourceGettokenRequestSite{
    Type:       "INET_DOMAIN",
    Identifier: customerDomain,
  },
  VerificationMethod: "DNS_TXT",
}
res, _ := verificationService.WebResource.GetToken(req).Do()
token := res.Token
fmt.Println("Site verification token: " + token)

Java

使用以下匯入作業:

import com.google.auth.http.HttpCredentialsAdapter;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.auth.oauth2.ServiceAccountCredentials;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.services.siteVerification.SiteVerification;
import com.google.api.services.siteVerification.SiteVerificationScopes;
import com.google.api.services.siteVerification.model.SiteVerificationWebResourceGettokenRequest;
import com.google.api.services.siteVerification.model.SiteVerificationWebResourceGettokenResponse;
import com.google.api.services.siteVerification.model.SiteVerificationWebResourceResource;
import java.io.FileInputStream;
import java.util.Arrays;

建立 API 用戶端並擷取憑證:

// Set up credentials with user impersonation
FileInputStream jsonKeyFileSteam = new FileInputStream(JSON_KEY_FILE);
GoogleCredentials credentials = ServiceAccountCredentials.fromStream(jsonKeyFileSteam)
                                    .createScoped("https://www.googleapis.com/auth/siteverification")
                                    .createDelegated(RESELLER_ADMIN_USER);

// Create the API service
SiteVerification verificationService = new SiteVerification.Builder(
    GoogleNetHttpTransport.newTrustedTransport(),
    JacksonFactory.getDefaultInstance(),
    new HttpCredentialsAdapter(credentials)).build();

// Fetch the token
SiteVerificationWebResourceGettokenRequest request =
    new SiteVerificationWebResourceGettokenRequest()
        .setVerificationMethod("DNS_TXT")
        .setSite(new SiteVerificationWebResourceGettokenRequest.Site()
            .setType("INET_DOMAIN")
            .setIdentifier(CUSTOMER_DOMAIN));
SiteVerificationWebResourceGettokenResponse response =
    verificationService.webResource().getToken(request).execute();
String token = response.getToken();
System.out.println("Site Verification token: " + token);

Node.js

使用以下匯入作業:

const {google} = require('googleapis');

建立 API 用戶端並擷取憑證:

// Set up credentials with user impersonation
const authJWT = new JWT({
  keyFile: jsonKeyFile,
  scopes: ['https://www.googleapis.com/auth/siteverification'],
  subject: resellerAdminUser,
});

// Create the API service
const verificationService = google.siteVerification({version: 'v1', auth: authJWT});

// Fetch the token
const { data } = await verificationService.webResource.getToken({
  requestBody: {
    site: {
      type: 'INET_DOMAIN',
      identifier: customerDomain,
    },
    verificationMethod: 'DNS_TXT',
  }
});
const token = data.token;
console.log(`Site Verification token: ${token}`);

PHP

建立 API 用戶端並擷取憑證:

// Set up credentials with user impersonation
$client = new Google_Client();
$client->setAuthConfig($JSON_KEY_FILE);
$client->setSubject($RESELLER_ADMIN_USER);
$client->setScopes('https://www.googleapis.com/auth/siteverification');

// Create the API service
$verificationService = new Google_Service_SiteVerification($client);

// Fetch the token
$request = new Google_Service_SiteVerification_SiteVerificationWebResourceGettokenRequest([
  'verificationMethod' => 'DNS_TXT',
  'site' => [
    'type' => 'INET_DOMAIN',
    'identifier' => $CUSTOMER_DOMAIN
  ]
]);
$response = $verificationService->webResource->getToken($request);
$token = $response->token;
print 'Site Verification token: ' . $token . PHP_EOL;

Python

使用以下匯入作業:

from google.oauth2 import service_account
from apiclient.discovery import build

建立 API 用戶端並擷取憑證:

# Set up credentials with user impersonation
credentials = service_account.Credentials.from_service_account_file(
    JSON_KEY_FILE, scopes=["https://www.googleapis.com/auth/siteverification"])
credentials_delegated = credentials.with_subject(RESELLER_ADMIN_USER)

# Create the API service
verification_service = build(serviceName="siteVerification", version="v1",
    credentials=credentials_delegated)

# Fetch the token
response = verification_service.webResource().getToken(
  body={
    "site": {
      "type": "INET_DOMAIN",
      "identifier": CUSTOMER_DOMAIN
    },
    "verificationMethod": "DNS_TXT"
  }).execute()
token = response["token"]
print("Site Verification token: " + token)

步驟 3:放置驗證憑證

撰寫程式碼,將憑證新增為客戶網域的 DNS 記錄中的 TXT 記錄。

如果是新網域,您可以為 Gmail 設定必要的 MX 記錄

步驟 4:觸發網域驗證

將憑證新增為 TXT 記錄後,您可以呼叫 Site Verification API 來進行驗證。這是藉由呼叫 webResource.insert() 來實現的。

如果找不到預期的憑證,呼叫就會失敗並顯示 400 錯誤。您可以採用指數輪詢重試策略,直到呼叫成功完成 DNS 傳播延遲為止。

如果呼叫傳回沒有錯誤,Site Verification API 會確認要驗證網域,webResourceowners 欄位中的任何電子郵件都是已驗證的擁有者。

驗證狀態可能需要 3 小時的時間才會傳播到客戶的 Google 工作區帳戶中。您可以透過設定客戶的管理員 (在呼叫provisionCloudIdentity) 當做owner以下webResource,才能使用 Android 手機或平板電腦尋找另一部裝置。

C#

// Set the customer's admin user as an owner to make sure the domain
// verification status is instantly propagated to the Workspace account
string[] owners = { "admin@" + customerDomain };

var resource = new SiteVerificationWebResourceResource {
  Site = new SiteVerificationWebResourceResource.SiteData {
    Type = "INET_DOMAIN",
    Identifier = customerDomain
  },
  Owners = owners
};

resource = verificationService.WebResource.Insert(resource, "DNS_TXT").Execute();
Console.WriteLine("=== Domain has been verified");

Go

// Set the customer's admin user as an owner to make sure the domain
// verification status is instantly propagated to the Workspace account
resource := &siteverification.SiteVerificationWebResourceResource{
  Site: &siteverification.SiteVerificationWebResourceResourceSite{
    Type:       "INET_DOMAIN",
    Identifier: customerDomain,
  },
  Owners: []string{"admin@" + customerDomain},
}
resource, err := verificationService.WebResource.Insert("DNS_TXT", resource).Do()
if err != nil {
  fmt.Println(err)
} else {
  fmt.Println("=== Domain has been verified")
}

Java

// Set the customer's admin user as an owner to make sure the domain
// verification status is instantly propagated to the Workspace account
SiteVerificationWebResourceResource resource =
    new SiteVerificationWebResourceResource()
        .setSite(new SiteVerificationWebResourceResource.Site()
            .setIdentifier(CUSTOMER_DOMAIN)
            .setType("INET_DOMAIN"))
        .setOwners(Arrays.asList("admin@" + CUSTOMER_DOMAIN));

resource = verificationService.webResource().insert("DNS_TXT", resource).execute();
System.out.println("=== Domain has been verified");

Node.js

// Set the customer's admin user as an owner to make sure the domain
// verification status is instantly propagated to the Workspace account
await verificationService.webResource.insert({
  verificationMethod: 'DNS_TXT',
  requestBody: {
    site: {
      type: 'INET_DOMAIN',
      identifier: customerDomain,
    },
    owners: [`admin@${customerDomain}`],
  }
});
console.log('=== Domain has been verified');

PHP

// Set the customer's admin user as an owner to make sure the domain
// verification status is instantly propagated to the Workspace account
$resource = new Google_Service_SiteVerification_SiteVerificationWebResourceResource([
  'site' => [
    'type' => 'INET_DOMAIN',
    'identifier' => $CUSTOMER_DOMAIN,
  ],
  'owners' => ['admin@' . $CUSTOMER_DOMAIN]
]);

$resource = $verificationService->webResource->insert('DNS_TXT', $resource);
print '=== Domain has been verified' . PHP_EOL;

Python

# Set the customer's admin user as an owner to make sure the domain
# verification status is instantly propagated to the Workspace account
resource = verification_service.webResource().insert(
  verificationMethod="DNS_TXT",
  body={
    "site": {
      "type": "INET_DOMAIN",
      "identifier": CUSTOMER_DOMAIN
    },
    "owners": ["admin@" + CUSTOMER_DOMAIN]
  }).execute()
print("=== Domain has been verified")