Sign in from a command-line tool
The device flow for CLIs: the user approves in a browser, tokens persist in ~/.config/anchring and refresh on their own. Personal access tokens for CI.
Register a public, device grant app (kind cli) with your API's audience, e.g. acme-api.
import { createAuth } from '@anchring/auth/cli';
const auth = createAuth({ project: 'acme', env: 'test', clientId: 'test_…' }); // the CLI app's client id
await auth.device.signIn({ open: true });
// Open acme.test.anchr.ing/device and enter ABCD-1234
await auth.fetch('https://api.acme.example/reports');
await auth.signOut();The user opens the printed address in any browser, signs in (passkey, password, email code) and approves the code. Tokens are stored in ~/.config/anchring/tokens.json (respects XDG_CONFIG_HOME; file 0600, atomic writes) and refreshed automatically; processes sharing the file refresh one at a time under a lock file. Pass storage to use the OS keychain instead.
device.signIn options: onCode (show the code yourself), open (launch the browser; only https: URLs, or http: on loopback, never through a shell), scope and signal (cancel).
Not signed in
import { createAuth, SignInRequiredError } from '@anchring/auth/cli';
try {
await auth.fetch('https://api.acme.example/reports');
} catch (e) {
if (e instanceof SignInRequiredError) console.error('Not signed in: run `acme login`.');
else throw e;
}CI and scripts
For CI, users create a personal access token on their account page (name, scopes, at most a year) and your CLI or script sends it as a bearer. The API accepts it when its middleware has pat: { audience } (see Protect your API).
ACME_TOKEN=kh_pat_test_… # created on the user's account page
curl -H "Authorization: Bearer $ACME_TOKEN" https://api.acme.example/api/reportsEvery option, error and token type: the SDK README.