﻿# 如何透過 .NET 的 Google.Apis.Auth 套件來取得服務帳戶的 Access Token

為了要能夠呼叫 [Google Cloud Platform](https://cloud.google.com/?hl=en) (`GCP`) 上的 API 資源，我們通常要透過 OAuth 2.0 的方式來取得存取權杖 (Access Token)。其實 Google 對 .NET 的支援還算完整，只是文件寫的太差，為了要能成功串好這個 OAuth 流程，花了我不少時間，主要是沒辦法找到一個清楚且完整的範例程式。也因為網路上可用的範例程式太少，導致各大 LLM 平台的回答都充滿了幻覺，每個都在胡說八道。這篇文章我就分享「服務帳戶」如何取得 Access Token 的方法，並提供一個完整可用的範例程式。

![image](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/c6ac957b47257790a80f-4a8f7ee4-dfa7-4dac-97d5-e7e0539e966f.webp)

### TL;DR

廢話不多說，直接看解答：

1.  先安裝 [Google.Apis.Auth](https://www.nuget.org/packages/Google.Apis.Auth) 套件
    
    ```bash
    dotnet add package Google.Apis.Auth
    ```
    
2.  透過以下程式碼就可以輕鬆獲取 Access Token
    
    你要先從 GCP Console 取得「服務帳戶」的 `KEY` 檔案 (`JSON`)，然後透過以下程式碼就可以輕鬆獲取 Access Token：
    
    ```csharp
    async Task<string> GetAccessToken(string path)
    {
        // 取得 GCP 的應用程式 Credentials 有兩種方法
    
        // Method 1: 透過 JSON 金鑰檔案來取得
        var cred = await GoogleCredential.FromFileAsync(path, QueryCancelToken);
    
        // Method 2: 透過 GOOGLE_APPLICATION_CREDENTIAL 環境變數來取得
        // 該環境變數必須指向你的 JSON 金鑰檔案 (使用 Docker 時會採用這種方式)
        // 這種用法我們又稱為 Application Default Credentials (ADC)
        // var cred = await GoogleCredential.GetApplicationDefaultAsync(QueryCancelToken);
    
        if (cred.IsCreateScopedRequired)
        {
            cred = cred.CreateScoped(new[]
                { "https://www.googleapis.com/auth/cloud-platform" }
            );
        }
    
        string accessToken
            = await cred.UnderlyingCredential.GetAccessTokenForRequestAsync();
    
        return accessToken;
    }
    ```
    

很簡單對吧！對，但你就是找不到有人這樣寫！😒

> 這讓我懷疑是不是所有寫 .NET 的人都沒在用 GCP！😂

### 觀念解析

首先，要透過 .NET 存取 Google 的 APIs 服務， 可以透過 NuGet 套件來存取的服務相當多，清單如下：

-   [Supported Google APIs](https://developers.google.com/api-client-library/dotnet/apis)

如果遇到不支援的 API，也可以透過 [Google OAuth2 API Client Library for .NET](https://developers.google.com/api-client-library/dotnet/apis/oauth2/v2) 來取得 Access Token，你再自己呼叫 REST API 即可，所有的 Google APIs 都有提供 API 服務。而這組 OAuth2 的 .NET APIs 就被整理在 `Google.Apis.Auth` 套件中！

基本上，依據不同的 API 服務，就有不同的認證與授權方法：

1.  簡單的 API 存取 (`API keys`)
    
    這種 API 呼叫不會存取任何使用者資料，就是單純的呼叫 API 並取得結果而已。
    
    例如 Google AI Gemini API 就是用這種方式，只要有 API key 就能呼叫 API。
    
2.  已授權的 API 存取 (OAuth 2.0)
    
    這部分包含了 `Web application`、`Installed application` 與 `Service Account` 這三種存取方式。
    
    `Web application` 用來提供有 UI 互動介面的網頁，讓使用者可以授權應用程式存取他們在 Google 上的資料。
    
    `Installed application` 又稱 `Mobile & Desktop Apps`，用來提供給沒有 Web 介面的應用程式，例如桌面應用程式或行動裝置應用程式。這種應用程式一定要能夠開啟系統預設的瀏覽器進行授權。
    
    `Service Account` 則是透過「服務帳戶」來存取 Google APIs，這種方式適合用在沒有 UI 介面的後端應用程式，例如排程應用程式或 LINE Bot 機器人之類的後端服務。這種應用程式是在 GCP 中建立一個「服務主體」(Service Principal)，並透過這個主體來存取 Google APIs。
    
    > OAuth 2.0 是一個蠻大的議題，這裡不打算深入討論，有興趣的讀者可以參考 [OAuth 2.0](https://oauth.net/2/) 的官方文件。
    

接下來我就介紹範例程式比較難找到的 `Service Account` 來做說明。

基本上，要能成功利用 `Service Account` 來存取 Google APIs，你需要以下幾個步驟：

1.  啟用 API 服務
    
    開啟 [Google Cloud console](https://console.cloud.google.com/) 首頁
    
    選取 `Project` 專案
    
    ![選取 Project 專案](https://stwillblogassets.blob.core.windows.net/files/images/external/github.com/209ae63aab2aa3ae5a55-1fa529ab-be9d-4b91-9854-1014499007dd.jpg)
    
    點擊 `+ ENABLE APIS AND SERVICES` 按鈕
    
    搜尋你要執行的 API 服務，例如 `Vertex AI API`，然後點擊 `ENABLE` 按鈕
    
    > 有些服務一定要啟用 Billing 綁定才能使用，例如 `Vertex AI API` 就是這樣。你要按下 `ENABLE BILLING` 才能開始使用。
    
2.  建立服務帳戶
    
    第二步驟則是你需要一個「身分」來呼叫 API，這個身分就是「使用者帳戶」或「服務帳戶」。
    
    **使用者帳戶**又稱 `User Principal` 或 `User Account`。
    
    **服務帳戶**又稱 `Service Principal` 或 `Service Account`。
    
    你可以在 `IAM & Admin` 頁面中[建立服務帳戶](https://console.cloud.google.com/iam-admin/serviceaccounts)。
    
    選擇完專案後，按下 `+ CREATE SERVICE ACCOUNT` 按鈕，然後填入服務帳戶的名稱，然後按下 `DONE` 按鈕。
    
    建立完成後，你會得到一個 `Email`，這個不能用的 `Email` 其實就是你的**服務帳戶名稱**！
    
    點擊剛剛建立的服務帳戶，然後切換到 `KEYS` 頁籤，點擊 `ADD KEY` 下拉選單，點擊 `Create new key` 按鈕，在 `Key type` 欄位選擇 `JSON`，按下 `CREATE` 按鈕即可下載一個 JSON 格式的金鑰檔。內容大概長這樣：
    
    ```json
    {
      "type": "service_account",
      "project_id": "will-340916",
      "private_key_id": "139aabaad33db2a9e87d39fb51c3133893557b44",
      "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvwI...eJDQ==\n-----END PRIVATE KEY-----\n",
      "client_email": "testsa@will-340916.iam.gserviceaccount.com",
      "client_id": "106288110503118499253",
      "auth_uri": "https://accounts.google.com/o/oauth2/auth",
      "token_uri": "https://oauth2.googleapis.com/token",
      "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
      "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/testsa%40will-340916.iam.gserviceaccount.com",
      "universe_domain": "googleapis.com"
    }
    ```
    
    > 我在上面提供的範例程式中，在 `GetAccessToken` 方法傳入的 `path` 參數，就是要指定這個下載的 JSON 檔案！
    
3.  授權給這個服務帳戶
    
    目前你只是建立了一個「身分」，但這個身分還沒有任何權限，所以你什麼事都不能做，還需要透過 `IAM` 頁面來授權這個身分的權限。
    
    先到 [IAM](https://console.cloud.google.com/iam-admin/iam) 頁面，點擊 `GRANT ACCESS` 按鈕
    
    在 `Add principals` 的地方輸入你的**服務帳戶名稱**，也就是你**服務帳戶名稱**的 `Email` 地址，然後在 `Assign roles` 選擇你要授權的內建角色，然後按下 `SAVE` 按鈕。
    
    > 這裡可以選擇的角色非常多，基本上你是不會知道要設定什麼的！你必須到個別 Google APIs 查詢有哪些存取控制的內建角色，以及這些角色擁有哪些權限。我以 [Vertex AI access control with IAM](https://cloud.google.com/vertex-ai/docs/general/access-control) 為例，這邊就列出了許多 `Role` 與 `Permissions` 的對應清單， 挑選正確的 `Role` 即可。
    
    相信我，剛開始陷入選擇障礙的黑水溝是正常的，我也不知道該怎樣救你！😂
    
    > GCP 平台非常大，所有服務的權限(`Permission`)加起來有數萬條，有人整理了一個 [Permissions Reference for Google Cloud IAM](https://gcp.permissions.cloud/) 網站，幫你整理了「所有」服務的 `Permission` 權限清單，還提供全文檢索服務，真的超讚！😍
    
    不過，你也可以不授權，直接呼叫 API 看看，呼叫 API 的過程自然會跟你說缺什麼權限！👍
    
    以下列 Vertex AI Gemini API 的回應內容來說，這個服務帳戶缺少 `aiplatform.endpoints.predict` 權限，而從 [Vertex AI access control with IAM](https://cloud.google.com/vertex-ai/docs/general/access-control) 可以查到 `Vertex AI User` 角色擁有這個權限，因此你可以選擇這個角色。
    
    ```json
    {
      "error": {
        "code": 403,
        "message": "Permission 'aiplatform.endpoints.predict' denied on resource '//aiplatform.googleapis.com/projects/vertex-ai-gemini-api-417217/locations/us-central1/publishers/google/models/gemini-1.0-pro' (or it may not exist).",
        "status": "PERMISSION_DENIED",
        "details": [
          {
            "@type": "type.googleapis.com/google.rpc.ErrorInfo",
            "reason": "IAM_PERMISSION_DENIED",
            "domain": "aiplatform.googleapis.com",
            "metadata": {
              "resource": "projects/vertex-ai-gemini-api-417217/locations/us-central1/publishers/google/models/gemini-1.0-pro",
              "permission": "aiplatform.endpoints.predict"
            }
          }
        ]
      }
    }
    ```
    
    注意: 剛設定授權與移除授權都需要等待 1 ~ 5 分鐘才會生效，建議去喝杯咖啡再回來測試。
    

再來，就是 OAuth 2.0 在取得 Access Token 時，有一個 `scope` 的概念，這個 `scope` 就是你要存取的 API 服務的權限範圍，你必須在程式碼中指定這個 `scope`，這樣你才能取得正確的 Access Token。不過，不是每一個 Google 的 API 服務都需要設定 `scope`，有些 API 服務是不需要設定 `scope` 的。但若需要設定 `scope` 的話，你可以到 [OAuth 2.0 Scopes for Google APIs](https://developers.google.com/identity/protocols/oauth2/scopes) 查詢你要存取的 API 服務的 `scope`。

老實說，這一頁也是會讓人陷入選擇障礙的泥沼！😂

先講一個最常見的兩個 Scopes，不知道怎麼選的，用第一個即可:

1.  `https://www.googleapis.com/auth/cloud-platform`
    
    檢視、修改、設定和刪除您的 Google Cloud 資料，並查看您的 Google 帳號的電子郵件地址。
    
2.  `https://www.googleapis.com/auth/cloud-platform.read-only`
    
    查看您在 Google Cloud 服務中的資料並查看您的 Google 帳號的電子郵件地址
    

### 相關連結

-   Google for Developers
    -   [Get Started | API Client Library for .NET](https://developers.google.com/api-client-library/dotnet/get_started)
    -   [Class GoogleCredential (1.60.0) | .NET client library | Google Cloud](https://cloud.google.com/dotnet/docs/reference/Google.Apis/latest/Google.Apis.Auth.OAuth2.GoogleCredential?fbclid=IwAR2Jrx1RuQfztEmqXJEstBTGQPKR-8XhaRHLY0Qyx-1qFKJ932rVjhurBkU#Google_Apis_Auth_OAuth2_GoogleCredential_GetApplicationDefaultAsync_System_Threading_CancellationToken_)
-   Google APIs Client Library for .NET
    -   [API Client Library for .NET](https://developers.google.com/api-client-library/dotnet)
    -   [GitHub - googleapis/google-api-dotnet-client](https://github.com/googleapis/google-api-dotnet-client)
-   Vertex AI Gemini API
    -   [Overview of Generative AI on Vertex AI](https://cloud.google.com/vertex-ai/generative-ai/docs/learn/overview)
    -   [Try the Vertex AI Gemini API](https://cloud.google.com/vertex-ai/generative-ai/docs/start/quickstarts/quickstart-multimodal)
