What's wrong
create_device_auth_router refuses to build unless auth_provider._source == "accounts", and app.py gates the /oauth/* mount the same way. So on an OIDC deployment the RFC 8628 device-authorization grant does not exist.
The stated reasoning is that OIDC deployments delegate login to the IdP via the cli-ticket flow and therefore never need the grant. That covers a CLI logging a human in. It does not cover a third-party application asking to act as a user, which is what the grant is for.
Why the cli-ticket flow is not a substitute
/auth/cli-login hands back the server's own session JWT. Compared with a delegated token from the device grant it has:
|
device grant |
cli-ticket |
scope claim (path allowlist) |
✅ |
❌ |
grant_id → revocation denylist |
✅ |
❌ |
act provenance (which app acted) |
✅ |
❌ |
| Rotating refresh token |
✅ |
❌ |
| Absolute grant lifetime |
✅ |
❌ |
Everything that makes a token safe to hand to an application is exactly what it lacks. An OIDC operator's only option today is to give an external client full account authority with no revocation handle.
header mode is genuinely excluded — identity is asserted by an upstream proxy, so there is no session to delegate from. But oidc mints the same HS256 session cookie accounts does, and UnifiedAuthProvider._check_cookie already picks between the two configs identically.
A second, quieter problem
The consent page's anti-phishing gate requires a login performed after the grant began (session iat ≥ grant.created_at), and bounces a stale session through the login page with reauth=1.
reauth=1 is honoured only by the accounts SPA login form. /auth/login never read the parameter. So simply widening the mount condition would produce:
stale session → bounce /auth/login?reauth=1
→ IdP recognises its own session
→ silent redirect back
→ callback mints a session with a fresh iat
→ gate satisfied, user proved nothing
No error, no failing test — the control disappears while still appearing to be there. Any fix that mounts the grant under OIDC has to address this at the same time, or it ships a security regression that passes CI.
Impact
Self-hosted OIDC deployments cannot safely integrate any external client — Slack, CI bots, code-review bots — without handing over an unscoped, unrevocable session token.
Proposed fix
Mount the grant for oidc as well as accounts, and make the re-authentication demand real under OIDC (prompt=login + max_age=0, with the returned auth_time verified rather than assumed).
PR: omnigent-ai#4304
Imported from the parent repository for autonomous engineering evaluation.
Source issue: omnigent-ai#4476
What's wrong
create_device_auth_routerrefuses to build unlessauth_provider._source == "accounts", andapp.pygates the/oauth/*mount the same way. So on an OIDC deployment the RFC 8628 device-authorization grant does not exist.The stated reasoning is that OIDC deployments delegate login to the IdP via the cli-ticket flow and therefore never need the grant. That covers a CLI logging a human in. It does not cover a third-party application asking to act as a user, which is what the grant is for.
Why the cli-ticket flow is not a substitute
/auth/cli-loginhands back the server's own session JWT. Compared with a delegated token from the device grant it has:scopeclaim (path allowlist)grant_id→ revocation denylistactprovenance (which app acted)Everything that makes a token safe to hand to an application is exactly what it lacks. An OIDC operator's only option today is to give an external client full account authority with no revocation handle.
headermode is genuinely excluded — identity is asserted by an upstream proxy, so there is no session to delegate from. Butoidcmints the same HS256 session cookieaccountsdoes, andUnifiedAuthProvider._check_cookiealready picks between the two configs identically.A second, quieter problem
The consent page's anti-phishing gate requires a login performed after the grant began (session
iat≥grant.created_at), and bounces a stale session through the login page withreauth=1.reauth=1is honoured only by the accounts SPA login form./auth/loginnever read the parameter. So simply widening the mount condition would produce:No error, no failing test — the control disappears while still appearing to be there. Any fix that mounts the grant under OIDC has to address this at the same time, or it ships a security regression that passes CI.
Impact
Self-hosted OIDC deployments cannot safely integrate any external client — Slack, CI bots, code-review bots — without handing over an unscoped, unrevocable session token.
Proposed fix
Mount the grant for
oidcas well asaccounts, and make the re-authentication demand real under OIDC (prompt=login+max_age=0, with the returnedauth_timeverified rather than assumed).PR: omnigent-ai#4304
Imported from the parent repository for autonomous engineering evaluation.
Source issue: omnigent-ai#4476