Authentication
Authenticate the CLI with your BroomVA account using device-code flow or manual tokens.
Authentication
The CLI authenticates with the BroomVA platform to access your prompts, skills, and organization resources. Two authentication methods are supported.
Device authorization flow (recommended)
The primary authentication method uses the OAuth 2.0 Device Authorization Grant (RFC 8628). This is the same flow used by GitHub CLI, Azure CLI, and other developer tools.
broomva auth loginThe flow works as follows:
- The CLI sends
POST /api/auth/device/codewithclient_id: "broomva-cli" - The server generates a device code (64-hex-char random string) and a user code (format:
ABCD-1234) - The CLI displays both and asks you to open the verification URL in your browser
Open this URL in your browser:
https://broomva.tech/device
Then enter this code:
ABCD-1234
Or open the direct link:
https://broomva.tech/device?code=ABCD-1234
Waiting for authorization...- You open the URL, sign in (if not already), and enter the user code
- The CLI polls
POST /api/auth/device/tokenwithgrant_type: "urn:ietf:params:oauth:grant-type:device_code"until you approve - On approval, the server signs a JWT via
signLifeJWT()and returns it as an access token (with an optional refresh token) - The CLI stores the token in
~/.broomva/config.json
Authenticated successfully! Token stored in ~/.broomva/config.jsonIf the server is unreachable or the device-code endpoint is not available, the CLI automatically falls back to the manual flow.
Server-side implementation
The device code flow is implemented across three API endpoints:
| Endpoint | Method | Purpose |
|---|---|---|
/api/auth/device/code | POST | Issue a device code + user code pair |
/api/auth/device/authorize | POST | Approve or deny (called from the browser) |
/api/auth/device/token | POST | Poll for the access token |
Code generation: The user code uses a character set that avoids ambiguous characters -- letters ABCDEFGHJKMNPQRSTUVWXYZ (no I, L, O) and digits 23456789 (no 0, 1). This prevents confusion when reading codes aloud or entering them manually.
Rate limiting: The code endpoint is rate-limited to 10 requests/minute per IP. The token endpoint is rate-limited to 5 requests/minute per device code + IP combination. When the token endpoint detects excessive polling, it returns a slow_down error per RFC 8628, and the CLI backs off by an additional 5 seconds.
Expiration: Device codes expire after 15 minutes (expires_in: 900). The polling interval is 5 seconds by default.
Polling behavior
The CLI polls at the interval specified by the server (default: 5 seconds). The server responds with one of these RFC 8628 error codes:
| Response | Meaning | CLI action |
|---|---|---|
authorization_pending | User has not acted yet | Continue polling |
slow_down | Polling too fast | Back off by 5 seconds |
access_denied | User denied authorization | Exit with error |
expired_token | Device code expired | Exit with error |
HTTP 200 + access_token | User approved | Store token, exit success |
Token format
On approval, the authorize endpoint calls signLifeJWT() to create a signed JWT containing the user's ID and email. The token response includes:
{
"access_token": "eyJ...",
"token_type": "Bearer",
"expires_in": 2592000,
"refresh_token": "eyJ..."
}The refresh_token is included when the auth system issues token pairs (introduced in BRO-121).
Manual token flow
If you prefer to copy a token directly from the web UI:
broomva auth login --manualThis flow:
- Instructs you to sign in at broomva.tech (email, Google, or GitHub -- accounts with the same email are automatically linked)
- Directs you to
/api/auth/api-tokento generate a token - Prompts you to paste the token
- Validates the token against the server via the
ApiClient.validateToken()method - Stores the token in
~/.broomva/config.json
Token storage
Tokens are stored in ~/.broomva/config.json:
{
"token": "brv_...",
"apiBase": "https://broomva.tech"
}The file is created with 0600 permissions (owner read/write only). The token is a JWT signed by the platform's auth system (Better Auth) that encodes your user ID and organization memberships.
Treat your token like a password. Anyone with your token can make API requests as you. If you suspect your token has been compromised, revoke it from the web UI and run broomva auth login to get a new one.
Token resolution order
When the CLI needs a token, it checks these sources in order:
--tokencommand-line flagBROOMVA_TOKENenvironment variable~/.broomva/config.jsonfile
The first non-empty value wins.
Checking auth status
broomva auth status
# Authenticated (token from config-file)
# Token expires: 2026-04-21T00:00:00ZWith JSON output for scripting:
broomva auth status --json
# {"authenticated":true,"source":"config-file","expiresAt":"2026-04-21T00:00:00Z"}The status command validates the token against the server. If the token is expired or revoked, it reports the token as invalid.
Printing the raw token
For use in scripts and curl commands:
broomva auth token
# brv_eyJ...This outputs only the token string with no formatting, suitable for command substitution:
curl -H "Authorization: Bearer $(broomva auth token)" https://broomva.tech/api/...Logging out
broomva auth logout
# Logged out. Token removed from ~/.broomva/config.jsonThis removes the token from the local config file. The token remains valid on the server until it expires -- logging out is a local-only operation.