Skip to content

go-authgate/device-cli

Repository files navigation

AuthGate CLI - OAuth 2.0 Device Flow Client

Trivy Security Scan Lint and Testing

A CLI tool for authenticating with AuthGate server via OAuth 2.0 Device Authorization Flow (RFC 8628). Designed for headless environments, SSH sessions, and scenarios where browser-redirect flows are impractical.

Key capabilities: token storage with multi-client support · automatic token refresh · RFC 8628-compliant polling with exponential backoff · TLS 1.2+ enforcement


Table of Contents


Prerequisites

  • Go 1.25+ — required to build from source
  • AuthGate server — must be running and accessible (AuthGate Documentation)
  • Default server address: http://localhost:8080

Quick Start

# 1. Clone and build
git clone <repository-url>
cd device-cli
make build

# 2. Get your Client ID from the AuthGate server startup logs:
#    Client ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

# 3. Run
./bin/device-cli -client-id=<your-client-id>

Configuration

Priority order: Flag > Environment Variable > .env file > default

Parameter Flag Environment Variable Default
Client ID -client-id CLIENT_ID (required)
Server URL -server-url SERVER_URL http://localhost:8080
Token File -token-file TOKEN_FILE .authgate-tokens.json
Token Store -token-store TOKEN_STORE auto

Token Store modes:

  • auto (default): Tries OS keyring first, falls back to file if keyring is unavailable
  • keyring: Uses OS keyring only (macOS Keychain, GNOME Keyring, Windows Credential Manager)
  • file: Uses JSON file only

Example .env file:

CLIENT_ID=abc-123
SERVER_URL=http://localhost:8080
TOKEN_FILE=.authgate-tokens.json
TOKEN_STORE=auto
./bin/device-cli -h   # view all options

Device Flow Architecture

sequenceDiagram
    participant CLI as AuthGate CLI
    participant Server as AuthGate Server
    participant Browser as User's Browser

    CLI->>Server: POST /oauth/device/code (client_id)
    Server-->>CLI: device_code, user_code, verification_uri

    CLI->>CLI: Display verification_uri + user_code

    Browser->>Server: User visits verification_uri
    Browser->>Server: User enters user_code + logs in
    Server-->>Browser: Authorization granted

    loop Polling every 5s (with backoff on slow_down)
        CLI->>Server: POST /oauth/token (device_code)
        Server-->>CLI: authorization_pending / slow_down / tokens
    end

    Server-->>CLI: access_token + refresh_token
    CLI->>CLI: Save tokens to .authgate-tokens.json (0600)

    note over CLI,Server: On subsequent runs
    CLI->>Server: Verify access_token
    alt Token valid
        Server-->>CLI: 200 OK
    else Token expired
        CLI->>Server: POST /oauth/token (grant_type=refresh_token)
        Server-->>CLI: New access_token + refresh_token
        CLI->>CLI: Update token file atomically
    end
Loading

How to Use

First Time Login

  1. Run the tool with your client ID

  2. The CLI displays a verification URL and user code:

    Please open this link to authorize:
    http://localhost:8080/device?user_code=ABC12345
    
    Or manually visit: http://localhost:8080/device
    And enter code: ABC12345
  3. Open the URL in your browser

  4. Log in to AuthGate (default: admin / check server logs for password)

  5. Enter the user code when prompted

  6. The CLI detects authorization automatically and saves your tokens

Subsequent Runs

Tokens are saved locally after first login. The CLI will:

  • Reuse valid access tokens
  • Automatically refresh expired access tokens using the refresh token
  • Start a new device flow only if refresh fails

Token Storage

Tokens are managed by the credstore package from sdk-go, which supports multiple storage backends:

Backend Description
auto Tries OS keyring first, falls back to file (default)
keyring OS keyring: macOS Keychain, GNOME Keyring, Windows Credential Manager
file JSON file at .authgate-tokens.json (configurable)

A single store supports multiple Client IDs.

When using file-based storage, the JSON format is:

{
  "tokens": {
    "client-id-1": {
      "access_token": "...",
      "refresh_token": "...",
      "token_type": "Bearer",
      "expires_at": "2026-01-20T12:00:00Z",
      "client_id": "client-id-1"
    },
    "client-id-2": {
      "access_token": "...",
      "refresh_token": "...",
      "token_type": "Bearer",
      "expires_at": "2026-01-20T13:00:00Z",
      "client_id": "client-id-2"
    }
  }
}

Security properties:

  • File-based storage: created with 0600 permissions (owner read/write only)
  • Keyring storage: uses OS-level credential encryption
  • Automatic fallback warning when OS keyring is unavailable

Never commit token files to version control. Add .authgate-tokens.json to .gitignore.


Usage Examples

# First run — prompts for browser authorization
./bin/device-cli -client-id=abc-123

# Subsequent runs — reuses saved tokens automatically
./bin/device-cli -client-id=abc-123

# Custom server URL
./bin/device-cli -client-id=abc-123 -server-url=https://auth.example.com

# Use OS keyring for token storage
./bin/device-cli -client-id=abc-123 -token-store=keyring

# Use file-only storage
./bin/device-cli -client-id=abc-123 -token-store=file

# Multiple clients — stored in same backend by default
./bin/device-cli -client-id=abc-123
./bin/device-cli -client-id=xyz-789

# Multiple clients — separate token files (file mode)
./bin/device-cli -client-id=abc-123 -token-store=file -token-file=./work-tokens.json
./bin/device-cli -client-id=xyz-789 -token-store=file -token-file=./personal-tokens.json

Error Reference

The CLI handles all OAuth 2.0 Device Authorization Grant error codes defined in RFC 8628:

Error Meaning CLI Behaviour
authorization_pending User has not authorized yet Continues polling, shows progress dots
slow_down Server requests slower polling Adds 5s to polling interval per RFC 8628 §3.5 (max 60s)
expired_token Device code expired (default: 30 minutes) Stops polling, prompts to restart authentication
access_denied User explicitly denied authorization Stops and displays denial message
Other errors Unexpected server errors Stops and displays detailed error information

Advanced Features

Polling with Additive Backoff

  • Initial interval: 5 seconds (set by server)
  • TUI progress: interactive terminal UI when running in a TTY; plain text output otherwise
  • slow_down backoff: adds 5 seconds per signal (per RFC 8628 §3.5), capped at 60s
Initial:        5s
1st slow_down: 10s
2nd slow_down: 15s
3rd slow_down: 20s
...
Maximum:       60s

Context and Cancellation

  • All operations respect Go context cancellation
  • Graceful shutdown on Ctrl+C
  • Per-operation timeouts: device code 10s, token exchange 5s, verification 10s, refresh 10s

Security

HTTP Client

Protection Detail
Request timeout Per-operation: 5–10 seconds (see Context section)
Minimum TLS version TLS 1.2
HTTP warning Warns automatically when server URL uses plain HTTP
Connection pooling Idle connection limits to manage resources

Input Validation

  • SERVER_URL: Validates URL format and scheme (http/https only)
  • CLIENT_ID: Warns if value is not a valid UUID format

Token Storage Security

  • Keyring (default when available): OS-level credential encryption
  • File fallback: 0600 permissions (owner-only)
  • CLI warns when OS keyring is unavailable and file storage is used

Error Handling

  • All errors are checked — no silent failures
  • Error chains preserve full context for debugging
  • Token values are truncated or redacted in log output

Best Practices

  1. Add .authgate-tokens.json to .gitignore
  2. Use HTTPS URLs in production — the CLI will warn if you don't
  3. Delete token files when no longer needed
  4. Review any security warnings printed at startup

Troubleshooting

CLIENT_ID not set Provide via -client-id=<id> flag, CLIENT_ID env var, or .env file. Find your ID in the AuthGate server startup logs.

connection refused Start the AuthGate server in another terminal:

./bin/authgate server

Verify the server URL matches (default: http://localhost:8080).

token verification failed The token was revoked or is invalid. Delete the token file and re-authenticate:

rm .authgate-tokens.json
./bin/device-cli -client-id=<your-id>

refresh failed The CLI will automatically start a new device flow. Follow the browser authorization steps again.

Polling is slowing down Normal behavior — the server returned a slow_down signal. The CLI has automatically increased its polling interval. See Error Reference.

context deadline exceeded A request timed out. Check your network connection and server availability.


Development

# Run tests with coverage
make test

# Build binary (output: bin/device-cli)
make build

# Lint and format
make lint
make fmt

# Cross-platform builds
make build_linux_amd64
make build_linux_arm64

# Clean build artifacts
make clean

Learn More

For issues or questions, please open an issue on the project repository.

About

AuthGate CLI is a command-line tool that demonstrates how to authenticate with AuthGate server using the OAuth 2.0 Device Authorization Flow.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors