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

How to use service callout for google access token

I am designing a proxy for this API because the API has this Authorization:

curl -X POST -H "Authorization: Bearer $(gcloud auth print-access-token)" \
-H "Content-Type: application/json"

I set service callout、Extract token、AssingMessage to achieve the goal。But I keep getting the 401 message of token error.("message": "Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.")
Is there any wrong setting in my service callout?

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ServiceCallout async="false" continueOnError="false" enabled="true" name="SC-auth">
  <DisplayName>SC-auth</DisplayName>
  <Request>
    <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
    <Set>
      <Headers>
        <Header name="Content-Type">application.json</Header>
        <Header name="Authorization">bearer</Header>
      </Headers>
      <FormParams>
        <FormParam name="grant_type">refresh_token</FormParam>
        <FormParam name="refresh_token">{request.formparam.a}</FormParam>
      </FormParams>
    </Set>
  </Request>
  <Response>accessTokenResponse</Response>
  <Timeout>30000</Timeout>
  <HTTPTargetConnection>
    <Authentication>
      <HeaderName ref="Authorization">STRING</HeaderName>
      <GoogleAccessToken>
        <Scopes>
        </Scopes>
      </GoogleAccessToken>
    </Authentication>
  </HTTPTargetConnection>
</ServiceCallout>
0 3 282
3 REPLIES 3

It looks to me that there is some confusion here. Let me try to help. 

1st, your service callout is mis-configured. 

This part: 

<ServiceCallout async="false" continueOnError="false" enabled="true" name="SC-auth">
  <Request>
    <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
    <Set>
      <Headers>
        <Header name="Content-Type">application.json</Header>
        <Header name="Authorization">bearer</Header>
      </Headers>
      <FormParams>...</FormParams>
    </Set>
  </Request>

...has two problems. First, if you are sending JSON, the content-type header should probably be "application/json" (with a slash) not application.json"  (with a dot).  Then you are using FormParams, which implies... you ARE NOT sending JSON.  So those are in conflict. Either send JSON with the application/JSON content-type, or send form params with the application/x-www-form-urlencoded content-type. Choose one. 

But that inconsistency is not why you are seeing an Authentication error.  There is a second problem here - this section is setting the Authorization header to "bearer".  That's not correct.  An Authorization header should be "Bearer TOKEN" where TOKEN is replaced by an actual access token.  So I think you need to remove or modify that configuration line.  Probably, remove it, because...

In a later section it appears that you are using the Authentication tag.  

  <HTTPTargetConnection>
    <URL>https://www.googleapis.com/auth/cloud-platform</URL>
    <Authentication>
      <HeaderName ref="Authorization">STRING</HeaderName>
      <GoogleAccessToken>
        <Scopes>
          <Scope>https://www.googleapis.com/auth/cloud-platform</Scope>
        </Scopes>
      </GoogleAccessToken>
    </Authentication>
  </HTTPTargetConnection>

There are multiple problems here too.  First you are using URL = https://www.googleapis.com/auth/cloud-platform  That's not a URL you will connect with.   What is your goal?  Normally you would use something like bigquery.googleapis.com to reach BQ, or storage.googleapis.com to reach Google Cloud Storage, or secretmanager.googleapis.com to reach the secret manager, etc.  The  URL https://www.googleapis.com/auth/cloud-platform doesn't return anything, except the phrase "cloud platform".  It's not a real service.  So you need to figure out what service you're trying to invoke. 

Next, within the Authentication tag, you are using 

      <HeaderName ref="Authorization">STRING</HeaderName>

That is also wrong. The ref should refer to the name of a context variable. "Authorization" is not the name of a context variable, probably.  Also, you probably don't want to the HeaderName element here at all. 

The purpose of the Authentication tag here is to tell Apigee "go get a token, and inject it into the Authorization header for me as a bearer token, before making this ServiceCallout request."  The HeaderName element allows you to modify the name of the header into which the bearer token will be inserted.  So if you don't want to use the Authorization header for some reason , the HeaderName element allows you to override it.  You probably don't want that, so omit that element here. 

And if the Authentication tag tells Apigee to inject a header, then you do not need the Set/Headers/Header/@name=Authorization in the prior section. 

Last,  you are invoking your API Proxy with 

curl -X POST -H "Authorization: Bearer $(gcloud auth print-access-token)" ...

...which effectively is passing YOUR token into the proxy.  I am not sure why you would do that, you haven't explained. 

You probably do not want that. 

It seems like you are hacking around, without a lot of understanding of what you're doing. 

What is your goal here?  Can you explain what you are trying to accomplish?

 

Thank you very much for your reply, I really need to understand the meaning of each line of instructions.

Currently I want to use apigee to manage various APIs

One of the APIs is an APP built by the agent builder on GCP, which provides an API for integration calls. In front of this API, we need to obtain the verification token first, so I think we should make a service callout in APIGEE, and then extract the token and assign it back. But I really don't have a clear understanding of how service callout is used.

 


@RitaKuo wrote:

we need to obtain the verification token first, so I think we should make a service callout in APIGEE, and then extract the token and assign it back.


 

If the "verification token" is an access token that is used by Google Cloud on behalf of a service account, then you can use that Authentication element in your target endpoint to tell Apigee to do that automatically. 

But it sounds like you might want to spend more time looking over the Apigee documentation, maybe watch some videos, to get a better understanding of what it is doing and how to best use it. 

If you have an Apigee proxy, that is a thing that an app can call, in lieu of directly calling the upstream service.  Why not have the client app just call the upstream thing directly?  There are lots of reasons you might want to inject a proxy between the client and the upstream. The main category is "non functional requirements" - things like logging, rate limiting, credential mapping (client passes one type of credential to Apigee (eg API key) and Apigee uses a different kind of credential (eg token) calling the upstream), request routing, caching, and many more.

One of the things Apigee X can do is automatically retrieve an access token or ID token on behalf of a service account, and then apply that token to the Apigee-to-upstream call.  The way to tell Apigee to do that is to use the Authentication element.  This is documented - read up on it to get more details.  You do not have to configure Apigee to "manually" generate or retrieve a token. You CAN do that if you want, but you do not have to do that. The Authentication element makes it a lot easier. 

good luck