Altinity / Altinity/acmctl

OAuth: /singleauth rejects id_tokens minted by Native CLI client

Open
#1 0 comments 0 reactions 0 assignees View on GitHub

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

  1. A Native Auth0 application is configured on altinity.auth0.com:

  2. acmctl oauth does the standard authorization-code-with-PKCE flow:

    • Generates code_verifier, S256 code_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 (with code_verifier) and gets a valid id_token
  3. acmctl POSTs the id_token to ACM:

    POST /api/singleauth
    Content-Type: application/x-www-form-urlencoded
    token=<id_token>
    
  4. 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:

  1. Web UI calls GET /api/singleauth → ACM generates state + code_verifier server-side, returns the Auth0 authorize URL
  2. Browser does the Auth0 dance, lands on https://acm.altinity.cloud/singleauth?code=...&state=...
  3. Web UI POSTs that code+state to /api/singleauth
  4. 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.com JWKS
  • Verify aud matches an allow-list of trusted CLI client IDs (start with BjVCqpdbU4Z6zYpxHPXG1zU4N6QCIlGX)
  • Verify exp > now, iss == https://altinity.auth0.com/
  • Look up the user by Auth0 sub claim (or email if sub isn'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-facing acmctl oauth command
  • 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

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.