Get hands-on experience with 20+ free Google Cloud products and $300 in free credit for new customers.

How to Call Google (Healthcare) API from Apigee Proxy with JWT as Bearer Access Token

Hello my fellow Apigeeans,

All of the FHIRFLY proxies have to call the Google Healthcare API to get data. To that end, the Google Healthcare API requires an Bearer access_token to be placed in the Authentication header. I have experimented with various method of obtaining said token. That said, I was most intrigued by this method, using a JWT signed with a X509 private key obtained for the Google Healthcare API service account as the Bearer token itself.

I feel like the policy is almost there, but I am getting an error back from the google:

{
 "error": {
 "code": 401,
 "message": "Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.",
 "status": "UNAUTHENTICATED"
 }
}

My JWT generator policy looks like this: (Note I have changed the audience to https://healthcare.googleapis.com/ with no luck)

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<GenerateJWT name="Generate-JWT-SelfSigned">
    <Algorithm>RS256</Algorithm>
    <PrivateKey>
        <Value ref="private.private_key"/>
    </PrivateKey>
    <Issuer>service-myproject@myproject.iam.gserviceaccount.com</Issuer>
    <Subject>service-myproject@myproject.iam.gserviceaccount.com</Subject>
    <Audience>https://accounts.google.com/</Audience>
    <ExpiresIn>240s</ExpiresIn>
    <OutputVariable>self_signed_jwt</OutputVariable>
</GenerateJWT>

The private key was issued to the Service account specified in the issuer and subject through GCloud AIM. I have setup other service account keys (for Apigee Dupal portal for instance)

I add the self_signed_jwt to the Authorization header in an Assign Message policy like so:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<AssignMessage continueOnError="false" enabled="true" name="AM-GOOGLE_TOKEN">
    <DisplayName>AM-GOOGLE_TOKEN</DisplayName>
    <Properties/>
    <Set>
        <Headers>
            <Header name="Authorization">Bearer {self_signed_jwt}</Header>
        </Headers>
    </Set>
    <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
    <AssignTo createNew="false" transport="http" type="request"/>
</AssignMessage>

and the relevant snippet of my flow in the main policy looks like this:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ProxyEndpoint name="default">
    <PreFlow name="PreFlow">
        <Request>
            <Step>
                <Name>FC-GET-GOOGLE-CLOUD-TOKEN</Name>
            </Step>
            <Step>
                <Name>AM-GOOGLE_TOKEN</Name>
            </Step>
        </Request>
        <Response/>
    </PreFlow>
    <Flows>
        <Flow name="ListEndpoint">
            <Description>Performs a Get operation at the Endpoint type level.</Description>
            <Request/>
            <Response/>
            <Condition>(proxy.pathsuffix MatchesPath "/Endpoint") and (request.verb = "GET")</Condition>
        </Flow>

I have done a trace and the jwt is there and it is set to the Authorization header correclty as shown it the attached image.

10921-header-and-jwt.png

Is it something with my JWT generator, or is it something else?

Thanks again for your support!

Richard Braman

FHIRFLY

Solved Solved
0 5 1,146
1 ACCEPTED SOLUTION

It's something else. The policy that you use to generate the self-signed JWT looks fine to me. I think there is some confusion on which token goes where.

From the doc page you listed, this diagram shows the flow:

10922-screenshot-20210401-093736.png

In English,

  1. a system must create (and sign) a JWT
  2. the system sends the JWT to Google's Token dispensary endpoint (https://oauth2.googleapis.com/token)
  3. That token endpoints responds with a Bearer token
  4. that bearer token can then be used when making request to other endpoints, for example healthcare.googleapis.com

There are details missing from this description, obviously. In particular, the request in step 2 "Send the JWT to the token dispensary endpoint", must be a POST, and it must have a particular form.

POST https://oauth2.googleapis.com/token
content-type:application/x-www-form-urlencoded

grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=JWT_HERE

The self-signed JWT gets sent as a FORM parameter. It is not a Bearer token, and in no case should you place the self-signed JWT into an Authorization header. I don't know of any system that will accept that self-signed JWT when passed in that way.

The self-signed JWT is a token, but is not a bearer token. It is a token that you use to request a bearer token. So you see there are two distinct tokens you must deal with. The first is used in the token-grant request. The response to the token-grant request includes another token (as it happens, it is NOT a JWT), which you can then use as a Bearer token (place it in the Authorization header)

Does this help?

View solution in original post

5 REPLIES 5