I have simple test callable function deployed via firebase deploy --only functions.
Here it's content:
export const test = region('europe-west1').https.onCall((data, context) => {
logger.debug('test call info', data, context);
return 'hello, world!';
});
I can successfully call it by doing HTTP call to https://europe-west1-{project}.cloudfunctions.net/test and recieve {"result": "hello, world!"}.
Now want to protect this function via IAM policies.
- I create new service account and load it's credentials json.
- I go to console and remove from
allUsersa Cloud Functions Invoker role and add service account created in #1 with Cloud Functions Invoker role. <--- Now I recieve 403 Forbidden when I try to call my test function, as expected - I create id-token with my service account at my PC
const auth = new GoogleAuth({keyFile: './key.json'});
targetAudience = new URL('https://europe-west1-{project}.cloudfunctions.net/test');
const client = await auth.getIdTokenClient(targetAudience as string);
const idToken = await client.idTokenProvider.fetchIdToken(targetAudience as string);
console.log(idToken);
- I verify my token is correct and issued for the right service account via
https://www.googleapis.com/oauth2/v3/tokeninfo?id_token={{id-token}}. - I call my endpoint with
Authorization: Bearer {{id-token}}header. - I get 401 Unauthorized error with JSON telling me
{
"error": {
"message": "Unauthenticated",
"status": "UNAUTHENTICATED"
}
}
- I'm in pain since after reading most of google cloud docs, SO (1, 2), etc. it seems that I do everything right but still get this error.
UPD1: providing additional info to @DazWilkin
1.
gcloud auth activate-service-account cloud-functions-invoker@{project-id}.iam.gserviceaccount.com --key-file="key.json"
Activated service account credentials for: [cloud-functions-invoker@{project-id}.iam.gserviceaccount.com]
gcloud projects get-iam-policy {project-id}
...
- members:
- serviceAccount:cloud-functions-admin@{project-id}.iam.gserviceaccount.com
- serviceAccount:cloud-functions-invoker@{project-id}.iam.gserviceaccount.com
role: roles/cloudfunctions.invoker
...
- I copy this value
gcloud auth print-identity-token | clip - Here is what I get after calling
https://www.googleapis.com/oauth2/v3/tokeninfo?id_token={{id-token}}
{
"aud": "32555940559.apps.googleusercontent.com",
"azp": "cloud-functions-invoker@{project-id}.iam.gserviceaccount.com",
"email": "cloud-functions-invoker@{project-id}.iam.gserviceaccount.com",
"email_verified": "true",
"exp": "1629157648",
"iat": "1629154048",
"iss": "https://accounts.google.com",
"sub": "114693730231812960252",
"alg": "RS256",
"kid": "6ef4bd908591f697a8a9b893b03e6a77eb04e51f",
"typ": "JWT"
}
- I make this curl (copied from postman). I've also tried same curl with command-line - got same result.
curl --location --request POST 'https://europe-west1-{project-id}.cloudfunctions.net/test' \
--header 'Authorization: Bearer
{token}
' \
--header 'Content-Type: application/json' \
--data-raw '{
"data": {
"goodbye": "world:("
}
}'
UPD2: new input
This behaviour is observed only with callable functions https.onCall(). If I use https.onRequest() things work fine but I'm still confused about onCall behaviour, since it seems that I implement protocol correctly (see my curl).
gcloudto [gcloud auth print-identity-token](cloud.google.com/sdk/gcloud/reference/auth/print-identity-token) aftergcloud auth activate-service-account@](cloud.google.com/sdk/gcloud/reference/auth/…). This will eliminate whether your client generation of the identity token is incorrect. Although your check using the endpoint does provide a good way to check.gcloud projects get-iam-policyfor the project to confirm the invoker binding.{token}in lieu of the value in your question, then I don't understand why it isn't working!