Google-api-dotnet-client: GetAccessTokenForRequestAsync response token missing kid in header

Created on 9 Jun 2020  路  19Comments  路  Source: googleapis/google-api-dotnet-client

I have been converting code from Java version to .NET version.

Issue we ran into is that when we get a bearer token for API calls, the .NET version is missing the "kid" field in the header of the JWT token. Otherwise token is valid.

.NET :

_credentials = ServiceAccountCredential.FromServiceAccountData(File.OpenRead(serviceAccountFile));
_token = _credentials.GetAccessTokenForRequestAsync(_audience).Result;

Java :

ServiceAccountJwtAccessCredentials credentials = null;
credentials = ServiceAccountJwtAccessCredentials.fromStream( new FileInputStream( accountFile ),
        new URI( audience ) );
String token = credentials.getRequestMetadata( ).get( HttpHeaders.AUTHORIZATION )
        .get( 0 );

Tokens are otherwise identical and correct. However, the missing kid field is causing error. Both audience and account file are the same.

Are we doing something wrong here, is there a bug in the GetAccessTokenForRequestAsync function?

p2 bug

All 19 comments

I'll take a look at this as soon as I have a moment, but it might very well be early next week.

For extra details, the header of the JWT token with .NET version is:

{
  "alg": "RS256",
  "typ": "JWT"
}

while Java version gives:

{
  "alg": "RS256",
  "typ": "JWT",
  "kid": <value>
}

Dunno if that will help.

Can you add the exact error details? And the snippet of code where you get the error so we can reproduce it?

The error if HTTP response, 401 Unauthorized. Both codes attempt exact same thing.

Java version (which works):

//Request is a HttpPost variable. It comes as a parameter for the function
//bearerToken is acquired at the creation of the class, see the code in the OP
request.addHeader( HttpHeaders.AUTHORIZATION, this.bearerToken );
CloseableHttpClient httpclient = HttpClients.createDefault( );
CloseableHttpResponse response = httpclient.execute( request );

.NET version

//_client is a HttpClient that is created at the start of the program and persists.
//payload is a JsonObject passed as a variable to function 
//_token is acquired at the start of the program. See OP
//_host is the target URI
HttpContent content = new StringContent(payload.ToString(), Encoding.UTF8, "application/json");
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, _host);
request.Content = content;
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _token);
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
request.Headers.ConnectionClose = true;

HttpResponseMessage response = await _client.SendAsync(request);

Java version successfully compeltes the request, but .NET version gives 401 Unauthorized response. Both versions target the same endpoint.


<html><head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>401 Unauthorized</title>
</head>
<body text=#000000 bgcolor=#ffffff>
<h1>Error: Unauthorized</h1>
</body></html>

Payloads are identical in structure. Requests are made from the same network. So far, we have tracked the issue to missing section in the token header.

Thanks @Mansikka , I will take a look as soon as I can.

One last questions out of curiosity though. Are these calls you are making with and HttpClient to Google APIs? If so, I'm just curios why you are not using the client library for whatever Google API you are calling?

Yes, the end point is an app-engine that takes JSON payload to process. Reason why I am not using any specific API is because I do not have control over the other end and this is what I have been instructed to use.

Feel free to ask further clarifications if needed, I will respond to best of my capacity.

Thanks! I have enough info for now, as soon as I've taken a look I'll update here.

@Mansikka I'm working on this. Can you please let me know which Google API you are calling, I want to test with that specific one as well as others.

@amanda-tarafa I am not actually sure, this is an endpoint I was asked to call. However, I might be able to tell more next week when I get back in contact with the other end of the system, who have been handling cloud.

OK, I'll keep testing with other APIs, in any case, if you could peek the value of _host, that would be enough, something like
https://bigtableadmin.googleapis.com/.... would give me all the info I need.

Sure, I will take a peek on it next week.

Will downgrade to P2 now, I've fix the issue and have tests running. Just waiting to test against the original offending API.

Alright, so I asked about the API and I was told that it is a custom program which is done using Goole AppEngine, everything is done inside it. You need permissions for it separately in order to test it.

OK, then my tests with Google APIs should be equivalent in any case, they use the same mechanism as custom AppEngine apps use for authentication with a JWT.
I will submit a PR shortly then.

That is good to hear. Thank you for your efforts!

I'll update here when this change has been released, which will be later today.

This fix has now been published in https://www.nuget.org/packages/Google.Apis.Auth/1.47.0.

Thanks! I will report back tomorrow if there is an issue after I have updated everything and ran the tests.

Hi, I am happy to report that everything works. Thank you @amanda-tarafa for your help!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jamierytlewski picture jamierytlewski  路  3Comments

iwillspeak picture iwillspeak  路  7Comments

bijington picture bijington  路  5Comments

LindaLawton picture LindaLawton  路  9Comments

ganySA picture ganySA  路  5Comments