Skip to navigation

Authentication

Beta
View as Markdown
Early access

The CLI generator is in early access. Reach out to get started.

Each generated CLI reads authentication credentials from the security schemes declared in your OpenAPI spec. Credentials can come from environment variables, CLI flags, files, or a combination of these through fallback chains.

Without a credential, the CLI still works — you can explore the command tree, view help, and use --dry-run.

Credential sources

The CLI supports several ways to supply credentials, configured at build time.

SourceDescription
Environment variableRead from an env var (the most common option).
CLI flagAuto-registered as a --<flag-name> global flag.
FileRead trimmed contents from a file path (~ is expanded).
LiteralBaked into the binary at compile time.
Fallback chainTry multiple sources in order; first non-empty value wins.

A typical fallback chain lets the CLI flag override the env var, which in turn overrides a file:

# CLI flag takes priority
box users get-current-user --api-token sk-123
# Otherwise falls back to the environment variable
export BOX_API_KEY=sk-123
box users get-current-user
# Otherwise reads from a file
echo "sk-123" > ~/.box/token
box users get-current-user

Supported auth schemes

The CLI supports every scheme type that OpenAPI’s securitySchemes defines:

SchemeHow the CLI applies it
Bearer (http: bearer)Sends Authorization: Bearer <token>.
API key (apiKey)Sends the key in the configured header (for example, X-Auth-Token).
Basic (http: basic)Sends Authorization: Basic <base64(user:pass)>. Each field has its own credential source.
OAuth 2Sends Authorization: Bearer <token>. Declare an OAuth flow to have the CLI obtain and refresh the token itself.

OAuth flows

Declaring an oauth scheme under auth-schemes in generators.yml makes the CLI acquire tokens on its own instead of reading a pre-issued token from the environment. Three flows are supported through the type field:

FlowtypeUse case
Client credentialsclient-credentialsMachine-to-machine. The CLI exchanges a client ID and secret from environment variables for a token, configured with the same auth-schemes fields the SDKs use.
Authorization code with Proof Key for Code Exchange (PKCE)authorization-codeInteractive login. The CLI opens a browser and receives the callback on a loopback listener.
Device codedevice-codeInteractive login on machines without a browser, such as SSH sessions and containers.

The interactive flows are public-client only: they use PKCE rather than a client secret. They require CLI generator 0.29.0 or later.

Log in and out

Every generated CLI exposes an auth command group:

my-cli auth login # run the declared flow and store the token
my-cli auth status # show each scheme and whether a credential is present
my-cli auth logout # remove the stored credential

Tokens are stored in the OS keyring, refreshed automatically when they expire, and sent as Authorization: Bearer <token> on every request. Pass --no-browser to auth login to print the authorization URL instead of opening a browser, and --with-token to skip the flow and read a token from stdin.

Authorization code with PKCE

generators.yml
auth-schemes:
OAuth:
scheme: oauth
type: authorization-code
client-id: my-public-client-id
authorization-url: https://idp.example.com/authorize
token-url: https://idp.example.com/oauth/token
refresh-url: https://idp.example.com/oauth/token
scopes:
- plants:read
- plants:write

Omitting redirect-uri binds an OS-assigned loopback port at login (recommended, no port registration needed). To pin a port, set redirect-uri to a loopback URL, and list ports to add fallbacks tried in order when the primary port is busy:

generators.yml
redirect-uri:
url: http://127.0.0.1:8484/callback
ports: [8485, 8486]

The host must be 127.0.0.1 or localhost over http, the port is required, and the path is arbitrary. Register every redirect URI the CLI can produce, including each backup port, with the authorization server.

Hosted callback pages

Once login finishes, the loopback listener renders a built-in page for the success and failure states. success-redirect-url and error-redirect-url hand that last screen off to pages you host instead:

generators.yml
success-redirect-url: https://example.com/app/oauth/cli/success
error-redirect-url: https://example.com/app/oauth/cli/error

Both take an absolute http or https URL. The success redirect forwards no query parameters, since the credential is already captured on the loopback. The error redirect appends error and, when the authorization server sent one, error_description, so the hosted page can explain the failure:

https://example.com/app/oauth/cli/error?error=access_denied&error_description=User+denied+the+request

Neither URL is the OAuth redirect_uri, so neither needs registering with the authorization server. Omitting both keeps the built-in pages.

Device code

generators.yml
auth-schemes:
OAuth:
scheme: oauth
type: device-code
client-id: my-public-client-id
device-authorization-url: https://idp.example.com/oauth/device/code
token-url: https://idp.example.com/oauth/token

auth login prints a user code and verification URL, then polls the token endpoint until the user approves. redirect-uri and pkce are rejected for this flow.

Extra request parameters

Authorization servers that require additional literal parameters, such as an Auth0 audience, accept them through per-request maps.

generators.yml
authorization-parameters:
audience: https://api.example.com
token-parameters:
audience: https://api.example.com
refresh-parameters:
audience: https://api.example.com

The device-code flow uses device-authorization-parameters in place of authorization-parameters.

Auth strategies

When a spec declares multiple security schemes, the CLI composes them according to one of these strategies:

StrategyBehavior
AutoDefault. Infers the right composition from the spec’s security blocks.
AnyThe API accepts any one of the declared schemes. The first scheme with a credential wins.
AllThe API requires every scheme simultaneously (for example, HMAC signature plus API key).
RoutingPer-operation dispatch. Each endpoint’s security block determines which schemes to use.

Operations that declare security: [] (an empty list) opt out of authentication entirely — no credentials are sent regardless of what’s configured.

Configure the any strategy

When an API accepts more than one credential under the any strategy, the CLI authenticates with whichever source is populated, using the first scheme that has a credential. Declaring the schemes requires two steps:

  1. In your OpenAPI spec, define the schemes under securitySchemes and list them as multiple auth schemes in the security array.

    openapi.yml
    components:
    securitySchemes:
    # ...BearerAuth and TokenAuth defined here
    security:
    - BearerAuth: []
    - TokenAuth: []
  2. In generators.yml, define the same schemes under auth-schemes and compose them with api.auth set to any.

    generators.yml
    auth-schemes:
    # ...BearerAuth and TokenAuth defined here, each with its env var
    api:
    auth:
    any: [BearerAuth, TokenAuth]

The scheme names must match across both files.

A scheme that’s missing from the spec is ignored without warning, even when its environment variable is set. With both declared, set either variable and the command runs:

# Authenticate with MY_API_KEY
export MY_API_KEY=sk-123
my-cli users list
# Or authenticate with MY_TOKEN instead
export MY_TOKEN=tok-456
my-cli users list

Named profiles

For APIs where every call carries a tenant identifier (an account SID, an org slug, a workspace), a profile stores it once instead of it being typed on every command. A profile is a named bundle of request context resolved once per invocation: a credential, parameter defaults, server URL variables, an optional base URL, a retry limit, and a default output format. Nothing else is accepted; an unknown key is rejected at write time rather than stored and ignored.

Profiles are off by default. Enable them with the profiles config option in generators.yml, which adds a profiles command group and a global --profile / -p flag to the generated CLI. A CLI generated without the option is unchanged.

generators.yml
config:
binaryName: acme
profiles:
enabled: true

Manage profiles

acme profiles create prod --set AccountSid=AC11… --with-token # token read from stdin, stored in the OS keychain
acme profiles create sub --parent prod --set AccountSid=AC99… # subaccount that shares prod's credential
acme profiles set prod ACME_REGION=us1 ACME_RETRIES=3 # add or change values by env var name
acme profiles use sub # make it the active profile
acme messages list # no --account-sid needed
acme messages list -p prod # one command against another tenant; the active profile is unchanged
acme profiles list # every profile, with the active one marked
acme profiles current # the profile in effect and how it was selected
acme profiles show staging # one profile's resolved config without selecting it
acme profiles remove sub # also deletes that profile's stored credential

profiles create accepts --set <name>=<value> for API parameters (validated against the spec, so a typo is rejected), --server-var <name>=<value> or --<name> <value> for server URL variables, --base-url, --retries <N>, --default-format, --parent <name>, and --with-token to read a credential from stdin. When several auth schemes are declared, --scheme names the one the credential belongs to.

profiles set <name> KEY=VALUE ... takes any environment variable the CLI reads (a credential such as ACME_AUTH_TOKEN, or a setting such as ACME_RETRIES, ACME_BASE_URL, ACME_OUTPUT, or ACME_<SERVER_VAR>) or an API parameter by its spec name, and creates the profile if it doesn’t exist. Keys are validated before anything is written, and an unrecognized key fails with a suggestion. auth login --from-env captures the credential from the CLI’s own environment variables into the active profile.

Select a profile

A profile is selected by, in order, --profile / -p, the <PREFIX>_PROFILE environment variable, then the profile marked active by profiles use, where <PREFIX> is the binary name uppercased with - replaced by _. With no profile selected, the CLI behaves as if the feature were absent. A named profile that doesn’t exist is an error rather than a fallthrough to environment credentials, so a command never silently runs against the wrong tenant. The profiles group itself always runs unprofiled, so a stale active pointer can be repaired.

Scripts and agents should pass -p per invocation: it mutates no global state, so parallel invocations can’t race.

for tenant in prod sub; do acme messages list -p "$tenant" --format json; done

Precedence

With a profile selected, each value resolves as explicit flag → selected profile → environment variable → spec default. The order is the same however the profile was selected: -p, <PREFIX>_PROFILE, or profiles use. The profile’s stored values win, and environment variables fill only the values the profile leaves unset. With no profile selected, values resolve as explicit flag → environment variable → spec default. A CI pipeline that exports ACME_API_KEY or ACME_BASE_URL should therefore select no profile; if one is selected, its stored values take effect over the exported variables. Retries resolve as --retries → profile → <PREFIX>_RETRIES → x-fern-retries, and --retries 0 is equivalent to --no-retry. profiles current reports when an environment variable is supplying a credential the selected profile doesn’t store, and auth status lists the profile’s keyring credential before the environment variable.

Storage and inheritance

Non-secret settings live in ~/.config/<bin>/profiles.toml. Secrets are never written to that file: credentials go to the OS keychain under a profile-scoped account, the same store auth login uses.

--parent <name> sets single-level inheritance. A child inherits its parent’s credential, base URL, server variables, parameter defaults (child wins per key), and retries. It doesn’t inherit the default output format, since output shape belongs to the invocation rather than the tenant. Because the parent is resolved at read time, editing it propagates to its children.

Help output

Every generated CLI includes a dynamically rendered Authentication: section in its --help output listing every scheme, the expected env var or flag, and whether a credential is detected.