Skip to content

Add OAuth device code flow#2352

Draft
ejsmith wants to merge 13 commits into
mainfrom
feature/oauth-device-flow
Draft

Add OAuth device code flow#2352
ejsmith wants to merge 13 commits into
mainfrom
feature/oauth-device-flow

Conversation

@ejsmith

@ejsmith ejsmith commented Jul 6, 2026

Copy link
Copy Markdown
Member

Summary

Adds RFC 8628-style OAuth device authorization for remote and headless MCP/OAuth clients. Device clients can request a device/user code, users can approve access through the Svelte verification page, and token polling now returns the expected pending, slow_down, denied, expired, or token responses.

Changes

  • Added urn:ietf:params:oauth:grant-type:device_code support, authorization server metadata, device authorization, consent, approve, deny, and token exchange endpoints.
  • Stores pending device authorizations in cache with opaque device codes, normalized user-code lookup, expiration, polling interval enforcement, and one-time consumption.
  • Added persisted OAuth application grant_types across models, dynamic registration, repository mapping, admin API, and admin UI.
  • Added Svelte /next/oauth/device verification flow with manual code entry and verification_uri_complete support.
  • Updated OAuth HTTP samples, AI tools copy, controller tests, admin tests, and OpenAPI baseline.

Compatibility

Existing authorization-code clients continue to default to authorization_code refresh_token. Authorization-code clients still require redirect URIs; device-only clients may omit redirects.

Validation

  • dotnet test --project tests/Exceptionless.Tests/Exceptionless.Tests.csproj -- --filter-class Exceptionless.Tests.Controllers.OAuthControllerTests
  • dotnet test --project tests/Exceptionless.Tests/Exceptionless.Tests.csproj -- --filter-class Exceptionless.Tests.Controllers.OAuthApplicationControllerTests
  • dotnet build --no-restore -v:q
  • cd src/Exceptionless.Web/ClientApp; npm run build
  • OpenAPI baseline snapshot regenerated and verified
  • git diff --check

ejsmith commented Jul 6, 2026

Copy link
Copy Markdown
Member Author

/preview

@github-actions github-actions Bot added the dev-preview Deploy this pull request to the shared dev environment. label Jul 6, 2026
@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown

Preview deployed

@github-actions

Copy link
Copy Markdown

Code Coverage

Package Line Rate Branch Rate Complexity Health
Exceptionless.Core 73% 65% 9705
Exceptionless.AppHost 38% 40% 139
Exceptionless.Insulation 23% 23% 205
Exceptionless.Web 84% 67% 6982
Summary 77% (22951 / 29952) 65% (10520 / 16092) 17031

@niemyjski

Copy link
Copy Markdown
Member

Follow-up feedback and thermo-nuclear audit complete.

Feedback classification

  • Submitted reviews, inline comments, and review threads: none. GitHub currently reports 0 submitted reviews, 0 inline comments, and 0 review threads, so there are no Codex, Copilot, or human review findings to accept, reject, reply to, or resolve.
  • /preview from ejsmith: old operational trigger, already fulfilled. The current preview is deployed from e880702ad49c.
  • Preview deployment bot comment: informational and current; no requested action.
  • Coverage bot comment: informational; current aggregate coverage is 77%, with no requested action or branch-specific regression finding.

Proven RCA and surgical fix

The full branch-vs-main audit found one real cache-ownership race. A stale user-code lookup read owner A, failed to find A's device authorization, and then unconditionally deleted the shared user-code key. If that key had been reassigned to owner B between the read and cleanup, A could delete B's valid mapping.

Commit e880702ad49c centralizes user-code cleanup behind compare-and-remove semantics and uses that invariant for creation rollback, authorization removal, and stale lookup cleanup. The focused regression proves that a stale owner cannot remove a reassigned mapping while the current owner still can.

Thermo-nuclear diff audit

Audited the complete effective diff against origin/main, including state transitions, authorization boundaries, serialization compatibility, generated API contracts, frontend consent behavior, helper duplication, branching, async orchestration, and file growth. No further actionable issue was proven. OAuthService remains at 998 lines and the dedicated OAuthDeviceService at 624 lines; the feature does not push a production file across 1,000 lines.

Verification

  • dotnet test --project tests/Exceptionless.Tests/Exceptionless.Tests.csproj --no-restore -- --filter-class Exceptionless.Tests.Services.OAuthDeviceServiceTests --parallel none --max-threads 1 — 1/1 passed.
  • dotnet build tests/Exceptionless.Tests/Exceptionless.Tests.csproj --no-restore --nologo --disable-build-servers -m:1 — passed with 0 warnings and 0 errors.
  • git diff --check — clean.
  • GitHub Actions run 30210204358 — API 8m18s, client 1m38s, E2E 4m51s, Docker build 4m07s, Docker publish 43s, deploy 52s, preview, version, and CLA all passed; website deploy skipped as expected.

A local integration-host attempt was blocked before the test body by unrelated shared Elasticsearch test-* alias/index conflicts from concurrent worktrees. The isolated unit regression passed, and the clean CI API suite provides the authoritative broader backend proof.

No external blocker remains. Draft/admin state was not changed.

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

Labels

dev-preview Deploy this pull request to the shared dev environment.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants