3
votes

I put together what I thought was the hello-world of firestore Java client auth, but it consistently dies with

WARNING: Your application has authenticated using end user credentials from Google Cloud SDK. We recommend that most server applications use service accounts instead. If your application continues to use end user credentials from Cloud SDK, you might receive a "quota exceeded" or "API not enabled" error. For more information about service accounts, see https://cloud.google.com/docs/authentication/. Credentials: ServiceAccountCredentials{clientId=104986335035620796437, [email protected], privateKeyId=8e...11, transportFactoryClassName=com.google.auth.oauth2.OAuth2Utils$DefaultHttpTransportFactory, tokenServerUri=https://oauth2.googleapis.com/token, scopes=[], serviceAccountUser=null} Exception in thread "main" java.util.concurrent.ExecutionException: com.google.api.gax.rpc.UnavailableException: io.grpc.StatusRuntimeException: UNAVAILABLE: Credentials failed to obtain metadata

"Credentials failed to obtain metadata" doesn't explain it - is my service account JSON wrong? Why does it still give me that warning, and are they related?

val firestoreOptions = FirestoreOptions.getDefaultInstance().toBuilder()
        .setProjectId("nextbot3")
        .setCredentials(GoogleCredentials.fromStream(ClassLoader.getSystemClassLoader().getResourceAsStream("serviceAccountKey.json")))
        .setDatabaseId("nextbot3")
        .setTimestampsInSnapshotsEnabled(true)
        .build()
println("Credentials: " + firestoreOptions.credentials)
val db = firestoreOptions.service!!

val docRef = db.collection("users").document("a-user").collection("devices").document("a-bot")
println(docRef.set(mapOf("hello" to "world")).get())

my serviceAccountKey.json is straight from what I downloaded from the cloud (not firebase) project admin page:

{
  "type": "service_account",
  "project_id": "nextbot3",
  "private_key_id": "8edf2b2607309e5da929109550090a5818cd8511",
  "private_key": "-----BEGIN PRIVATE KEY--...--END PRIVATE KEY-----\n",
  "client_email": "[email protected]",
  "client_id": "104986335035620796437",
  "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/junkbot%40nextbot3.iam.gserviceaccount.com"
}
1
What's firestoreOptions.service!!? I don't see that listed as a method on FirestoreOptions. - Doug Stevenson
Kotlin shortcut, it means firestoreOptions.getService() and check that the result isn't null. - Benjamin H
Click the link I provided in my comment to the documentation. There's no getService method on FirestoreOptions that I can see. - Doug Stevenson
I have a hunch that this is because you create FirestoreOptions via the default instance. That builder has an ADC provider that gets precedence over the GoogleCredentials you have specified. Try FirestoreOptions.newBuilder() instead. - Hiranya Jayathilaka

1 Answers

2
votes

When we create FirestoreOptions via the default instance (i.e. FirestoreOptions.getDefaultInstance().toBuilder()), we get a Builder configured with Application Default Credentials. Those credentials get precedence over the service account you've specified. Use FirestoreOptions.newBuilder() to avoid this situation.