The Will Will Web

記載著 Will 在網路世界的學習心得與技術分享

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

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

image

TL;DR

廢話不多說,直接看解答:

  1. 先安裝 Google.Apis.Auth 套件

    dotnet add package Google.Apis.Auth
    
  2. 透過以下程式碼就可以輕鬆獲取 Access Token

    你要先從 GCP Console 取得「服務帳戶」的 KEY 檔案 (JSON),然後透過以下程式碼就可以輕鬆獲取 Access Token:

    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 套件來存取的服務相當多,清單如下:

如果遇到不支援的 API,也可以透過 Google OAuth2 API Client Library for .NET 來取得 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 applicationInstalled applicationService 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 的官方文件。

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

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

  1. 啟用 API 服務

    開啟 Google Cloud console 首頁

    選取 Project 專案

    選取 Project 專案

    點擊 + ENABLE APIS AND SERVICES 按鈕

    搜尋你要執行的 API 服務,例如 Vertex AI API,然後點擊 ENABLE 按鈕

    有些服務一定要啟用 Billing 綁定才能使用,例如 Vertex AI API 就是這樣。你要按下 ENABLE BILLING 才能開始使用。

  2. 建立服務帳戶

    第二步驟則是你需要一個「身分」來呼叫 API,這個身分就是「使用者帳戶」或「服務帳戶」。

    使用者帳戶又稱 User PrincipalUser Account

    服務帳戶又稱 Service PrincipalService Account

    你可以在 IAM & Admin 頁面中建立服務帳戶

    選擇完專案後,按下 + CREATE SERVICE ACCOUNT 按鈕,然後填入服務帳戶的名稱,然後按下 DONE 按鈕。

    建立完成後,你會得到一個 Email,這個不能用的 Email 其實就是你的服務帳戶名稱

    點擊剛剛建立的服務帳戶,然後切換到 KEYS 頁籤,點擊 ADD KEY 下拉選單,點擊 Create new key 按鈕,在 Key type 欄位選擇 JSON,按下 CREATE 按鈕即可下載一個 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 頁面,點擊 GRANT ACCESS 按鈕

    Add principals 的地方輸入你的服務帳戶名稱,也就是你服務帳戶名稱Email 地址,然後在 Assign roles 選擇你要授權的內建角色,然後按下 SAVE 按鈕。

    這裡可以選擇的角色非常多,基本上你是不會知道要設定什麼的!你必須到個別 Google APIs 查詢有哪些存取控制的內建角色,以及這些角色擁有哪些權限。我以 Vertex AI access control with IAM 為例,這邊就列出了許多 RolePermissions 的對應清單, 挑選正確的 Role 即可。

    相信我,剛開始陷入選擇障礙的黑水溝是正常的,我也不知道該怎樣救你!😂

    GCP 平台非常大,所有服務的權限(Permission)加起來有數萬條,有人整理了一個 Permissions Reference for Google Cloud IAM 網站,幫你整理了「所有」服務的 Permission 權限清單,還提供全文檢索服務,真的超讚!😍

    不過,你也可以不授權,直接呼叫 API 看看,呼叫 API 的過程自然會跟你說缺什麼權限!👍

    以下列 Vertex AI Gemini API 的回應內容來說,這個服務帳戶缺少 aiplatform.endpoints.predict 權限,而從 Vertex AI access control with IAM 可以查到 Vertex AI User 角色擁有這個權限,因此你可以選擇這個角色。

    {
      "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 查詢你要存取的 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 帳號的電子郵件地址

相關連結

留言評論