If you've wired up an AWS CodeBuild project to pull source from GitHub
via CodeConnections (formerly CodeStar Connections), you may have hit
this error at the moment you call UpdateProject or trigger your first
build:
OAuthProviderException: User is not authorized to access connection
arn:aws:codestar-connections:us-east-1:123456789012:connection/...
The error wording is wrong in two different ways at the same time, which
is what makes it such a brutal debugging session. This post walks
through what the message actually means, two distinct root causes that
both produce it, and the IAM policy shape that resolves both.
"User" doesn't mean what you think it means
The first thing to know: the User in the error message is almost
never the IAM principal making the API call. It's the CodeBuild
service role — the role CodeBuild will eventually assume to clone
your repo at build time.
When you call UpdateProject with a source.auth.type of
CODECONNECTIONS, AWS validates the service role's permissions to
use the connection. If those permissions are missing, the API call
fails with this error and blames "User," meaning the service role —
not you.
So the first thing to do when this error fires: stop checking the
permissions on whoever ran the command, and start checking the
permissions on the service role referenced by the CodeBuild
project.
Trap #1: GetConnectionToken is required but undocumented
The AWS docs for CodeConnections IAM say you need
codestar-connections:UseConnection on the connection ARN. That's
true but incomplete.
You also need codestar-connections:GetConnectionToken — and
this requirement is undocumented in the IAM examples page as of
this writing. Without it, UpdateProject returns
OAuthProviderException even though your policy "looks right."
This trap was first surfaced publicly in a comment on a Terraform
AWS Provider GitHub issue, and you'll find it referenced in the
SDK source if you go looking. The minimum service-role grant that
actually works is:
statement {
effect = "Allow"
actions = [
"codestar-connections:UseConnection",
"codestar-connections:GetConnectionToken", # ← undocumented requirement
"codeconnections:UseConnection",
"codeconnections:GetConnectionToken", # ← same
]
resources = [aws_codestarconnections_connection.your_connection.arn]
}
(More on why the snippet duplicates each action under both prefixes
in a moment.)
If you've been chasing this for an hour and your policy already has
UseConnection, adding GetConnectionToken is very likely the fix.
Trap #2: list actions don't accept ARN scoping
Now here's the meaner one — and the one that bites you the second
time, after you've added GetConnectionToken and your build still
fails with the same error.
CodeConnections has three actions that do not accept resource-level
permissions at all:
ListConnectionsListInstallationTargetsListTagsForResource
If you grant these with resources scoped to a specific connection
ARN, the statement silently never matches the action. AWS denies
the call without telling you which action was denied — and surfaces
the same misleading "User is not authorized to access connection"
error as Trap #1.
AWS's internal flow for UpdateProject (and for the build's source-
resolution phase) calls list-level actions as part of validating the
connection binding. Your policy can grant codeconnections:* on the
connection ARN and STILL fail, because the wildcard doesn't help when
the action itself refuses resource-level scoping.
The fix: split your connections grant into two statements. List
actions get resources: ["*"]. Resource-level actions stay scoped to
your connection ARN.
# Statement 1: list-level actions that don't accept ARN scoping.
# These MUST use "*" or they'll be silently denied.
statement {
sid = "CodeConnectionsListLevel"
effect = "Allow"
actions = [
"codestar-connections:ListConnections",
"codestar-connections:ListInstallationTargets",
"codestar-connections:ListTagsForResource",
"codeconnections:ListConnections",
"codeconnections:ListInstallationTargets",
"codeconnections:ListTagsForResource",
]
resources = ["*"]
}
# Statement 2: resource-level actions you can safely scope.
statement {
sid = "CodeConnectionsResourceLevel"
effect = "Allow"
actions = [
"codestar-connections:GetConnection",
"codestar-connections:GetConnectionToken",
"codestar-connections:PassConnection",
"codestar-connections:UseConnection",
"codeconnections:GetConnection",
"codeconnections:GetConnectionToken",
"codeconnections:PassConnection",
"codeconnections:UseConnection",
]
resources = [aws_codestarconnections_connection.your_connection.arn]
}
If you'd rather not enumerate every action, the loosest version
that's still narrowly enough scoped is Get*, List*, Pass*, Use* on
"*", with the understanding that the role can then enumerate every
connection in your account. For most CI workloads that's a reasonable
tradeoff.
Why both codestar-connections:* and codeconnections:*?
In March 2024 AWS renamed the service from CodeStar Connections to
CodeConnections. Both action prefixes still work, and AWS aliases
them internally — but different parts of the AWS SDK and AWS-internal
callers use different prefixes, sometimes within the same API call.
ARNs created before the rename keep the legacy arn:aws:codestar- prefix; ARNs created after the rename keep it too,
connections:...
because changing them would break thousands of existing integrations.
Granting both prefixes for every action you care about costs you
nothing and saves a future debugging session if AWS quietly shifts
its internal caller from one prefix to the other.
A note on diagnosis
When this error fires and your IAM simulator says the calling user
has the permissions, the simulator is testing the wrong principal.
The denial is on the service role, not the API caller. CloudTrail's
event for the failed UpdateProject shows the actual denied principal
in userIdentity, and sometimes references the target principal by
ARN in the error metadata. That's the surest way to ground-truth
what's failing.
aws iam simulate-principal-policy is still useful — just point it
at the service role's ARN, not your own:
aws iam simulate-principal-policy \
--policy-source-arn arn:aws:iam::ACCOUNT:role/your-codebuild-service-role \
--action-names \
codestar-connections:UseConnection \
codestar-connections:GetConnectionToken \
codestar-connections:GetConnection \
codeconnections:ListConnections \
codeconnections:ListInstallationTargets \
--resource-arns CONNECTION_ARN
The list actions will report implicitDeny against an ARN-scoped
policy even when the resource-level ones are allowed. That's the
giveaway.
A note on IAM eventual consistency
One more wrinkle: even with a correct policy, terraform apply
(or whatever provisioning tool you use) sometimes fails on the first
apply and succeeds on the second, with zero changes between them.
That's IAM eventual consistency. When the policy and the
UpdateProject call land in the same plan, the policy attachment
may not have propagated by the time the API call is made. AWS
returns the same misleading error, because at the moment of the
check, the service role genuinely doesn't have the permission yet.
If you're seeing this race repeatedly, the simplest workaround is
to put the IAM resources in a separate stack/workspace from the
CodeBuild project itself, so the policy is fully applied before
the project resource is touched. A CDK project that takes exactly
this approach is [linked at the bottom of this post]; their stack
comment explicitly calls out the eventual-consistency hazard.
Summary
If you're stuck on OAuthProviderException: User is not authorized and your IAM looks correct:
to access connection
- You're checking the wrong principal. It's the CodeBuild service role, not the caller.
-
Add
GetConnectionTokento the service role's grants — it's required but undocumented. -
Split list actions out to
resources: ["*"]. They don't accept ARN scoping and will silently fail otherwise. -
Grant both
codestar-connections:*andcodeconnections:*action prefixes. The rename in 2024 left both still in use. - If applies are flaky, separate the IAM policy from the CodeBuild project so policy attachments fully propagate first.
References worth bookmarking:
- AWS docs: Permissions and examples for AWS CodeConnections
- AWS docs: Troubleshooting connections
- The Terraform AWS Provider GitHub issue where
GetConnectionTokenwas first surfaced publicly (search the provider issues forOAuthProviderException) - fourTheorem's
codebuild-gha-runnersCDK repo (theconnection- stack.tsfile) for a working reference implementation that uses the wildcard-resource approach and addresses eventual consistency




















