🔑 feat: let estela-cli send people here to create its key - #304
Merged
Merged
Conversation
erick-GeGe
force-pushed
the
feat/api-keys-cli
branch
from
September 25, 2026 16:08
cc6138c to
16e7ea4
Compare
This was referenced Sep 28, 2026
joaquingx
approved these changes
Sep 28, 2026
`estela login` will open /settings/apiKeys?cli=1. That opens the create form already filled in — named after the signed-in user, carrying the scopes the CLI needs — and waits for a click. It never creates the key on arrival: a link someone sends you must not mint a credential by itself, which is why GitHub prefills a PAT form rather than generating one. The duration is left out of the request on that path, so the key inherits `API_KEY_DEFAULT_DAYS`. Change that setting and the CLI hands out the new period without either side being touched. Nothing about the machine travels in the URL — the name is built from the session — so there is no untrusted input to validate. Also fixes a gap the flow would have fallen into: PrivateRoute sent people to /login without remembering where they were headed, and LoginPage always landed on /projects, so anyone not signed in lost the flow on the way. Both now carry a `next`, accepted only when it is a same-site path — "//host" reads as another origin to the browser, so it is rejected along with absolute URLs. This helps any deep link, not just this one.
An API key carries no username, so a CLI holding one has no way to say who it is connected as — and "the key was accepted" is a weaker thing to tell someone than "you are admin". GET /api/auth/whoami answers with the username and email behind whatever credential was used, key or session. It follows the shape already used by the password-change action in this file: authentication declared on the action rather than the viewset, since the rest of it is public. estela set-token calls it to validate the key and name the account in one round trip.
…piry The deploys page still taught the password login, down to a `Password:` line. It now shows set-host, the optional create-api-token, and set-token — the same in both the demo and the bring-your-own-project column, which were identical copies of each other. The form arriving from ?cli=1 locked the expiry to whatever the server defaults to, with nothing to change it. There is no reason a CLI key cannot last a week if that is what someone wants, so it gets the same selector as every other key, preselected at 90 days. The prefilled name loses its spaces: `estela-cli@erick` rather than `estela-cli @ erick`, which reads better next to a hostname and copies as one word.
The key modal told people to run `estela login <key>`, which is not a command — and putting a credential on a command line is the one shape worth avoiding: it lands in shell history and shows up in the process list while it runs. It now names `estela set-token`, which asks for the key without echoing it. The copy button above already put the key on the clipboard, so there is nothing to retype.
A key carries no visible permissions, so a program holding one cannot tell whether it is about to be refused until it tries — which is how a read-only key gets as far as uploading a project before the API turns it away. whoami now returns the scopes of the key that authenticated the call, so a client can say up front what it will and will not be able to do. The field is absent for a session, which has no scopes to report.
A key with the `run` scope still gets nowhere if its owner is a viewer on the project, and until now that produced DRF's default "You do not have permission to perform this action" — indistinguishable from the key simply lacking the scope, which is a different problem with a different fix. IsAdminOrReadOnly now names what is actually in the way, so the two read apart: Your role on this project does not allow this action. This API key does not have the required scope. Sessions get the clearer wording too; the old text never said what to change.
docs/api.yaml predated the whoami endpoint, so the schema and the generated TS client were missing both the route and the WhoAmI definition — including its scopes field, which is what tells a client what a key may do.
erick-GeGe
force-pushed
the
feat/api-keys-cli
branch
from
September 28, 2026 18:05
a86eb18 to
580d8ea
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
estela loginsends a username and password, gets the Django token back, and writes it to~/.estela.yamlin plain text at mode0664. That token is also the browser session, so it cannot be rotated without logging the person out.It is also why the captcha had to be switched off: a captcha exists to tell humans from programs, and the CLI is a program. The captcha is not the bug — machines having no door of their own is.
This builds the door on estela's side. The CLI changes come later, in its own repo.
What
estela loginwill open/settings/apiKeys?cli=1, which opens the create form already filled in:One click. It never creates the key on arrival — a link someone sends you must not mint a credential by itself, which is why GitHub prefills a PAT form rather than generating one.
After creating, the result modal adds the line to paste:
Two details worth naming:
API_KEY_DEFAULT_DAYS. Change that setting and the CLI hands out the new period without either side being touched.A gap this flow would have fallen into
PrivateRouteredirected to/loginwithout remembering where you were headed, andLoginPagealways landed on/projects. Anyone not already signed in would open the CLI's link, log in, and arrive at the project list with the flow gone.Both now carry a
next, andsafeNextPathaccepts it only as a same-site path:?next=/projects/settings/apiKeys%3Fcli%3D1/settings/apiKeys?cli=1/projects/abc/projects/abc//evil.com/projectshttps://evil.com/projects//hostis rejected alongside absolute URLs because the browser reads it as another origin — without that check the parameter is an open redirect.This helps any deep link, not just this one.
Still to come, in the estela-cli repo
estela set-host,estela login <key>,ESTELA_API_KEYfor CI,~/.estela.yamlat0600, andlogoutleaving the key alone — expiry now puts a ceiling on it. Then the captcha can go back on.Verified
yarn lint,tsc --noEmitandyarn buildpass, and the redirect table above was checked against the guard.