Authfu

Reference · CLI

The authfu CLI

Everything an app needs — creation, configuration, roles, people, groups, agent identities — from the terminal your coding agent already lives in.

The whole loop

bash
authfu login
authfu init                     # detects the framework, creates the app, writes the auth files
authfu roles add "Editor" --description "Can change content"
authfu roles assign teammate@example.com editor

Authenticate, let authfu init wire the repository up, define a role, and grant it by email — the person does not need an account yet; the role is theirs at first sign-in.

Install

bash
npm install -g @authfu/cli   # or: npx @authfu/cli <command>

Scopes

A key carries only the scopes it asked for. The default three — profile:read, apps:read, apps:write — deliberately do not include the newer ones, so a key that can create apps cannot by itself grant people access to them, read your audit trail, or establish a SAML federation trust.

bash
authfu login --scope profile:read,apps:read,apps:write,\
groups:read,groups:write,assignments:read,assignments:write

Add audit:read to read the audit trail, and saml:read,saml:write to manage SAML service providers.

Commands

CommandWhat it does
authfu loginBrowser handshake; key stored at ~/.authfu/config.json (0600), revocable in the console. --scope asks for more than the default three.
authfu initWire this repository up: detect the framework, create or reuse the app, write the auth files, the env names, and the gitignore entry. Re-running converges — it never mints a second app or duplicates a redirect URI. --dry-run shows the plan and writes nothing.
authfu appsList your applications; * marks the one linked to this directory.
authfu apps create [name]Create an app; prints the client ID and one-time secret. --link binds the directory.
authfu link <client-id>Bind this directory to an app. The project file holds only the public client ID — commit it.
authfu setUpdate the linked app: --name --description --homepage --logo --redirect-uri…
authfu rolesList the app’s roles with holder counts. The slug is the exact roles-claim value.
authfu roles add <name>Create a role (--description optional).
authfu roles assign <email> <role>Grant a role by email — works before the person has signed up. Additive; unknown roles are refused.
authfu groupsList the groups you administer. groups create, groups delete, and groups members add|remove manage them.
authfu apps assign <client-id>Give a group or a person access: --group <name|id> or --user <email>, with --role repeatable. Assigning an address with no account yet returns pending, not a failure.
authfu apps unassign <client-id>Remove that grant. Reports whether anything was actually removed.
authfu apps assignments [client-id]Who can reach an app and with which roles — groups, people, and pending invitations in one table.
authfu access explain <email>Why one person can or cannot reach the app. See below.
authfu agent loginDevice flow for a coding agent: you approve with your authenticator; the token says sub=you, act=the agent.
authfu agent token / status / logoutPrint a valid token for scripting, list sessions, or revoke server-side and forget locally.

Explaining access

“Why can’t they sign in?” has two very different answers, and the remedies are nothing alike: nobody granted them anything is a setup gap you close with one command, and somebody switched them off is a decision to reverse. Guessing between them is how an afternoon goes into re-assigning an app to a suspended employee. access explain says which it is, and never makes you infer it.

bash
$ authfu access explain teammate@example.com --app app_1
DENIED    teammate@example.com -> Fite Camp
  cause     configuration — nothing grants them access to this app
  reason    not_invited
  detail    The app is invite-only and they hold no invitation.
  direct    no direct assignment
  groups    no group grants them access
  launcher  no tile (not_in_launcher)
  fix       authfu apps assign app_1 --user teammate@example.com --role <role>

It reports the door and the launcher tile separately, and flags a MISMATCH when the two disagree rather than averaging them into one confident half-answer. Under --json the provider’s own document rides along untouched, so nothing is lost to a field the CLI did not render.

Machine-readable output

Every command takes --json: exactly one JSON document on stdout and nothing else, with every human word moved to stderr. Errors carry a stable code and a stable exit status, so a script can branch on the failure rather than parse prose. Secrets are redacted from every stream — including an error body that echoes one back.

Writing prompts that use it

Because apps, roles, groups and people are all CLI-reachable, a prompt like “add sign-in with administrator and editor roles, protect /admin for administrators, and make me an administrator” is fully executable by a coding agent — the machine-readable rules live in llms.txt.

Next steps