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 type | Required IAM actions | Resource 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
| Symptom | Cause | Fix |
|---|---|---|
| "is not authorized to perform: sts:GetServiceBearerToken" | sts:GetServiceBearerToken is missing from the IAM identity policy — it cannot be granted via the CodeArtifact resource policy | Add 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 ARN | Change the resource for GetAuthorizationToken to the domain ARN: arn:aws:codeartifact:region:account:domain/name |
| Cross-account access denied despite resource policy allowing it | The identity policy in the calling account does not grant the CodeArtifact actions — resource policy alone is not sufficient | Add the CodeArtifact actions to the calling role's IAM identity policy in the CI/consumer account |
| 403 on ReadFromRepository for a specific package | The repository policy was updated after the token was issued — tokens cache the permission snapshot at issuance time | Fetch a new token after updating repository policies; old tokens reflect old permissions |
| "AccessDeniedException: User is not authorized to perform codeartifact:PublishPackageVersion" in CI | The CodeBuild service role has only consumer permissions (ReadFromRepository) but not publisher permissions | Add codeartifact:PublishPackageVersion and codeartifact:PutPackageMetadata to the CodeBuild role |
| Developers can publish locally but CI cannot | Developer IAM user/role has broader permissions than the CodeBuild service role; the service role is missing publish actions | Match the CI role policy to include the same publish actions that developers have; use iam simulate-principal-policy to compare |