[ECR] [request]: return 401 with challenge instead of 400 on expired access token
- Dominant language
- Shell
- Stars
- 5.4k
- Forks
- 334
- PR merge metrics
- No merged PRs in 30d
Description
### Community Note
* Please vote on this issue by adding a 👍 [reaction](https://blog.github.com/2016-03-10-add-reactions-to-pull-requests-issues-and-comments/) to the original issue to help the community and maintainers prioritize this request
* Please do not leave "+1" or "me too" comments, they generate extra noise for issue followers and do not help prioritize the request
* If you are interested in working on this issue or have submitted a pull request, please leave a comment
**Tell us about your request**
When a bearer token presented to `public.ecr.aws` is **expired or invalid**, return `401 Unauthorized` with a `WWW-Authenticate: Bearer …` challenge (per the Docker Registry v2 / OCI token-auth flow), instead of `400 Bad Request`. Today the expired-token case returns `400` with no challenge, which is problematic for registry clients that evict from their access token cache on 401.
**Which service(s) is this request for?**
Amazon ECR — **ECR Public** (`public.ecr.aws`) registry/distribution API, token authentication.
**Tell us about the problem you're trying to solve. What are you trying to do, and why is it hard?**
We noticed that when authenticating to `public.ecr.aws` with the standard Docker Registry v2 bearer-token flow — fetch a token from the realm, cache it, and reuse it until it expires — an **expired or invalid** token is rejected with **`400 Bad Request` and no `WWW-Authenticate` header**, instead of the expected `401 Unauthorized` + `WWW-Authenticate: Bearer realm=…,service=…,scope=…` challenge.
Because `400` carries no challenge and is a client-error class, clients neither refresh the token nor retry — the request hard-fails until the cached token happens to rotate for some other reason. The correct `401 + challenge` would instead tell the client to re-acquire a token from the realm and retry.
Reproduction — status code by auth state (`HEAD /v2/komodor-public/telegraf/manifests/sha256:e9ed55dccc73b5c399c59cc1db44ddef2d14325bd3d0e3cf78ffb2f034ad0e2b`):
| Authorization presented | Response | `WWW-Authenticate` |
|---|---|---|
| (none) | `401 Unauthorized` | `Bearer realm="https://public.ecr.aws/token/",service="public.ecr.aws",scope="aws"` ✅ |
| valid token | `200 OK` | — |
| expired / invalid token | **`400 Bad Request`** | **(none)** ❌ |
Expected behavior:
- Expired / invalid / missing token → `401 Unauthorized` + `WWW-Authenticate: Bearer realm=…,service=…,scope=…` (RFC 6750; ideally `error="invalid_token"` for the expired case).
- Never `400 Bad Request` for an expired/invalid bearer token on an otherwise well-formed request.
The no-token path already returns the correct `401 + challenge` (see table) — aligning the expired/invalid-token path with it fully resolves the issue.
**Are you currently working around this issue?**
No — not currently working around this issue.
**Additional context**
**Attachments**
Contributor guide
Research direction
Start by reproducing the documented HEAD request to public.ecr.aws and compare the no-token, valid-token, and expired or invalid-token responses. Read the Docker Registry v2 bearer-token flow and RFC 6750 expectations. Done means expired or invalid tokens return 401 Unauthorized with the WWW-Authenticate Bearer challenge rather than 400 without a challenge.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- aws, docker
- Domain
- api, authentication, cloud
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100