FAForever / FAForever/faf-user-service
Device verification page rejects the bare verification_uri (manual/cross-device login broken)
- Dominant language
- Kotlin
- Stars
- 3
- Forks
- 6
- Avg merge
- 2d 16h
- Merged PRs (30d)
- 23
Description
## Summary
The device authorization verification page (`/oauth2/device-login`, `DeviceLoginView`) only works when opened via the `verification_uri_complete` returned by Hydra. Opening the bare `verification_uri` — as a user would when following the RFC 8628 manual flow ("go to this URL and enter this code") — fails with **"Zugriff verboten / Ungültiger Login-Weg"** (access forbidden / invalid login path).
## Steps to reproduce
1. Start an OAuth 2.0 Device Authorization Grant (RFC 8628) flow, e.g. a client `POST`s to `/oauth2/device/auth`.
2. Take the `verification_uri` from the response (e.g. `https://…/oauth2/device-login`, **without** query parameters) and open it manually in a browser.
3. The page renders an error instead of prompting for the user code.
Opening `verification_uri_complete` (which contains `device_challenge` and `user_code` as query params) works correctly.
## Root cause
`DeviceLoginView.beforeEnter` requires both `device_challenge` and `user_code` to be present as query parameters, otherwise it throws `NoChallengeException`:
```kotlin
override fun beforeEnter(event: BeforeEnterEvent?) {
val params = event?.location?.queryParameters?.parameters
val possibleChallenge = params?.get("device_challenge")?.firstOrNull()
val possibleUserCode = params?.get("user_code")?.firstOrNull()
if (possibleChallenge.isNullOrBlank() || possibleUserCode.isNullOrBlank()) {
throw NoChallengeException()
}
...
}
```
Since the bare `verification_uri` carries no query parameters, the manual path always throws.
## Why this matters
RFC 8628 §3.3 defines `verification_uri` as the end-user–facing URL the user is expected to visit and **then manually enter the `user_code`**. `verification_uri_complete` (§3.3.1) is only an optional convenience that pre-fills the code. A conforming device therefore may show the user just the `verification_uri` + `user_code` — this is the intended experience when the device cannot open a browser locally, or when the user completes authorization on a **different device** (phone, another computer).
Concretely: this came up in the FAF client (downlords-faf-client) device-login migration. Auto-opening `verification_uri_complete` works, but the displayed/clickable `verification_uri` fallback does not, so cross-device / headless authorization is currently impossible.
## Suggested fix
When `DeviceLoginView` is entered without a `device_challenge` (and/or `user_code`), instead of throwing, present an input field for the user to type their `user_code` and submit it, resolving the `device_challenge` through Hydra's device verification flow — then continue to the existing authorize step. The current behavior would remain the "fast path" when the complete URI is used.
## Environment
- Repo: `faf-user-service`
- Affected view: `src/main/kotlin/com/faforever/userservice/ui/view/oauth2/DeviceLoginView.kt`
- Hydra config: `hydra.yml` → `urls.device_verification: …/oauth2/device-login`
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.