Google-api-dotnet-client: Add support for Service Account authentication with JSON Key File

Created on 14 Apr 2015  路  17Comments  路  Source: googleapis/google-api-dotnet-client

Service account authentication can be done directly with .p12 files or with 
JSON Key files.

Currently, the .NET library only supports .p12 files.   This feature requests 
is to add support for JSON key files

see
https://cloud.google.com/storage/docs/authentication#generating-a-private-key


Some additional references for JSON key files in java and go libraries:

https://code.google.com/p/google-api-java-client/source/browse/google-api-client
/src/main/java/com/google/api/client/googleapis/auth/oauth2/GoogleCredential.jav
a#776
https://github.com/golang/oauth2/issues/20
https://issues.jenkins-ci.org/browse/JENKINS-24571

cross reference with:  
https://code.google.com/p/google-api-dotnet-client/issues/detail?id=280


Original issue reported on code.google.com by [email protected] on 23 Feb 2015 at 7:18

Enhancement

Most helpful comment

The full source code should be like this

GoogleCredential credential;
using (var stream = new FileStream(fileName, FileMode.Open, FileAccess.Read))
{
    credential = GoogleCredential.FromStream(stream)
        .CreateScoped(VisionService.Scope.CloudPlatform);
}

var service = new VisionService(new BaseClientService.Initializer()
{
    HttpClientInitializer = credential,
    ApplicationName = "my-app-name",
});

All 17 comments

Actually, this has been added in 1.9.3 release a while ago.

Not sure if I'm missing something, but nowhere in the source code can I find any implementation of authorization for a Service Account using a JSON key file.

GoogleCredential.FromStream allows you to create service account credential from the JSON key.
https://github.com/google/google-api-dotnet-client/blob/master/Src/GoogleApis.Auth.DotNet4/OAuth2/GoogleCredential.cs#L87

At lower level, service account credential can be created from a PKCS-8 key, which is the private key format used in JSON keys:
https://github.com/google/google-api-dotnet-client/blob/master/Src/GoogleApis.Auth.DotNet4/OAuth2/ServiceAccountCredential.cs#L88

All the links referenced by @jtattermusch are dead and serve 404 errors. Is there any documentation on this?

The full source code should be like this

GoogleCredential credential;
using (var stream = new FileStream(fileName, FileMode.Open, FileAccess.Read))
{
    credential = GoogleCredential.FromStream(stream)
        .CreateScoped(VisionService.Scope.CloudPlatform);
}

var service = new VisionService(new BaseClientService.Initializer()
{
    HttpClientInitializer = credential,
    ApplicationName = "my-app-name",
});

How do you define a user to impersonate? For the Directory Service this is how I used to do it with a .p12 file:

var certificate = new X509Certificate2(HttpContext.Current.Server.MapPath(relativeKeypath), "notasecret", X509KeyStorageFlags.Exportable);

return new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
    User = user,
    Scopes = scopes
}.FromCertificate(certificate));

If I don't specify a User I get an Invalid Credentials [401] error.
How do I do this now?

@samofwise You can create new service account for this.

Go to IAM & Admin Service accounts, click Create service account and use path to created json files as fileName in my prev sample.

image
image

@sergey-tihon Thanks for your help (very descirptive), However I think you misunderstood me (sorry if I wasn't very clear.

I have done the above process and generated my json file and I am using your code from above
But the service account I am wanting to use is one with Domain Wide Delegation (I have already set it up)
Along with this I need to impersonate a particular user (ie [email protected]).

When using a p12 key i did this by setting the User property on the ServiceAccountCredential.Initializer
User = [email protected]
However with the new GoogleCredential class there is no option to do this.

So how do i set a user to impersonate with the new GoogleCredential class?

APIs and authentication are two separate things. Not all of the APIs for example support services accounts. The core library handles authentication and the classes for the specific api handles its own functionality and accept the authentication from the core library.

Was having the same problem. I could not find any easy way to read the credentials from a stream _and_ have the User set for the credential.

The API kept giving Bad Request 400, preconditionFailed.

I therefore solved it the dirty way - with reflection. It did solve my issue:

  ServiceAccountCredential credential;
  var scopes = ScopeConverter.Convert(context.Scopes);

  using (var stream = new FileStream(context.ServerSecretFilePath, FileMode.Open, FileAccess.Read))
  {
    credential = 
      GoogleCredential
        .FromStream(stream)
        .CreateScoped(scopes)
        .UnderlyingCredential as ServiceAccountCredential;
  }

  var userField = typeof(ServiceAccountCredential).GetField("user", BindingFlags.NonPublic | BindingFlags.Instance);
  userField?.SetValue(credential, "[email protected]");

  return credential;

@cldons I get an exception: Client is unauthorized to retrieve access tokens using this method.
Did you have to work around that? Or do I have something configured wrong?
Can we somehow make an official example on how to do this?

I guess that is more of an auth problem directly with the APIs and the user you are trying to use. Make sure that you configure correctly your auth settings, as described here (for instance):
https://stackoverflow.com/questions/42784640/client-is-unauthorized-to-retrieve-access-tokens-using-this-method-gmail-api-c-s

My solution was more focussed on how to achieve impersonation / setting a private field.

@eluchsinger Yes, this is more likely to be an problem with your auth configuration.
@cldons Just so you know, there is now a CreateWithUser() method to set the user on a service credential: https://github.com/google/google-api-dotnet-client/blob/master/Src/Support/Google.Apis.Auth/OAuth2/GoogleCredential.cs#L276

@eluchsinger which api are you trying to access? Not all APIs support service accounts.

Link to example json with service account @chrisdunelm if you have any improvements on that let me know

Was this page helpful?
0 / 5 - 0 ratings