Verifying the Device Flow
The CLI device authorization grant spans four codebases: the CLI client, the www.vulnetix.com approval page, the vdb-site API that issues and redeems grants, and the CLIAuthCode table in the SaaS database. This page is the repeatable check that all four still agree.
A. Unit tests (no network)
The client state machine — authorize, poll, authorization_pending, slow_down backoff, expired_token, access_denied, single-use redemption — runs against an httptest server.
just test-device-flow
The server side has matching unit tests for the grant state machine, the user_code format, and the device_code hashing:
cd ../vdb-site/api && go test ./internal/handler -run 'TestDevice|TestValidUserCode|TestNewDeviceCode' -v
Both must be green before anything deploys.
B. End-to-end, local
just verify-device-flow
The script boots vdb-site and the website against a local Postgres, then automates everything except signing in and clicking Approve. It asserts, in order:
- The
CLIAuthCodemigration is applied andmemberUuidis nullable. POST /api/site/v1/cli/device/authorizethrough the website Worker reachesvdb-siteand returns a grant. This alone proves thewww→vdb-sitepath.- The
device_codeis at least 40 characters — 256 bits of entropy, not a guessable code. POST .../approvereturns401when unauthenticated.- Fifteen rapid
authorizecalls trip the{60s, 12}rate limiter with a429. - Polling
/tokenbefore approval returnsauthorization_pending. - (manual) The browser pre-fills the code, and on approval shows no ApiKey and no OrgID — the old SaaS page displayed both.
- The CLI writes an
apikeycredential for a UUID org, and the rawdevice_codeis absent fromcredentials.json. - Replaying the redeemed
device_codereturnsaccess_deniedorexpired_token. A grant is single-use. - The database row is
claimed, approved, and stores only the sha256 of thedevice_code. vulnetix auth verifyaccepts the freshly minted credential.
Override DATABASE_URL, JWT_SECRET, WEBSITE_PORT, or SITE_API_PORT if your local setup differs.
C. Production smoke
Run after deploying. Stop at the first failure.
# 1. authorize is public and live — expect JSON with device_code + user_code
curl -sS -X POST https://www.vulnetix.com/api/site/v1/cli/device/authorize \
-H 'Content-Type: application/json' -d '{}' | jq
# 2. approve rejects the unauthenticated — expect 401
curl -s -o /dev/null -w '%{http_code}\n' -X POST \
https://www.vulnetix.com/api/site/v1/cli/device/approve \
-H 'Content-Type: application/json' -d '{"user_code":"AAA-AAA"}'
# 3. the retired SaaS endpoint is gone — expect 404
curl -s -o /dev/null -w '%{http_code}\n' \
https://app.vulnetix.com/api/cli/auth-code/poll/AAA-AAA
# 4. a real login
vulnetix auth login --verbose
vulnetix auth verify # expect exit 0
Then confirm the handler logged cleanly:
aws logs tail /ecs/vdb-site-api --since 10m --filter-pattern cli/device
Expect one authorize, one approve, several token polls, and no 500s.