4

I created a service account email and added cloudfunctions.invoker role to the email so I can make sure only cloud tasks can trigger cloud functions, and I removed AllUsers role. But when cloud tasks tried to run cloud function, the status code is UNAUTHENTICATED(16): HTTP status code 401 and execution failed.

My current code and console is like this.

index.ts

export const addTasks = functions.https.onCall((data, context) => {
  if (!context.auth) {
    throw new functions.https.HttpsError('failed-precondition', 'You are not authenticated.')
  }

  const client = new tasks.CloudTasksClient()

  const projectId = functions.config().project.id
  const queue = 'queue'
  const location = functions.config().project.location
  const parent = client.queuePath(projectId, location, queue)
  const url = `https://${location}-${projectId}.cloudfunctions.net/executeSomething`
  const serviceAccountEmail = functions.config().project.email

  const task: tasks.protos.google.cloud.tasks.v2.ITask = {
    httpRequest: {
      httpMethod: 'POST',
      url: url,
      oidcToken: {
        serviceAccountEmail: serviceAccountEmail,
      },
    },
    scheduleTime: {
      seconds: ...,
    },
  }

  const request: tasks.protos.google.cloud.tasks.v2.ICreateTaskRequest = {
    parent: parent,
    task: task,
  }

  return client.createTask(request)
}

My cloud function's console

enter image description here

I added the cloud functions invoker role to the service account email.

My firebase project environment variables

enter image description here

When I added AllUsers role to cloud functions, it works as expected so I am sure I made a mistake when resrticting access. What am I missing?

Update:

My cloud tasks console

enter image description here enter image description here

Ooto
  • 1,117
  • 16
  • 39

3 Answers3

2

for me removing audience: new URL(url).origin, from oidcToken object inside task resolved the UNAUTHENTICATED(16): HTTP status code 401 issue.

const task = {
    httpRequest: {
      httpMethod: 'POST',
      url,
      oidcToken: {
        serviceAccountEmail: email,
        //audience: new URL(url).origin, 
      },
      headers: {
        'Content-Type': 'application/json',
      },
      body,
    },
  };
GorvGoyl
  • 42,508
  • 29
  • 229
  • 225
0

Your OIDC token seems broken against the specification

Simply provide the email, without the attribute name like in this example, or use the snake_case like described in the spec

guillaume blaquiere
  • 66,369
  • 2
  • 47
  • 76
  • I tried with `test-653@myproject.iam.gserviceaccount.com` and updated my environment variables and deploy functions again but it still faied. (I input `test` and a random number is automatically added) I don't think the problem comes from the name. I attached some pictures. Please take a look. – Ooto Nov 23 '20 at 09:09
  • 401 means "you haven't provide authentication header", that's why I focused on the correct service account email definition. I would prefer a 403 "you are authenticated, but not authorized, or the token isn't readable/valid". I'm strongly convinced that the call is performed without Authorization header. But I don't know why it continues to fail. – guillaume blaquiere Nov 23 '20 at 09:37
0

Adding audience did the trick for me. Here is how my task object look like -

    const task = {
      httpRequest: {
        httpMethod: 'POST',
        url,
        headers: { 'Content-Type': 'application/json' },
        oidcToken: { 
          serviceAccountEmail: sa, 
          audience: aud 
        },
      },
    };

nodejs doc

davidbilla
  • 2,120
  • 1
  • 15
  • 26