Uitsmijter 0.11.0: Login Without a Keyboard, Providers Without a Script
Posted on July 8, 2026 • 7 min read • 1,357 wordsVersion 0.11.0 brings the OAuth 2.0 Device Authorization Grant, predefined uitrusting/v1 providers, tenant-aware provider scripts, a roles claim, and a round of spec-conformance fixes to the authorize and token endpoints.

Uitsmijter 0.11.0 is a wide release. It opens the door to a whole new class of clients — smart TVs, CLIs, and IoT devices that can’t reasonably show a login form — and at the same time it makes connecting Uitsmijter to your own user store dramatically simpler. Underneath, several endpoints were brought back in line with the OAuth and OIDC specifications, which quietly improves interoperability with off-the-shelf clients.
Here is what changed, and why it matters for you.
The headline feature is the OAuth 2.0 Device Authorization Grant (RFC 8628), often called the device flow. It exists for one reason: some devices simply cannot handle a normal login. A smart TV with an on-screen keyboard, a command-line tool on a headless server, a printer, a thermostat — none of them should be asking a user to peck out a password one letter at a time with a remote control.
The device flow solves this by splitting the login across two devices. The constrained device requests a device_code and a short, human-friendly user_code from POST /oauth/device_authorization, then shows the user a message like “Open login.example.com/activate and enter WDJB-MJHT.” The user approves the request in a browser on their phone or laptop at GET/POST /activate — going through the normal Uitsmijter login, with all their usual providers and rules. Meanwhile the device polls POST /token with the device_code grant until the tokens are issued.
The important detail: the constrained device never sees the password. It only ever holds the finished token.
Enabling it is a per-client opt-in — you add device_code to the client’s grant_types, and that’s it. When a client supports the grant, Uitsmijter advertises the device_authorization_endpoint in its OIDC discovery document so standard client libraries can find it automatically.
We covered the device flow in depth in its own article — Signing in without a keyboard — including the full configuration and the timing knobs.
uitrusting/v1) Until now, connecting Uitsmijter to a user store meant writing a JavaScript provider by hand: a UserLoginProvider and a UserValidationProvider implemented in the tenant configuration. That’s powerful, but for the common case — “call my user service and ask whether these credentials are valid” — it’s more ceremony than the task deserves.
Version 0.11.0 introduces predefined providers. Instead of a script, a tenant’s providers entry can now be a small object:
providers:
- type: "uitrusting/v1"
url: "https://users.internal.example.com"
token: "s3cr3t-internal-token" # optionalUitsmijter generates the login and validation providers internally. On login it sends a POST {url}/verify with { tenant, namespace, username, password_hash }; on refresh re-validation it sends the same payload without the hash. Your service answers with { known, valid, subject, roles, scopes, profile }, and Uitsmijter maps that straight onto the provider interface.
Two things are worth calling out:
token is sent as an X-Internal-Token header, so you can expose the /verify endpoint on an internal-only route that rejects anything without it.For the majority of integrations, this replaces a page of JavaScript with four lines of YAML.
If you do write your own provider scripts, they now receive the tenant alongside the credentials as credentials.tenant, an object of { name, namespace }. This makes multi-tenant user services much easier to build: a single provider script can scope each request to the correct tenant instead of guessing from the username or maintaining a separate script per tenant.
This is purely additive. Existing raw scripts that ignore the field behave exactly as before.
JWTs issued by Uitsmijter have always carried a single role claim. Version 0.11.0 adds an optional roles array claim for the increasingly common case where a user genuinely has several roles at once.
Providers can now expose a roles getter. When they do, the token carries the full roles array, and role continues to mirror the primary role for backward compatibility. When a provider only knows about a single role, the roles claim is omitted entirely — so existing tokens are byte-for-byte unchanged and nothing downstream needs to be updated until you’re ready to use it.
Three fixes in this release bring Uitsmijter closer to the letter of the relevant RFCs. Standards conformance isn’t glamorous, but it’s what lets an arbitrary OAuth client library work against Uitsmijter without special-casing.
client_secret is no longer required on /authorize Per RFC 6749 §4.1.1 and §3.2.1, the authorization endpoint identifies the client by client_id only. Confidential clients authenticate with their secret on the back-channel token request, not on the front-channel /authorize call.
Previously, Uitsmijter required the secret on /authorize. That broke spec-conformant OIDC clients — and worse, it pushed the client secret through the user-agent, where it doesn’t belong. As of 0.11.0 the secret is validated only at the token endpoint, where it is safe and where the spec expects it.
The device token endpoint now returns the standard OAuth error responses — HTTP 400 with {"error": "authorization_pending" | "slow_down" | "access_denied" | "invalid_grant"} — instead of the generic Uitsmijter error envelope. Notably slow_down is now a 400, not a 429, matching the RFC.
The endpoint also accepts the standard urn:ietf:params:oauth:grant-type:device_code grant-type URN, while keeping the bare device_code as an alias. Together these mean an off-the-shelf OAuth2 device-flow client interoperates with Uitsmijter unmodified.
Additional fixes land for running Uitsmijter behind Traefik 3.x, continuing the compatibility work as the ecosystem moves to the newer proxy.
device_grant_config is optional The device grant is enabled solely by listing device_code in a client’s grant_types. If you omit device_grant_config, sensible defaults apply — expires_in: 1800, interval: 5, and an auto-detected verification URI — rather than the request being rejected. You only add the config block when you actually want to change one of those values.
providers schema accepts two shapes To support predefined providers, each providers entry may now be either a raw JavaScript script (a string, exactly as before) or a predefined-provider object. The Tenant CRD schema accepts both, so existing script-based tenants keep working untouched while new ones can use the shorter form.
The AWS SDK dependency — used for loading tenant login templates from S3 — was bumped from soto 6.x to 7.x (soto-core 7, async-http-client 1.30+), and the S3 template loader was migrated to the new API. This keeps the S3 integration on a supported, maintained foundation.
When a user finishes approving a device, the “device authorized successfully” page now has a styled, centered layout with a success checkmark — instead of the previous plain, left-aligned text. It’s a small thing, but it’s the last screen a user sees in the device flow, and it should feel finished.
Shipping under Tests/Tools/Cli/DeviceGrantDemo is a small Rust reference client built on the standard oauth2 crate. It runs the full device flow against a Uitsmijter server end to end, so you can see exactly what a well-behaved client looks like. It also serves as a live conformance check: because it uses a standard, unmodified OAuth2 library, a passing run is evidence that our device flow speaks the spec correctly.
Version 0.11.0 is a drop-in upgrade. Nothing in this release changes existing behaviour:
roles getter.credentials.tenant is simply available if you want it./authorize change only removes a requirement, so any client that worked before still works.If you want to take advantage of the new capabilities, the two most rewarding places to start are adding device_code to a client’s grant_types to enable the device flow, and replacing a hand-written provider script with a uitrusting/v1 predefined provider.
Full release notes are available in the CHANGELOG. As always, thank you for being part of the Uitsmijter community.