如何使用谷歌云功能验证服务帐户?

问题描述 投票:0回答:1

我一直在尝试设置谷歌云功能(带有http触发器的第二代),但似乎无法通过未经授权的401。我使用通过 gcloud cli 生成的身份令牌使其正常工作,但我需要使用服务帐户来避免身份令牌的 1 小时过期时间。

我已经在服务帐户上设置了云函数调用者和云运行调用者权限,并将服务帐户直接连接到云函数。不管我做什么,我都无法让它授权使用服务帐户。

我正在尝试使用 googleauth gem 从 Rails 应用程序进行身份验证。以下是我的服务。

# app/services/cloud_function_service.rb

class CloudFunctionService
  # Use the credentials and URL from Rails credentials or environment
  def self.api_request(function_type, body_data)
    new.api_request(function_type, body_data)
  end

  def api_request(function_type, body_data)
    access_token = fetch_access_token
    uri = Rails.application.credentials.CLOUD_FUNCTION_URL

    body = {
      'function_type': function_type,
      'body': body_data
    }.to_json

    headers = {
      'Authorization' => "Bearer #{access_token}",
      'Content-Type' => 'application/json'
    }

    # Make the POST request with headers and body
    result = HTTParty.post(uri, body: body, headers: headers)
    JSON.parse(result.body)
  rescue => e
    Rails.logger.error "Failed to call cloud function: #{e}"
    nil
  end

  private

  def fetch_access_token
    authorizer = Google::Auth::ServiceAccountCredentials.make_creds(
      json_key_io: File.open('./google-service-account-keys.json'),
      scope: 'https://www.googleapis.com/auth/cloud-platform'
    )
    response = authorizer.fetch_access_token!
    response['access_token']
  end
end

ruby-on-rails google-cloud-platform google-cloud-functions google-auth-library
1个回答
0
投票

在这里回答我自己的问题。实际上有两个问题可能对任何从 Ruby on Rails 应用程序调用云函数的人有所帮助。

首先,我误解了访问令牌与身份令牌的用法,但实际上您确实需要两者。需要访问令牌才能访问 google API 端点(云函数端点不被视为其中的一部分)。需要身份令牌才能访问云功能端点。但是...您使用访问令牌来获取身份令牌。

其次,与 Google Auth 的其他语言库不同,Ruby 库不提供获取身份令牌的方法。它仅提供获取访问令牌的方法。您需要为身份令牌端点构建自己的 api 处理程序。

基本上,流程如下:

  1. 使用 GoogleAuth 获取服务帐户的访问令牌。
  2. 在请求中提供访问令牌以获取身份令牌(因为您在此处点击 google api,因此您使用访问令牌来获取 id 令牌)
  3. 使用身份令牌作为身份验证标头中的承载令牌来访问您的云函数 URL。

这是我最终的有效代码!

class CloudFunctionService
  def self.api_request(function_type, body_data)
    new.api_request(function_type, body_data)
  end

  def api_request(function_type, body_data)
    access_token = fetch_access_token
    service_account_email = _your_gcp_service_account_email_
    target_audience = _your_cloud_function_uri_
    identity_token = fetch_identity_token(service_account_email, target_audience, access_token)
    uri = _your_cloud_function_uri_

    body = {
      # Your body data
    }.to_json

    headers = {
      'Authorization' => "Bearer #{identity_token}",
      'Content-Type' => 'application/json'
    }

    result = HTTParty.post(uri, body: body, headers: headers)
    JSON.parse(result.body)
  rescue => e
    Rails.logger.error "Failed to call cloud function: #{e}"
    nil
  end

  private

  def fetch_access_token
    decoded_json_key = Base64.decode64(_your_base64_encoded_json_key_file)
    json_key_io = StringIO.new(decoded_json_key)
    authorizer = Google::Auth::ServiceAccountCredentials.make_creds(
      json_key_io: json_key_io,
      scope: 'https://www.googleapis.com/auth/cloud-platform'
    )

    authorizer.fetch_access_token!['access_token']
  end

  def fetch_identity_token(service_account_email, target_audience, access_token)
    url = "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/#{service_account_email}:generateIdToken"
    body = {
      audience: target_audience,
      includeEmail: true
    }.to_json
    response = HTTParty.post(
      url,
      body: body,
      headers: {
        'Content-Type' => 'application/json',
        'Authorization' => "Bearer #{access_token}"
      }
    )
    if response.code == 200
      JSON.parse(response.body)['token']
    else
      raise "Failed to fetch identity token: #{response.body}"
    end
  end
end

© www.soinside.com 2019 - 2024. All rights reserved.