-3

I am getting this error when calling AssumeRoleWithWebIdentityCommand from a firebase function.

Error: {     
   Type: 'Sender',     
   Code: 'InvalidIdentityToken',     
   Message: 'Incorrect token audience',     
}

I have configured a Service account, assigned it to the firebase function, and am attempting to assume the aws role like so.

const config = {
  policyName: "Firebase-Access-Dynamo",
  roleArn: "arn:aws:iam::---aws_account_id--:role/Firebase-Role",
  projectID: "---gcp_project_id----",
  serviceAccountName: "aws-bridge",
};

async function getGoogleIdToken() {
  const idc = await new GoogleAuth().getIdTokenClient(config.policyName);

  const { Authorization } = await idc.getRequestHeaders();

  return Authorization.replace(/^Bearer\s+/i, "");
}

exports.beforeusercreate = beforeUserCreated(
  {
    serviceAccount: `${config.serviceAccountName}@${config.projectID}.iam.gserviceaccount.com`,
  },
  async () => {
    const sts = new STSClient({ region: "us-east-1" });

    const { Credentials } = await sts.send(
      new AssumeRoleWithWebIdentityCommand({
        RoleArn: config.roleArn,
        RoleSessionName: `gcp-${Date.now()}`,
        WebIdentityToken: await getGoogleIdToken(),
      })
    );

    if (!Credentials) {
      throw new Error("no credential");
    }

    ....
  }
);

In terraform I have configured the google service account like so:

resource "google_service_account" "aws_bridge" {
  account_id   = "aws-bridge"
  display_name = "AWS Bridge (Firebase v2)"
}

#...some more stuff

locals {
   gcp_trust_policy = "Firebase-Access-Dynamo"
}

resource "aws_iam_openid_connect_provider" "google" {
  url             = "https://accounts.google.com"
  client_id_list  = [local.gcp_trust_policy]
}

resource "aws_iam_role" "firebase_access" {
  name = "Firebase-Role"
  assume_role_policy = jsonencode({
    Version = "2012-10-17",
    Statement = [{
      Effect    = "Allow",
      Principal = { Federated = "accounts.google.com" },
      Action    = "sts:AssumeRoleWithWebIdentity",
      Condition = {
        StringEquals = {
          "accounts.google.com:aud": google_service_account.aws_bridge.unique_id,
          "accounts.google.com:sub": google_service_account.aws_bridge.unique_id,
          "accounts.google.com:oaud": local.gcp_trust_policy
        }
      }
    }]
  })
}

resource "aws_iam_role_policy" "dynamo_access" {
  name = local.gcp_trust_policy
  role = aws_iam_role.firebase_access.id
  policy = jsonencode({
    Version = "2012-10-17",
    Statement = [{
      Effect = "Allow",
      Action = [
         "dynamodb:GetItem", 
         "dynamodb:PutItem", 
         "dynamodb:UpdateItem", 
         "dynamodb:Query"
      ],
      Resource = [
        aws_dynamodb_table.architect.arn,
        "${aws_dynamodb_table.architect.arn}/index/*"
      ]
    }]
  })
}

I have a lot of other terraform modules, I extracted the ones that I think pertain to my question. I'm hoping it's something obvious.

I have tried some suggestions from ChatGPT that haven't helped. Finding examples for this subject is difficult.

1 Answer 1

0

The "audience" of a JSON web token, encoded in its aud property, exists to make sure that it isn't possible to use a token intended for authentication to one system to get access to some other system. For example, if you have trust policies set to allow a particular subject ("sub") to access Google Cloud Platform and Amazon Web Services then you'd typically configure each with a different audience so that tokens generated for Amazon Web Services cannot be used to authenticate to Google Cloud Platform.

In your code example you've configured the IAM role's assume role policy to require an audience string that matches google_service_account.aws_bridge.unique_id, which is the same value you set for sub. That seems incorrect, because:

  • aud (audience) should somehow identify the system the credentials are being sent to
  • sub (subject) should somehow identify the system the credentials are being sent from

Therefore the aud condition in your assume role policy (aka "trust policy") should be set to something that identifies the role that is being assumed or the system that assuming this role would grant access to.

You also need to make sure that the JSON Web Token generated by your identity provider (Google, in your case) includes an aud value that exactly matches what you configured in your trust policy.

I assume you are using google-auth-library and so new GoogleAuth is returning an instance of GoogleAuth. In that case, the argument to getIdTokenClient is the audience value, which you've currently set to "Firebase-Access-Dynamo" by referring to config.policyName.

If that is the audience value you want to use (which seems reasonable, since it identifies what the token is granting access to: DynamoDB) then you could make this work by specifying the same audience string in your assume role policy:

      Condition = {
        StringEquals = {
          "accounts.google.com:aud": "Firebase-Access-Dynamo",
          "accounts.google.com:sub": google_service_account.aws_bridge.unique_id,
          "accounts.google.com:oaud": local.gcp_trust_policy
        }
      }
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.