0

This question is similar to How can I authorize a Google Service Account without the default credentials file? but specifically about how to use Rails' built in secrets/credentials functionality.

I realise it needs to be JSON but when I store credentials in a private method and pass that method to json_key_io I get a NoMethodError:

def call
  auth = Google::Auth::ServiceAccountCredentials.make_creds(
    json_key_io: credentials,
    scope: Google::Apis::ClassroomV1::AUTH_CLASSROOM_ROSTERS
  )
end

private

  def credentials
    @credentials ||= {
      type: "service_account",
      project_id: Rails.application.credentials.google[:project_id],
      private_key_id: Rails.application.credentials.google[:private_key_id],
      private_key: Rails.application.credentials.google[:private_key],
      client_email: Rails.application.credentials.google[:client_email],
      client_id: Rails.application.credentials.google[:client_id],
      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: Rails.application.credentials.google[:client_x509_cert_url],
    }.to_json
  end

NoMethodError (undefined method ``read' for "{\"type\":\"service_acco...

Matthew
  • 1,300
  • 12
  • 30

1 Answers1

1

The key thing I was missing (as pointed out by miggitymac in this answer) is that the client is expecting an IO object (a file), not a string.

With the addition of StringIO.new, this now works as expected:

def call
  auth = Google::Auth::ServiceAccountCredentials.make_creds(
    json_key_io: StringIO.new(credentials),
    scope: Google::Apis::ClassroomV1::AUTH_CLASSROOM_ROSTERS
  )
end

private

  def credentials
    @credentials ||= {
      type: "service_account",
      project_id: Rails.application.credentials.google[:project_id],
      private_key_id: Rails.application.credentials.google[:private_key_id],
      private_key: Rails.application.credentials.google[:private_key],
      client_email: Rails.application.credentials.google[:client_email],
      client_id: Rails.application.credentials.google[:client_id],
      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: Rails.application.credentials.google[:client_x509_cert_url],
    }.to_json
  end
Matthew
  • 1,300
  • 12
  • 30
  • 2
    I like to create a helper method, so I'm not calling `Rails.application.credentials` over and over. something like: `def google_creds(key); Rails.application.credentials[key.to_sym]; end` – Int'l Man Of Coding Mystery Mar 30 '21 at 13:13
  • 1
    @Int'lManOfCodingMystery just using a local variable inside the method - `creds = Rails.application.credentials.google` would do the job. – max Mar 30 '21 at 21:04
  • Ya, I suggested the method in case it needs to be accessed anywhere else, but ya the variable would be better if only used here – Int'l Man Of Coding Mystery Mar 30 '21 at 21:44