Guide · AWS Developer Tools · IAM

CodeArtifact IAM Permissions for MCP Servers — Domain Policies, Cross-Account, CI Roles

CodeArtifact uses a layered permission model — IAM identity policies on the calling principal, a domain-level resource policy, and a repository-level resource policy — and all three layers must allow the action for it to succeed. Three misunderstandings cause the majority of access errors: codeartifact:GetAuthorizationToken is a domain-level action but sts:GetServiceBearerToken is an STS action — it must be present on the IAM identity policy and cannot be granted via the domain or repository resource policy; omitting it produces "User is not authorized to perform: sts:GetServiceBearerToken on resource: arn:aws:sts::*:*" even when the codeartifact action is allowed; cross-account access requires both a principal grant in the domain/repo resource policy (allowing the foreign account) and the IAM role in the foreign account having the codeartifact actions — granting only the resource policy without the identity policy (or vice versa) results in access denied; and the domain resource policy uses codeartifact:GetAuthorizationToken as the resource, not the repository ARN — attempts to scope the token action to a specific repository ARN fail because tokens are issued at the domain level and the resource must be the domain ARN (arn:aws:codeartifact:region:account:domain/name).

TL;DR

Every principal that authenticates to CodeArtifact needs three IAM actions: codeartifact:GetAuthorizationToken (on the domain ARN), codeartifact:GetRepositoryEndpoint (on *), and sts:GetServiceBearerToken (on *). Consumers additionally need codeartifact:ReadFromRepository. Publishers additionally need codeartifact:PublishPackageVersion and codeartifact:PutPackageMetadata. Cross-account access requires the domain/repo resource policy to explicitly allow the foreign account principal.

Minimum IAM action sets by role type

Role typeRequired IAM actionsResource scope
Consumer (ECS task, developer install) codeartifact:GetAuthorizationToken, codeartifact:GetRepositoryEndpoint, codeartifact:ReadFromRepository, sts:GetServiceBearerToken Domain ARN for GetAuthorizationToken; repo ARN or * for others
Publisher (CI build role) All Consumer actions + codeartifact:PublishPackageVersion, codeartifact:PutPackageMetadata Repo ARN or * for publish actions
Admin (domain/repo management) All Publisher actions + codeartifact:CreateRepository, codeartifact:UpdateRepository, codeartifact:AssociateExternalConnection, codeartifact:PutDomainPermissionsPolicy, codeartifact:PutRepositoryPermissionsPolicy Domain ARN and repo ARN

Consumer role (ECS task reading packages)

// CDK: minimum IAM policy for an ECS task role that installs packages from CodeArtifact
const taskRole = new iam.Role(this, "McpTaskRole", {
  assumedBy: new iam.ServicePrincipal("ecs-tasks.amazonaws.com"),
});

taskRole.addToPolicy(new iam.PolicyStatement({
  sid: "CodeArtifactTokenAndEndpoint",
  effect: iam.Effect.ALLOW,
  actions: [
    "codeartifact:GetAuthorizationToken",   // Domain-level action — resource is the domain ARN
    "codeartifact:GetRepositoryEndpoint",   // Resolve npm/PyPI endpoint URL
    "sts:GetServiceBearerToken",            // Required alongside GetAuthorizationToken
  ],
  resources: [
    // GetAuthorizationToken must target the domain ARN, not a repository
    `arn:aws:codeartifact:${region}:${account}:domain/myorg`,
    "*",  // GetRepositoryEndpoint and sts:GetServiceBearerToken have no specific ARN
  ],
}));

taskRole.addToPolicy(new iam.PolicyStatement({
  sid: "CodeArtifactRead",
  effect: iam.Effect.ALLOW,
  actions: [
    "codeartifact:ReadFromRepository",     // Download packages
  ],
  resources: [
    // Scope to the specific repository used by this service
    `arn:aws:codeartifact:${region}:${account}:repository/myorg/mcp-servers`,
  ],
}));

Gotcha — resource ARN for GetAuthorizationToken: Unlike most CodeArtifact actions that target a repository ARN, codeartifact:GetAuthorizationToken is scoped to the domain. Using arn:aws:codeartifact:...:repository/myorg/mcp-servers as the resource for this action results in "is not authorized to perform codeartifact:GetAuthorizationToken on resource" even if the principal has the action — the resource type mismatch causes the evaluation to fail.

CI publisher role (CodeBuild publishing packages)

// CDK: IAM role for CodeBuild that publishes packages to CodeArtifact
const ciPublisherRole = new iam.Role(this, "CiPublisherRole", {
  assumedBy: new iam.ServicePrincipal("codebuild.amazonaws.com"),
});

ciPublisherRole.addToPolicy(new iam.PolicyStatement({
  sid: "CodeArtifactPublish",
  effect: iam.Effect.ALLOW,
  actions: [
    "codeartifact:GetAuthorizationToken",
    "codeartifact:GetRepositoryEndpoint",
    "codeartifact:ReadFromRepository",      // Install dependencies during build
    "codeartifact:PublishPackageVersion",   // Upload new package versions
    "codeartifact:PutPackageMetadata",      // Set package metadata (tags, description)
    "sts:GetServiceBearerToken",
  ],
  resources: ["*"],  // Scope to specific ARNs in production
}));

// JSON equivalent (for inline policy or SCPs)
// {
//   "Version": "2012-10-17",
//   "Statement": [{
//     "Effect": "Allow",
//     "Action": [
//       "codeartifact:GetAuthorizationToken",
//       "codeartifact:GetRepositoryEndpoint",
//       "codeartifact:ReadFromRepository",
//       "codeartifact:PublishPackageVersion",
//       "codeartifact:PutPackageMetadata",
//       "sts:GetServiceBearerToken"
//     ],
//     "Resource": "*"
//   }]
// }

Domain resource policy

The domain resource policy controls who can issue authorization tokens and perform domain-level operations. It is optional for same-account access (IAM identity policies are sufficient) but required for cross-account.

# Apply a domain resource policy that allows another account to authenticate
# (used when CI runs in account A but the CodeArtifact domain is in account B)

aws codeartifact put-domain-permissions-policy \
  --domain myorg \
  --domain-owner 111111111111 \
  --policy-document '{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Sid": "AllowCrossAccountTokenFetch",
        "Effect": "Allow",
        "Principal": {
          "AWS": "arn:aws:iam::222222222222:role/CiPublisherRole"
        },
        "Action": [
          "codeartifact:GetAuthorizationToken",
          "codeartifact:DescribeDomain",
          "sts:GetServiceBearerToken"
        ],
        "Resource": "*"
      }
    ]
  }'

Policy replacement semantics: put-domain-permissions-policy replaces the entire domain policy in a single atomic PUT — adding a new statement requires including all existing statements in the request. If you call it twice with different payloads, the second call overwrites the first. Use get-domain-permissions-policy to read the current policy before updating.

Repository resource policy

Repository policies control who can read from or publish to a specific repository. They are evaluated after the domain policy and the identity policy.

# Grant another account read access to a repository (for shared internal packages)
aws codeartifact put-repository-permissions-policy \
  --domain myorg \
  --domain-owner 111111111111 \
  --repository mcp-servers \
  --policy-document '{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Sid": "AllowCrossAccountRead",
        "Effect": "Allow",
        "Principal": {
          "AWS": "arn:aws:iam::333333333333:root"
        },
        "Action": [
          "codeartifact:ReadFromRepository",
          "codeartifact:GetRepositoryEndpoint",
          "codeartifact:DescribeRepository"
        ],
        "Resource": "*"
      },
      {
        "Sid": "AllowInternalPublish",
        "Effect": "Allow",
        "Principal": {
          "AWS": "arn:aws:iam::111111111111:role/CiPublisherRole"
        },
        "Action": [
          "codeartifact:PublishPackageVersion",
          "codeartifact:PutPackageMetadata",
          "codeartifact:ReadFromRepository"
        ],
        "Resource": "*"
      }
    ]
  }'

Cross-account architecture: CI in account A, domain in account B

// CDK Stack B (artifact account): domain + repository + cross-account resource policy
// Allows account A's CI role to authenticate and publish

const domain = new codeartifact.CfnDomain(this, "SharedDomain", {
  domainName: "shared-artifacts",
  permissionsPolicyDocument: {
    Version: "2012-10-17",
    Statement: [{
      Sid: "AllowCiAccountTokenFetch",
      Effect: "Allow",
      Principal: {
        // Wildcard on account A — any role in that account can get a token
        // Tighten to specific role ARN in production
        AWS: `arn:aws:iam::${ciAccountId}:root`,
      },
      Action: [
        "codeartifact:GetAuthorizationToken",
        "codeartifact:DescribeDomain",
        "sts:GetServiceBearerToken",
      ],
      Resource: "*",
    }],
  },
});

const repo = new codeartifact.CfnRepository(this, "SharedRepo", {
  domainName: domain.domainName,
  repositoryName: "mcp-packages",
  permissionsPolicyDocument: {
    Version: "2012-10-17",
    Statement: [{
      Sid: "AllowCiAccountPublish",
      Effect: "Allow",
      Principal: {
        AWS: `arn:aws:iam::${ciAccountId}:role/CiPublisherRole`,
      },
      Action: [
        "codeartifact:ReadFromRepository",
        "codeartifact:PublishPackageVersion",
        "codeartifact:PutPackageMetadata",
        "codeartifact:GetRepositoryEndpoint",
      ],
      Resource: "*",
    }],
  },
});
repo.addDependency(domain);
// CDK Stack A (CI account): CodeBuild role that cross-accounts into the artifact domain
// The role's identity policy must grant the actions even though the resource policy allows it

const ciRole = new iam.Role(this, "CiRole", {
  assumedBy: new iam.ServicePrincipal("codebuild.amazonaws.com"),
});

ciRole.addToPolicy(new iam.PolicyStatement({
  actions: [
    "codeartifact:GetAuthorizationToken",
    "codeartifact:GetRepositoryEndpoint",
    "codeartifact:ReadFromRepository",
    "codeartifact:PublishPackageVersion",
    "codeartifact:PutPackageMetadata",
    "sts:GetServiceBearerToken",
  ],
  resources: ["*"],
}));

Both sides are required: Cross-account CodeArtifact access follows the standard AWS cross-account pattern — the resource (domain/repo) policy must allow the foreign principal AND the foreign principal's identity policy must allow the action. Neither alone is sufficient.

Verifying effective permissions

# Check what permissions a specific principal has on a domain
aws codeartifact get-domain-permissions-policy \
  --domain myorg \
  --query policy

# Check what permissions a specific principal has on a repository
aws codeartifact get-repository-permissions-policy \
  --domain myorg \
  --repository mcp-servers \
  --query policy

# Simulate a specific action using IAM policy simulator (same-account only)
aws iam simulate-principal-policy \
  --policy-source-arn arn:aws:iam::123456789012:role/EcsTaskRole \
  --action-names codeartifact:ReadFromRepository \
  --resource-arns arn:aws:codeartifact:us-east-1:123456789012:repository/myorg/mcp-servers

# Test authentication end-to-end: try to get a token as the current identity
aws codeartifact get-authorization-token \
  --domain myorg \
  --domain-owner 123456789012 \
  --query authorizationToken \
  --output text | head -c 20  # Print just the first 20 chars to verify it worked

Common failure modes

SymptomCauseFix
"is not authorized to perform: sts:GetServiceBearerToken"sts:GetServiceBearerToken is missing from the IAM identity policy — it cannot be granted via the CodeArtifact resource policyAdd sts:GetServiceBearerToken with resource: "*" to the role's inline or managed policy
"is not authorized to perform: codeartifact:GetAuthorizationToken on resource: arn:...:repository/..."The policy grants GetAuthorizationToken on the repository ARN; this action is domain-scoped and must target the domain ARNChange the resource for GetAuthorizationToken to the domain ARN: arn:aws:codeartifact:region:account:domain/name
Cross-account access denied despite resource policy allowing itThe identity policy in the calling account does not grant the CodeArtifact actions — resource policy alone is not sufficientAdd the CodeArtifact actions to the calling role's IAM identity policy in the CI/consumer account
403 on ReadFromRepository for a specific packageThe repository policy was updated after the token was issued — tokens cache the permission snapshot at issuance timeFetch a new token after updating repository policies; old tokens reflect old permissions
"AccessDeniedException: User is not authorized to perform codeartifact:PublishPackageVersion" in CIThe CodeBuild service role has only consumer permissions (ReadFromRepository) but not publisher permissionsAdd codeartifact:PublishPackageVersion and codeartifact:PutPackageMetadata to the CodeBuild role
Developers can publish locally but CI cannotDeveloper IAM user/role has broader permissions than the CodeBuild service role; the service role is missing publish actionsMatch the CI role policy to include the same publish actions that developers have; use iam simulate-principal-policy to compare