3

I'm trying to use API Gateway in front of Cloud Functions, but getting a 401 response when the Cloud Functions are not publicly accessible.

I set the Gateway to use the default AppEngine service account identity (project-id@appspot.gserviceaccount.com), and gave the service account the Cloud Functions Invoker role in IAM. I can also see it among the invokers under the Cloud Functions Permissions tab. When the Gateway endpoint is invoked, I get a 401 response with a poorly formatted HTML payload that says "Your client does not have permission to the requested URL [url]". Unfortunately the Gateway logs are also not indicating what is the problem. When I make the Cloud Function public and wait a few minutes, the call succeeds.

I have read the "Securing backend services" section of the Guide a few times and can't see what I'm missing.

ps: This is not a JWT issue, that part works brilliantly

sw0rdf1sh
  • 311
  • 2
  • 12
  • Can you share your security definition and your endpoint definition in your OpenApi spec? – guillaume blaquiere Mar 15 '21 at 08:07
  • Sure, [here it is](https://pastebin.com/h2M9M4Em) – sw0rdf1sh Mar 15 '21 at 09:32
  • It could be interesting to change the backend and to deploy a public Cloud Functions that simply log all the request header. Like this, you will be able to catch the token and to decode it to understand its content. – guillaume blaquiere Mar 15 '21 at 20:19
  • 1
    Hi @guillaumeblaquiere! I do see the JWT in that case, but my goal is to shield the Cloud Functions from outside requests, and make the API Gateway the only point of entry to my app. – sw0rdf1sh Mar 16 '21 at 12:37
  • Ok, but did you decode the token? did you see some values not correct? The audience? the email? the issuers? – guillaume blaquiere Mar 16 '21 at 13:43
  • 3
    The Gateway successfully verifies the token, I know for a fact that it works fine. This issue I'm asking here is about the internal authentication when the API Gateway calls the Cloud Function within Google Cloud. This does not involve the JWT sent by the client. Instead, the Gateway is supposed to assume the identity of a service account (I set this to the default AppEngine service account), and the Cloud Functions should allow the call if the service account has the proper role (which it does, I set it under IAM). – sw0rdf1sh Mar 17 '21 at 14:18
  • Yes, it's correct. But, if the API gateway builds not correctly the token, or set the wrong audience, you will be able to see that by decoding the JWT token generated by API gateway and sent to your Cloud Function. That's why, the idea is to plug a public function that log the headers values to help you in this debugging step. – guillaume blaquiere Mar 17 '21 at 14:37
  • Or you can perform another test. Try to impersonate the service account when you call the protected Cloud Function. `curl -H "Authorization: Bearer $(gcloud auth print-identity-token --impersonate-service-account=@appspot.gserviceaccount.com --include-email --audiences= )" ` – guillaume blaquiere Mar 17 '21 at 14:41
  • Ah you meant I should inspect the internal authentication token, not the external one! I'll check this and report back – sw0rdf1sh Mar 18 '21 at 09:58

2 Answers2

3

Found the root of the problem: it's the aud claim in the internal JWT. I wanted to leverage the flexible addressability of Cloud Functions, e.g. that a function called a handles every request that starts with a/.... In my example, the function is called hello and I want to invoke it as /hello/example from the Gateway.

The internal JWT sets the Audience claim to the address given in the OpenAPI specs (https://...cloudfunctions.net/hello/example), which does not exactly match the base URL of the Cloud Function (which is only https://...cloudfunctions.net/hello). Therefore, the Cloud Functions authorizer rejects the JWT, even though the Audience is a valid URL that is actually served by this function.

The obvious solution is to not use the variable postfix when calling Cloud Functions from the API Gateway.

sw0rdf1sh
  • 311
  • 2
  • 12
0

BTW, you can use jwt_audience.

Set the jwt_audience to https://...cloudfunctions.net/hello

See https://cloud.google.com/endpoints/docs/openapi/openapi-extensions#jwt_audience_disable_auth