Skip to content

Device Authorization Flow (RFC 8628): Production Readiness Assessment & Follow-ups #2

Description

@devin-ai-integration

Context

Device authorization flow (RFC 8628) was published in v1.21.0 (9f8ce7c) and is live on dev-api.garnet.ai. This issue captures the production readiness assessment, test results, and actionable follow-ups based on hands-on testing of the dev deployment.

cc @nicolasparada — great work on the implementation. Architecture is correct, code is clean. Below are findings from a dedicated test session to help get this to prod.


Test Summary (dev-api.garnet.ai)

What works well ✓

  • Auth enforcement: Approve/reject correctly require user JWT — project tokens, unauthenticated callers, and cross-env tokens all get 401
  • Input validation: XSS/SQLi in deviceCode path → clean 404 (hash-based lookup prevents injection)
  • Expiry: 10-min TTL works correctly, transitions to expired status on poll
  • Device code entropy: Crockford Base32, 26 chars = 130 bits — brute-force infeasible
  • Method enforcement: PUT → 405, empty path → 404
  • State machine: Expired codes can't be approved/rejected (auth still enforced)

What couldn't be tested

  • One-time token reveal after approval (dev-app has Vercel deployment protection blocking external approval)
  • Project membership enforcement on approve
  • Second poll returns approved without token

Ask: Add integration tests in CI that exercise approve → poll → token-reveal → second-poll-no-token with a test user JWT.


Production Blockers

1. No rate limiting on unauthenticated endpoints

Tested 20 rapid POST /device-authorizations — all returned 201. Tested 20 rapid polls — all 200. No throttling observed.

Risk:

  • Table flooding (unlimited pending records)
  • Code-generation for phishing campaigns
  • Poll-based load amplification

Fix:

  • Per-IP rate limit on POST /device-authorizations (suggest: 10/min/IP)
  • Per-deviceCode rate limit on poll with RFC 8628 slow_down error (HTTP 400 {"error": "slow_down"}) when client polls faster than pollIntervalSeconds
  • 429 Too Many Requests with Retry-After header on burst

Note: This compounds existing audit finding #8 (no backend rate limiting).

2. CORS allows any origin

< Access-Control-Allow-Origin: *
< Access-Control-Allow-Methods: GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD

While create/poll are public by design, the wildcard combined with unauthenticated create means any malicious site can silently create device authorizations and poll for approved tokens from the user's browser.

Fix: Restrict device-auth CORS to https://app.garnet.ai (and dev-app for staging). Create/poll don't need browser-origin access from arbitrary sites — only the Garnet UI calls approve.

Note: Related to existing audit finding #15/#25 (CORS wildcard).

3. No client context on consent screen

CreateDeviceAuthorization has an empty request body. The approval page can only show "approve code X for project Y" — the user can't see what is requesting access. This is the classic device-flow phishing vector.

Fix: Add optional metadata fields:

type CreateDeviceAuthorization struct {
    ClientName    string `json:"clientName,omitempty"`    // e.g. "Devin", "Claude Code"
    Environment   string `json:"environment,omitempty"`   // e.g. "github.com/org/repo"  
    RequestReason string `json:"requestReason,omitempty"` // e.g. "Configure Jibril for CI"
}

Render on /device consent page so users can make informed approve/reject decisions.


Non-Blocking Follow-ups

Scoped device-flow tokens

Currently issues a standard project token (unlimited registrations, no TTL, no audit trail to the device-auth). Recommend minting a dedicated token per device authorization with: TTL, registration cap (e.g. 1 agent), and metadata linking to the device-auth ID + approving user.

PKCE-style client binding

Anyone who intercepts the deviceCode can poll and steal the project token after approval. While 130-bit entropy makes brute force impossible, adding a code_verifier at create time (must present at poll time) closes interception attacks.

/device page route

verificationURI points to dev-app.garnet.ai/device, but garnet-ui has no /device route. Confirm whether this is served by the control-plane directly or needs a garnet-ui route (preferred for UX consistency).

?code= preservation through login redirect

When an unauthenticated user visits /device?code=X, the ?code= param must survive the Auth0 OAuth redirect chain back to the device page.

Approve must check project membership

Given audit finding #462 (cross-project authorization leak via empty resourceID), explicitly test that a user cannot approve a device-auth for a project they don't belong to.

Expired record garbage collection

Expired device authorizations should be periodically purged. Especially important given the rate-limit gap — without cleanup, an attacker could accumulate millions of records.


Integration Roadmap

Phase Scope Depends on
1 — Ship to prod Fix 3 blockers above, deploy to api.garnet.ai, confirm /device page This issue
2 — CLI + MCP garnetctl auth device-login command, platform-mcp device-auth support Phase 1
3 — Agent SDK Devin/Claude/Copilot integration playbooks, scoped tokens, integration tests Phase 2
4 — OIDC federation GH Actions OIDC → short-lived credential exchange, deprecate static secrets for CI Phase 3

Full test report with raw curl output available on request. Tested 2026-06-12 from Devin session.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions