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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions