A post-discovery issuer refusal reports auth.discovery_failed: decide whether the stable code list may grow
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 0
- Forks
- 3
- Avg merge
- 8h 43m
- Merged PRs (30d)
- 60
Description
Problem
internal/auth/source_clientcred.go:125 refuses with auth.discovery_failed on a branch reached only after discovery has demonstrably succeeded.
Execution arrives there having already: read the OIDC discovery document, resolved the token endpoint from it, sent a client_credentials request to that endpoint, and received an answer. The deployment refused, in terms refusedGrant has no rule for — not refusedToNarrow(), not rejectedClient(). Every step proves discovery worked. "Discovery failed" is the one thing that certainly did not happen.
Why the wrong code costs something
The code is not cosmetic. Section 8 of the login walkthrough keys a troubleshooting entry off it, and that entry sends a reader to check:
- the issuer string, character for character, against the discovery document
- TLS trust for the deployment's certificate
- proxy, VPN and firewall reachability
- whether the issuer advertises
S256
All four are about reaching and reading the issuer. A CI engineer who lands on this branch will check their issuer URL, their trust store and their network, and find every one of them healthy — because the issuer answered.
The refusal's own recovery text already says the right thing ("check that this context's OAuth application is registered for the client-credentials grant and carries the permissions the command needs"). So the prose points one way and the code points another, and the code is what a script branches on.
The inconsistency
The other three uses of auth.discovery_failed agree with each other:
| Site | Condition |
|---|---|
internal/auth/discovery.go:52 |
the discovery document could not be read |
internal/auth/oauthflow/login.go:297 |
the same, on the login path |
internal/auth/tokenrequest.go:168 (issuerUnreachable) |
the token endpoint could not be reached |
Each means the shell never obtained an answer. The client-credentials branch is the outlier: it obtained one and could not classify it.
Worth noting how the browser path solves the same problem. internal/auth/source_browser.go:154 has the identical unclassifiable-refusal branch and returns auth.login_required, because a browser identity always has something to do — log in again. A client-credentials identity has no such fallback: wso2 login refuses that kind outright, so pointing there would leave the reader with nothing actionable. That is why this branch reached for a general-purpose code, and why it reached for the wrong one.
The decision
The accurate fix is a distinct code — auth.access_not_issued, or whatever name survives review — meaning the deployment answered and declined to issue access, for a reason the shell cannot classify.
That is a change to a contract, not a rename. Issue #17 defines the problem codes as a closed, stable list, and its user story 21 makes that stability the point: automation branches on these codes to tell "you may not" from "it is broken" without parsing prose. So the question this issue exists to settle is not really the name:
Under what rule may the stable code list grow? A code added for a condition that genuinely has no existing home is a different act from a code added because an existing one reads awkwardly. Whatever is decided here should be written down where the code list lives, so the next case does not re-open it.
Options, in the order I would consider them:
- Add
auth.access_not_issuedand record the rule that admitted it. Scripts matchingauth.discovery_failedtoday would stop matching this branch — a behavior change for anyone already handling it, though the slice has not shipped tomainyet, so the window for making it cheaply is now. - Keep the code, fix the guide. Give section 8's
auth.discovery_failedentry a fourth case covering the post-discovery refusal. Costs nothing, leaves a script unable to tell an unreachable issuer from a refusing one. - Reuse an existing code.
auth.narrowing_unavailableandauth.credential_unavailableare the near neighbours, and both are wrong here — that is precisely what the two branches above this one already ruled out.
Scope
Severity is low. The refusal is correct, the exit class is correct, and the recovery text is correct. Only the code is wrong, and only on one branch — but it is wrong in the direction that sends someone to inspect infrastructure that is working.
If option 1 is taken, section 8 of docs/guides/login.md gains an entry, and the acceptance tests around the client-credentials path pin the new code.
Found by review on #38, where it was deferred as a design change rather than a review fix.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with issue #17 and the refusal branches in internal/auth/source_clientcred.go, comparing them with internal/auth/discovery.go, internal/auth/oauthflow/login.go, and internal/auth/source_browser.go. Review the client-credentials acceptance tests and docs/guides/login.md. Done means the rule for extending the stable code list is decided and recorded, with the selected behavior and documentation covered by tests.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- authentication, cli, documentation, testing
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100