AUTH4D-58 per-application device settings and branding
The device flow from AUTH4D-16 (repository file docs/api-spec/auth4d-16.md) now applies each native application’s device
settings and branding, configured through AUTH4D-56 (repository file docs/api-spec/auth4d-56.md). Values are resolved per
tenant and client (resolveApplicationPresentation); unset fields take DEFAULT_DEVICE_SETTINGS,
so unconfigured applications behave exactly as before. Databases without migration 0008 also use
the defaults.
Start a device authorization
POST /device/authorization?tenant_id={tenantId} is unchanged except for the response values. The
response validates against ConfiguredDeviceAuthorizationResponseSchema:
| Field | Source | Default |
|---|---|---|
user_code |
device_settings.user_code_length (8–12) |
8 |
expires_in |
device_settings.code_ttl_seconds (120–1800) |
600 |
interval |
device_settings.poll_interval_seconds (5–60) |
5 |
{
"device_code": "dc_…",
"user_code": "7K2Q9ZP4XM",
"verification_uri": "https://auth.auth4.dev/device/verify?tenant_id=ten_…",
"verification_uri_complete": "https://auth.auth4.dev/device/verify?tenant_id=ten_…&user_code=7K2Q9ZP4XM",
"expires_in": 300,
"interval": 15
}
The effective settings are stored on the device-code record when the code is issued. Changing an application’s settings affects only codes issued afterwards; an in-flight code keeps its lifetime and polling interval.
Polling
POST /oauth/token with the device-code grant enforces the interval returned at issue time. A poll
earlier than the interval returns 400 slow_down and adds five seconds to that code’s interval
(RFC 8628 §3.5), up to 600 seconds. The code expires with expired_token after its configured
lifetime.
Verification data
GET /device/verify?tenant_id={tenantId}&client_id={clientId}&user_code={userCode} with
Accept: application/json returns DeviceVerifyResponseSchema. branding now holds the
application’s configured branding (only the fields that are set; {} when none are). A stored
accent color that no longer meets 4.5:1 contrast against white is omitted.
{
"tenant": { "tenant_id": "ten_…", "display_name": "Studio" },
"application": { "client_id": "cli_…", "display_name": "My game" },
"branding": {
"accent_color": "#1d4ed8",
"support_url": "https://support.example.com/help",
"privacy_policy_url": "https://example.com/privacy"
},
"user_code": "7K2Q9ZP4XM",
"scopes": ["openid", "profile"],
"browser_flow_id": "flw_…",
"csrf_token": "…"
}
user_code is accepted case-insensitively at any configurable length (8–12, matching
ConfiguredDeviceUserCodeSchema). POST /device/approval and POST /device/denial accept the same
lengths (ConfiguredDeviceApprovalRequestSchema). A code only matches the record it was issued
for, so accepting longer codes does not widen what a guess can reach. The device CSRF cookie’s
Max-Age is now the code’s remaining lifetime instead of a fixed 600 seconds.
The response keeps Cache-Control: no-store. Branding is public presentation data and contains no
secrets.
Source: docs/api-spec/auth4d-58.md