Authentication

Before any command reaches the FerrisKey API, ferris-ctl resolves where to connect, who you are, and which realm to operate on. This page explains each piece and the order in which they’re resolved.

Contexts

A context is a named connection profile. It holds:

FieldRequiredDescription
urlyesFerrisKey server URL
client-idyesOAuth2 client used to authenticate
client-secretnoSet only for confidential clients / service accounts
realmnoDefault realm for commands that need one

Contexts are stored as TOML at $XDG_CONFIG_HOME/ferriskey/config.toml (typically ~/.config/ferriskey/config.toml). The active context is tracked by current-context.

current-context = "local"

[contexts.local]
url = "http://localhost:3333"
client-id = "ferris-ctl"
realm = "master"

Manage contexts with the context command. Any field can be overridden per-command with --url, --client-id, --client-secret, --realm, or the matching FERRISKEY_* environment variable.

Two ways to authenticate

ferris-ctl supports two credential modes. You pick one when you set up a context.

Interactive login

Run ferris-ctl login to sign in as a user via the Device Authorization Grant. Best for humans and public clients.

Client credentials

Add a context with --client-secret. The CLI obtains a token with the OAuth2 client-credentials grant. Best for CI and service accounts.

Interactive login (Device Authorization Grant)

ferris-ctl login

This runs the OAuth 2.0 Device Authorization Grant (RFC 8628): the CLI requests a device code, shows you a verification URL and user code, opens your browser, and polls until you approve. On success it writes tokens to credentials.toml.

server_url = "http://localhost:3333"
realm = "master"
client_id = "ferris-ctl"
access_token = "eyJ..."
refresh_token = "..."
token_type = "Bearer"
expires_in = 300
obtained_at = 1700000000
scope = "openid profile email"

Protect the credentials file

credentials.toml contains bearer tokens. On Unix it is written with 0600 permissions (owner read/write only). Treat it like an SSH private key. Never commit it or share it.

Client credentials

For non-interactive use, store a client secret in the context:

ferris-ctl context add ci \
  --url https://auth.example.com \
  --client-id automation \
  --client-secret "$FERRISKEY_CLIENT_SECRET" \
  --realm master

No login step is needed. The CLI exchanges the secret for a token on each run.

Resolution order

When a command needs an access token, ferris-ctl resolves credentials in this order:

Reuse or refresh a login session

If credentials.toml exists and its (server_url, realm, client_id) matches the active context, the saved access_token is reused while it is still valid. When it has expired, the CLI silently refreshes it with the stored refresh_token and saves the new tokens, so you don’t have to log in again.

Fall back to client credentials

Otherwise, if the context has a non-empty client-secret, the CLI performs an OAuth2 client-credentials grant.

Fail with guidance

If neither is available, the command fails with: no credentials available: run 'ferris-ctl login' or add a context with --client-secret.

To discard the saved session, run ferris-ctl logout. It deletes credentials.toml; the next command then falls back to client credentials or asks you to log in again.

Environment variables

Every connection setting can be supplied via the environment, which is convenient in CI:

VariableOverrides
FERRISKEY_URL--url
FERRISKEY_CLIENT_ID--client-id
FERRISKEY_CLIENT_SECRET--client-secret
FERRISKEY_REALM--realm
XDG_CONFIG_HOMEconfig & credentials directory