Skip to content

feat: add Device Authorization Grant (RFC 8628) as login fallback#60

Merged
steve-calvert-glean merged 4 commits intogleanwork:mainfrom
pavlo-v-chernykh:feature/device-authorization-flow
Apr 10, 2026
Merged

feat: add Device Authorization Grant (RFC 8628) as login fallback#60
steve-calvert-glean merged 4 commits intogleanwork:mainfrom
pavlo-v-chernykh:feature/device-authorization-flow

Conversation

@pavlo-v-chernykh
Copy link
Copy Markdown
Contributor

@pavlo-v-chernykh pavlo-v-chernykh commented Mar 29, 2026

Summary

  • Adds OAuth 2.0 Device Authorization Grant (RFC 8628) as a fallback when the standard Authorization Code + PKCE flow fails (e.g. when DCR is unavailable with external IdPs like Okta)
  • Login strategy: auth code + PKCE → device flow → API token prompt
  • Parses glean_device_flow_client_id and device_authorization_endpoint from Glean's protected resource metadata and OIDC/RFC 8414 discovery

Changes

  • internal/auth/auth.go — refactored Login() into tryAuthCodeLogin + device flow fallback chain; extracted saveAndPrintToken helper that persists both tokens and client credentials for refresh
  • internal/auth/discovery.go — added glean_device_flow_client_id to protected resource metadata
  • internal/auth/device.go — new file implementing RFC 8628: device code request, token polling with slow_down interval backoff, input validation (URL scheme, interval/expiry clamping, empty token rejection)
  • internal/auth/device_test.go — 14 tests covering happy path, error cases, polling states, interval clamping, and context cancellation
  • internal/auth/discovery_test.go — added test for glean_device_flow_client_id parsing
  • README.md + snippets/readme/snippet-04.sh — updated auth docs to mention device flow

Test plan

  • go test -race ./... passes
  • golangci-lint run reports 0 issues
  • markdown-code check confirms docs in sync
  • Tested end-to-end against Okta SSO — device flow login + Glean API calls working

@steve-calvert-glean
Copy link
Copy Markdown
Collaborator

steve-calvert-glean commented Apr 4, 2026

Hey @pavlo-v-chernykh — thanks for this PR! The RFC 8628 implementation is solid: the polling logic, slow_down handling, interval clamping, and test coverage are all well done.

We've been going back and forth on whether to merge this right now, and wanted to be transparent about the hesitation. The core challenge is that Glean's own OAuth authorization server doesn't support the device code flow today. The PR correctly gates activation on the server advertising glean_device_flow_client_id and device_authorization_endpoint, so the code path would only trigger for external IdPs (like Okta) that support it — but we haven't been able to test that end-to-end ourselves.

Our concern is that shipping this could create a confusing experience: if the auth code + PKCE flow fails for any reason on an instance where device flow isn't actually supported, the CLI would attempt device flow, get an error from the authorization server, and the user would see a failure message about a flow they never asked for. The fallback trigger is also quite broad — any tryAuthCodeLogin failure routes to device flow, including transient issues like network timeouts or the user closing their browser.

We're not closing this — the work is good and we want to land it. A few things that would help us get there:

  1. Narrowing the fallback trigger — only fall back to device flow when the auth code flow fails specifically because DCR/client registration is unavailable, not on any arbitrary failure
  2. Confirmation that you've tested this against an actual IdP — your PR description mentions Okta Security, so it sounds like you have. Any details on the end-to-end flow would be great
  3. Timing — we're actively working on auth improvements (PRs fix: persist oauth host without keychain prompts #68, fix: normalize host in resolveHost to prevent token lookup mismatch #72) and want to make sure everything composes cleanly

Thanks again for the contribution — this is exactly the kind of thing we'll need as more customers use external IdPs.

@steve-calvert-glean steve-calvert-glean added the enhancement New feature or request label Apr 4, 2026
@pavlo-v-chernykh pavlo-v-chernykh force-pushed the feature/device-authorization-flow branch from 65ac121 to fb5d72c Compare April 4, 2026 22:02
@pavlo-v-chernykh
Copy link
Copy Markdown
Contributor Author

Thanks for the review, Steve.

1. Narrowed fallback trigger — introduced errNoOAuthClient sentinel in dcrOrStaticClient. Device flow only activates when the server has no registration endpoint and no static client is configured. DCR failures (403, 5xx, network) surface directly — no silent switch to device flow.

2. Testing — tested against a real Okta tenant with device flow enabled. Full flow works end-to-end: device code request, verification URL, user approval, token polling.

3. Timing — rebased onto latest main (includes #68 and #72). No conflicts, tests pass.

@pavlo-v-chernykh pavlo-v-chernykh force-pushed the feature/device-authorization-flow branch from fb5d72c to 0478ef9 Compare April 8, 2026 04:30
@rwjblue-glean
Copy link
Copy Markdown
Member

@pavlo-v-chernykh I accidentally made a conflict for you (in #82), sorry about that

@pavlo-v-chernykh pavlo-v-chernykh force-pushed the feature/device-authorization-flow branch from 0478ef9 to 774f1b6 Compare April 9, 2026 18:19
@pavlo-v-chernykh
Copy link
Copy Markdown
Contributor Author

pavlo-v-chernykh commented Apr 9, 2026

@rwjblue-glean No worries, already rebased and resolved it! Let me know if there's anything else needed from my side to get this merged.

pavlo-v-chernykh and others added 4 commits April 10, 2026 08:43
When DCR is unavailable (e.g. Okta SSO), auth login now falls back to
the OAuth 2.0 Device Authorization Grant. The user approves login on a
verification page instead of a local redirect.
The previous fallback triggered on any tryAuthCodeLogin failure, including
transient issues like network timeouts or the user closing their browser.
Now device flow only activates when dcrOrStaticClient returns
errNoOAuthClient (no registration endpoint + no static client), not when
DCR was attempted and failed.

Made-with: Cursor
…est helper

PR gleanwork#79 removed the package-level discoveryHTTPClient var from
discovery.go and inlined httputil.NewHTTPClient at each call site.
The device.go file (added in a parallel branch) still referenced the
deleted var, causing a build failure after rebase.

Inline the client at both device.go call sites to match the rest of
the auth package, and remove the now-unnecessary
overrideDiscoveryHTTPClient helper and all 16 calls across test files
(httptest.NewServer creates a real TCP server reachable by any client).
- Add auth:device debug namespace for tracing device flow login
- Improve fallback message: "Your SSO provider requires device-based
  login" instead of confusing OAuth jargon
- Rewrite README auth section with scannable table showing three
  login methods and when each is used
- Add "Credential resolution order" subsection
- Note that API tokens are scoped to individual user accounts

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@steve-calvert-glean steve-calvert-glean force-pushed the feature/device-authorization-flow branch from 774f1b6 to 2dee73a Compare April 10, 2026 17:24
@steve-calvert-glean steve-calvert-glean requested a review from a team as a code owner April 10, 2026 17:24
@steve-calvert-glean steve-calvert-glean merged commit 066386b into gleanwork:main Apr 10, 2026
4 checks passed
@steve-calvert-glean
Copy link
Copy Markdown
Collaborator

Thanks again for this contribution. @pavlo-v-chernykh! It's released in v0.13.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants