0
votes

This has been a source of frustration for the past couple of days because the documentation makes it sound like it should be straightforward.

I have a simple HTTP server running locally (Heroku eventually) that I want to use as a passthrough to upload a file to Firebase Cloud Storage. I have a credentials file and am able to successfully init the Firebase App and validate JWTs, but when I try to put an object, I always get an error.

Here is the code --

func main() {
   config := &firebase.Config{
      ProjectID:     "myproject-id",
      StorageBucket: "myproject-id.appspot.com",
   }
   credentialsJSON := os.Getenv("GOOGLE_CREDENTIALS")
   creds := google.Credentials{JSON: []byte(credentialsJSON)}
   opt := option.WithCredentials(&creds)

   app, err := firebase.NewApp(context.Background(), config,
    opt)

   if err != nil {
     log.Fatalln(err)
   }
   ctx, cancel := context.WithTimeout(context.Background(), time.Second*50)
   defer cancel()

   client, err := app.Storage(ctx)
   if err != nil {
     log.Fatalln(err)
   }
   fmt.Println(client)

   bucket, err := client.DefaultBucket()

   object := bucket.Object("foo.png")
   wc := object.NewWriter(ctx)

   r, err := os.Open("/Users/foo/test.png")
   defer r.Close()

   if _, err = io.Copy(wc, r); err != nil {
      log.Error(err)
   }

   if err := wc.Close(); err != nil {
     fmt.Println(err)
     log.Error(err)
   }
}

But this is what I get when I run this code --

 Post "https://storage.googleapis.com/upload/storage/v1/b/myproject-id.appspot.com/o?alt=json&name=foo.png&prettyPrint=false&projection=full&uploadType=multipart": oauth2: Transport's Source is nil

The Firebase Admin SDK documentation clearly states that the service account has full scope control on the storage account, but it seems like there is some mistake on bootstrapping the credentials correctly since the Transport token source is nil.

1

1 Answers

1
votes

If you're going to directly initialize the google.Credentials struct, then you must specify its TokenSource field. Specifying JSON alone is not sufficient. See how the google.golang.org/api/transport package is implemented:

https://github.com/googleapis/google-api-go-client/blob/144dbf001968463675e3221ab95bce7a93039a28/transport/http/dial.go#L81-L98

Notice how it only cares about creds.TokenSource.

The more common way to pass credentials to the Admin SDK is via option.WithCredentialsJSON(p []byte).