Google-cloud-dotnet: It's too difficult to create a client with anything but application Credentials.

Created on 23 Oct 2017  路  17Comments  路  Source: googleapis/google-cloud-dotnet

For all our APIs, it should be simple to create a client with

  1. Default application credentials. (implicitly)
  2. A path to .json credentials.
  3. Explicitly request Compute Engine credentials.

For an example, see the CloudLibrary class in https://github.com/GoogleCloudPlatform/dotnet-docs-samples/blob/master/auth/AuthSample/Program.cs.

In Pubsub, it was too difficult to create a client with a path to .json credentials:

 public static PublisherClient CreatePublisherWithServiceCredentials(string jsonPath)
        {
            GoogleCredential googleCredential = null;
            using (var jsonStream = new FileStream(jsonPath, FileMode.Open,
                FileAccess.Read, FileShare.Read))
            {
                googleCredential = GoogleCredential.FromStream(jsonStream)
                    .CreateScoped(PublisherClient.DefaultScopes);


            }

            return PublisherClient.Create(new Channel(PublisherClient.DefaultEndpoint.Host, PublisherClient.DefaultEndpoint.Port, googleCredential.ToChannelCredentials()));
        }
fixit-target p2 feature request

Most helpful comment

Note: Google.Cloud.Datastore.V1 2.2.0-beta02 now includes a DatastoreClientBuilder and a DatastoreDbBuilder to show how this can be done more generally.

All 17 comments

Well, the code can certainly be made simpler than that, to start with:

public static PublisherClient CreatePublisherWithServiceCredentials(string jsonPath)
{
    GoogleCredential googleCredential = GoogleCredential.FromFile(jsonPath)
        .CreateScoped(PublisherClient.DefaultScopes);
    Channel channel = new Channel(PublisherClient.DefaultEndpoint.ToString(), googleCredential.ToChannelCredentials());
    return PublisherClient.Create(channel);
}

(I don't know whether the scopes are actually needed for Pubsub - they seem to be for some APIs but not others; I have a standing action to investigate which need the scopes, and try to work out why.)

While I can understand that a PublisherClient.Create(GoogleCredential) might sound attractive, there are two issues with it:

  • It would hide the channel from the user, which means they wouldn't be able to dispose of it if they needed to. Maybe that's not so bad, in that they'd always be able to create the channel themselves if they did need to dispose of it later (or reuse for another client)
  • We don't want to use GoogleCredential (or anything else in Google.Apis.*) in the public API of any gRPC-based library. We'd like to replace Google.Apis.Auth and drop the whole dependency chain, which we wouldn't be able to do if it were in the public API.

We could potentially expose a Create(ChannelCredentials) method, which would end up with code of:

public static PublisherClient CreatePublisherWithServiceCredentials(string jsonPath)
{
    GoogleCredential googleCredential = GoogleCredential.FromFile(jsonPath)
        .CreateScoped(PublisherClient.DefaultScopes);
    return PublisherClient.Create(googleCredential.ToChannelCredentials());
}

Any thoughts about whether that's a sufficient improvement on the current situation that it's worth doing?

I agree removing scope would be nice. I was trying to work out a way of creating a method with generics which would allow me to return either a SubscriberClient or a PubliserClient.

I'm working on getting scopeless credentials fixed for gRPC clients. #1580 helps track how widespread the problem is. @LindaLawton the good news is that it seems to work for PubSub, so at least there you wouldn't have a problem.

I beginning to wonder whether a simple CreateFromCredentialsFile(string file, FooSettings = null) method is the simplest fix here. I assume that having a file is the most common situation here.

@SurferJeffAtGoogle: Any feedback on whether CreateFromCredentialsFile would solve a sufficiently larger proportion of cases to be worth doing? On the one hand, I don't generally like very specific ways of doing something, preferring to compose more general operations. On the other hand, if it makes the most common non-default credentials story a one step operation, that's great... and it does avoid issues with exposing Google.Apis.* types in the public API.

Personally i think its worth doing for anyone who will be using this like me via a windows service, console app or windows application. You cant be 100% that people will only use this hosting on Google cloud servers.

GOOGLE_APPLICATION_CREDENTIALS doesn't work for me either due to the fact that there could be more then one running on the same server to different projects. Unless of course there is a way to supply a different name to the library.

It would hide the channel from the user, which means they wouldn't be able to dispose of it if they needed to. Maybe that's not so bad, in that they'd always be able to create the channel themselves if they did need to dispose of it later (or reuse for another client)

Exactly how bad is it? How costly is it to use two channels for two clients instead of one? I've never heard a user say anything, positive or negative, about channels. They don't want to learn about channels just to publish a message. It's an implementation detail. The more we can hide it from them, the better.

We don't want to use GoogleCredential (or anything else in Google.Apis.*) in the public API of any gRPC-based library. We'd like to replace Google.Apis.Auth and drop the whole dependency chain, which we wouldn't be able to do if it were in the public API.

Is there a way (or proposed way) to pass a json credentials path to PublisherClient.Create() without referencing Google.Apis.Auth?

By removing the dependency from the PublisherClient API to Google.Apis.Auth, we're removing a really useful trail of breadcrumbs that the user can use to discover other ways of authenticating. And feedback that I've heard from users leads me to believe that passing a path to the json credentials file is a really common way to authenticate. It should be easy, and easy to discover.

How bad is it?

Not disposing of channels isn't usually a problem, but you wouldn't want to create a new channel on every request. PublisherClient.Create() will use a shared channel for all clients created that way. If you're using custom credentials, it would be best to create a single client instance.

While it would be nice to be able to hide all this away as an implementation detail, that's not always feasible - and I don't think it's feasible in this case. While many users could ignore it, that could lead to poor performance in some cases, and there may well be some users who really need to dispose of the channel.

Is there a way (or proposed way) to pass a json credentials path to PublisherClient.Create() without referencing Google.Apis.Auth?

I'd be proposing a method (probably with a new name) that takes a string - the path to the file - just like GoogleCredential.FromFile does.

While it would be useful right now to accept a GoogleCredential, that would bake the dependency on Google.Apis.Auth in forever, and we really, really don't want to do that. When the new auth library is ready, we can start generating methods that accept whatever abstraction that exposes. We've made some progress on that, but I don't expect it to be ready for a while. Meanwhile, we're caught in limbo - which is why I see accepting a file as a reasonable workaround. It has low long-term costs (it's a method that will become less useful later, but do no harm) but significant benefit.

I'm having trouble imagining the near and distant future auth.

In this pull request:
https://github.com/GoogleCloudPlatform/dotnet-docs-samples/pull/417
I wrote code that represents, ideally, what I think auth code should look like. Of course the code does not compile.

Other DPEs disagree with some points anyway, so don't spend too much time critiquing that code.

But to help me understand the future of auth as you see it, could you prepare a similar PR?

For the near future, imagine:

var client = PublisherClient.CreateWithCredentialsFromFile("foo.json");

... where CreateWithCredentialsFromFile is certainly a name that needs discussing.

We're not at a stage where we're ready to create a PR showing longer-term options yet, but I suspect that discussion would be better in a design doc anyway.

I am using Google.Cloud.PubSub.V1 the latest stable release. where PublisherClient.Create method does not allow to pass any credentials. How can I inject google credentials here?

@Prachi7 Use PublisherClient.CreateAsync(...) and set the ChannelCredentials in the ClientCreationSettings. To get a ChannelCredentials instance, see the first FAQ entry.

@chrisdunelm Thanks a lot!!! It worked :)

Note: Google.Cloud.Datastore.V1 2.2.0-beta02 now includes a DatastoreClientBuilder and a DatastoreDbBuilder to show how this can be done more generally.

Renamed as PubSub is now somewhat different to other APIs, and for most uses custom credentials are reasonably easy to use. This issue is now focused on the more general problem (which client builders aim to address).

Client builders are now merged; still to do:

  • Releases (starting with some betas, waiting a while)
  • Updating the documentation

FAQ is now updated.

Closing this now, as the releases will just go out over time.

Was this page helpful?
0 / 5 - 0 ratings