A command line tool sits in an awkward position. It acts on a person's behalf, so it needs their authority. It runs on a machine you do not administer, alongside software you cannot see, and anything shipped inside the binary can be read by anyone who downloads it. It is a public client in the strict sense: it cannot hold a secret.
That constraint decides the whole design. The tool must obtain a credential through an interaction the person completes in a browser, store it somewhere with real protection, and be revocable from a screen far away from the laptop it is sitting on.
Loopback with a proof key is the default
The standard flow for a tool on a machine with a browser is to start a temporary listener on a local port, open the system browser to the authorisation page, and receive the response back on that listener. The person authenticates in a real browser window, with their existing session, their password manager and their second factor all working normally.
The proof key is what makes it safe without a client secret. The tool generates a random verifier, sends its hash with the request, and presents the original when exchanging the code. Another process on the same machine that intercepts the redirect cannot complete the exchange, because it does not have the verifier. Bind the response with a state value as well, generated per attempt and checked on return, and keep the listener open only for the duration of the attempt.
- Bind the port at the moment of use and register the redirect with a loopback address
- Generate a fresh proof key and state per attempt; never reuse either
- Reject a callback whose state does not match, without explaining why on the page
- Close the listener as soon as the code arrives, or after a short timeout
When the device flow is the right fallback
Some machines have no browser and no way to open one: a server over a remote shell, a container, a build agent. There the flow inverts. The tool displays a short code and a URL, the person opens them on a device that does have a browser, and the tool polls until the authorisation completes.
It is the correct answer for that situation and a poor default for a laptop, because it asks the person to read a code across to another device and it is the flow most susceptible to being talked through by somebody on a phone call. Detect the environment and prefer loopback wherever a browser can be opened, keeping the device path for the case that needs it and making the confirmation screen state clearly which tool is being authorised and from where.
Prefer the flow that keeps the person in a real browser. Reading a code to another device is a fallback, not a design.
Where the token lives is the part that gets skipped
The flow is standard and most implementations get it right. Storage is where the differences show up. A token written to a file in the home directory with default permissions is readable by every process that runs as that user, gets copied into backups, and ends up in a dotfiles repository roughly as often as you would fear.
Use the operating system credential store where one exists, which is the whole point of those APIs, and fall back to a file with restrictive permissions only when it does not. Keep separate profiles rather than one global credential, because people work across several accounts and a single slot leads to authenticating in a loop. And never accept a token as a command line argument, since arguments are visible to other processes and land in shell history; an environment variable or standard input is the correct channel.
- Prefer the platform credential store; use a permission-restricted file only as a fallback
- Support named profiles so several accounts can coexist without re-authentication
- Accept a token from the environment or standard input, never from an argument
- Print the account and environment on every authenticated command that changes something
Continuous integration wants a different credential
The credential a person obtains interactively belongs to that person. Using it in a pipeline means the build is running as an individual, it stops working when they leave, and the audit trail attributes automated actions to somebody who was asleep at the time. It is the most common cause of a mysteriously broken pipeline six months after a departure.
Issue a distinct credential type for non-interactive use: owned by the organisation rather than a person, scoped to what the pipeline actually does, individually revocable and separately visible in the audit trail. Then give both types a management screen listing every active credential with the machine and the last time it was used. Being able to look at that list and revoke the entry for a lost laptop, without touching anything else, is the point of the whole exercise.
Everything here, already built
Sign-in, enterprise SSO, directory provisioning, roles and an audit trail behind one API. Start with the quickstart and have a working sign-in this afternoon.