OAuth: /singleauth rejects id_tokens minted by Native CLI client
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 0
- Forks
- 0
- Avg merge
- 9m
- Merged PRs (30d)
- 2
Description
Summary
acmctl oauth (browser-based PKCE flow against altinity.auth0.com) is fully implemented client-side and reaches a valid Auth0 id_token, but ACM's POST /api/singleauth rejects that token with HTTP 403 Bad credentials.
Because of this, acmctl oauth cannot complete the login until the ACM backend is taught to accept Native-client tokens.
Reproducer
-
A Native Auth0 application is configured on
altinity.auth0.com: -
acmctl oauthdoes the standard authorization-code-with-PKCE flow:- Generates
code_verifier, S256code_challenge, random state - Listens on
http://localhost:49152/cb - Opens browser to
https://altinity.auth0.com/authorize?...&client_id=BjVCq... - User signs in via Google SSO
- Auth0 redirects to the localhost callback with
?code=...&state=... - acmctl exchanges code at Auth0
/oauth/token(withcode_verifier) and gets a validid_token
- Generates
-
acmctl POSTs the id_token to ACM:
POST /api/singleauth Content-Type: application/x-www-form-urlencoded token=<id_token> -
ACM responds:
HTTP/2 403 {"error":"Bad credentials"}
We also tried {token, code, state} (full PKCE shape) — same Bad credentials.
What's happening
/singleauth is built for ACM's own server-side OAuth flow:
- Web UI calls
GET /api/singleauth→ ACM generates state + code_verifier server-side, returns the Auth0 authorize URL - Browser does the Auth0 dance, lands on
https://acm.altinity.cloud/singleauth?code=...&state=... - Web UI POSTs that code+state to
/api/singleauth - ACM looks up the state in its server-side store, exchanges code with Auth0 using the stored verifier, mints a session token
For a CLI-initiated PKCE flow, none of that state exists in ACM. The id_token we hold is signed by the same Auth0 tenant but was minted by a different client (the Native CLI app), so ACM's signature/audience check rejects it.
Proposed fix
Pick one — both unblock acmctl with similar effort:
A) Accept Native-client id_tokens on /singleauth (smallest change)
Extend the existing endpoint to accept a {token: <id_token>}-only request shape:
- JWT-validate against
altinity.auth0.comJWKS - Verify
audmatches an allow-list of trusted CLI client IDs (start withBjVCqpdbU4Z6zYpxHPXG1zU4N6QCIlGX) - Verify
exp > now,iss == https://altinity.auth0.com/ - Look up the user by Auth0
subclaim (oremailifsubisn't mapped) - Issue an ACM session token, return as today
B) New endpoint /api/cli-auth (cleaner isolation)
Same shape as A, but a separate code path so /singleauth's existing behavior is untouched. Slightly more surface area for slightly less coupling.
Why this is OK security-wise
Trusting id_tokens from a specific Native client_id is the standard pattern. The id_token's signature proves it was issued by altinity.auth0.com; the aud claim proves it was minted for our specific Native app; the audit trail of who can authenticate to that app lives in Auth0's own connection allow-list. All major auth providers (Auth0, Okta, Azure AD) document this pattern for CLI tools.
Acmctl-side status
The CLI code is committed and works up to the /singleauth call:
pkg/api/oauth.go— the OAuth flow (PKCE + loopback listener + Auth0 token exchange)cmd/oauth.go— the user-facingacmctl oauthcommand- Profile machinery (so the resulting token saves to the right
prod/dev/stage profile) is independently working
When this issue is fixed and ACM accepts the id_token, no acmctl change should be needed beyond what's already on master (commits 29baec2, 35424e3).
Workaround until fixed
Use ACM_API_KEY from 1Password (resolve at shell start), or acmctl login --token <key> to write a manually-minted API key into the profile. acmctl oauth errors with the explicit "Bad credentials" message and a hint pointing at this issue.
Contributor guide
No contributing guide indexed for this repository
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 by reading pkg/api/oauth.go and cmd/oauth.go to confirm the token request and the existing acmctl OAuth flow. Then inspect the ACM implementation of POST /api/singleauth and its authentication tests; done means a trusted Native-client id_token is accepted and acmctl oauth completes login without the 403 response.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- api, authentication, backend
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100