--- title: "1.37.3 — OAuth device sign-in for headless agents; consent approval fix" description: "Adds the OAuth device authorization grant, so an agent with no browser can connect to an MCP endpoint by showing a code that a person approves from any signed-in browser. It's off by default. Also fixes the browser consent page, whose Approve button failed on every request." date: "2026-10-20" tags: ["release"] canonical: "https://django-mojo.sitesmojo.com/changelog/1-37-3/" --- Adds the OAuth device authorization grant, so an agent with no browser can connect to an MCP endpoint by showing a code that a person approves from any signed-in browser. It's off by default. Also fixes the browser consent page, whose Approve button failed on every request. ### Added - OAuth device authorization grant (RFC 8628). It adds a `device_authorization` endpoint, a hosted verification page, and the `urn:ietf:params:oauth:grant-type:device_code` grant on the token endpoint. The client receives an ordinary OAuth grant, with the same audience confinement, refresh rotation, replay detection and revocation as a browser consent. - New file-only settings: - `OAUTH_DEVICE_FLOW_ENABLED` (default off); - `OAUTH_DEVICE_ALLOW_API` (default off, so device grants are `mcp`-only); - `OAUTH_DEVICE_CODE_TTL` (600 s, clamped to 60–1800); - `OAUTH_DEVICE_INTERVAL` (5 s); - `OAUTH_DEVICE_MAX_TTL_DAYS` (30, at most 90). - The person approving chooses how long the connection lasts: 30, 60 or 90 days, never more than `OAUTH_DEVICE_MAX_TTL_DAYS`. The lifetime is absolute and is never extended by refreshing. - The approval page shows the requesting machine's IP, location, user agent and request age. It warns when that machine is on a different network, warns loudly when a web page rather than a device started the request, and warns when the account lacks `view_admin` or `assistant`. - Approving requires an interactive session with a login in the last 10 minutes. API keys, per-user API keys and group tokens are refused. - Failed code lookups are capped at about 5 per person and 20 per IP (per /64 for IPv6) every 15 minutes. After that they're refused with 429 and reported as `oauth:device_code_bruteforce`. A device code presented again after use revokes the grant it produced and is reported as `oauth:device_code_replay`. - Ending a person's sessions (sign out everywhere, password change) also cancels device approvals the device hasn't collected yet. - `OAuthGrant.flow` records how consent was given: `authorization_code` or `device_code`. ### Changed - Dynamic client registration and client metadata documents now store the granted `grant_types`. The device grant is never implied; a client must request it by name. A client registered only for the device grant may omit `redirect_uris`. ### Fixed - Approving on the browser consent page failed for every request with "response_type must be code". MCP clients such as Codex reached the consent page but could never finish connecting. ### Upgrade notes - There's a new `account` migration (the device-code table and a `flow` column on OAuth grants). - Nothing changes until `OAUTH_DEVICE_FLOW_ENABLED = True` is set in the deployment settings file. It needs a restart; there is no database or Admin switch. Discovery advertises the device endpoint only while the flow is on. - Clients registered before this release never declared the device grant, so `device_authorization` answers `unauthorized_client` for them. Register a new client with the device grant type. - A device grant approved by an account without `view_admin` or `assistant` is still issued, but every MCP call with it is refused. The page warns about this before approving.