Skip to content

Refresh rejected controller credentials before reenrollment - #241

Merged
yuval-k merged 1 commit into
agentdesktop-dev:mainfrom
rhummelmose:fix/refresh-controller-token-before-reenrollment
Nov 12, 2026
Merged

yuval-k merged 1 commit into
agentdesktop-dev:mainfrom
rhummelmose:fix/refresh-controller-token-before-reenrollment

Conversation

@rhummelmose

@rhummelmose rhummelmose commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

A controller stream can reject an access token while the device still has usable refresh credentials. The daemon currently treats that Unauthenticated response as a rejected device identity: it removes the saved enrollment and starts interactive sign-in again.

Refresh once and reconnect before removing the identity. The refresh is forced even when the local token deadline has not elapsed. Rotated credentials are persisted, and transient refresh failures retain the enrollment and use the existing retry backoff. If the refresh grant is rejected, or the controller still rejects the refreshed credentials, the existing re-enrollment behavior remains. An authenticated stream resets the recovery allowance for a later token expiry.

Validation:

  • Five regression tests exercise the actual session loop against isolated OAuth and gRPC services: successful refresh, transient failure, rejected refresh grant, rejected device after refresh, and a second rejection after an accepted stream.
  • All five fail against the original code.
  • All 35 remote connection tests pass with the change.
  • cargo fmt --all -- --check passes.
  • cargo clippy --locked -p agentdesktop-agent --lib --tests -- -D warnings passes.

These session tests use Linux's temporary file-backed secret store. A native macOS sleep/wake validation and installation of a signed client build remain pending.

Closes #245.

@yuval-k

yuval-k commented Nov 11, 2026

Copy link
Copy Markdown
Contributor

there's some inflight PRs about enrollment, named this #233 and this #236 that IIUC might make this issue disappear if merged. Please let me know if you feel like this is not the case.

@rhummelmose

Copy link
Copy Markdown
Contributor Author

there's some inflight PRs about enrollment, named this #233 and this #236 that IIUC might make this issue disappear if merged. Please let me know if you feel like this is not the case.

Thanks, I checked both PRs. #233 would avoid this for token-enrolled devices, since they use certificate authentication without OIDC revalidation. That could be useful for our MDM deployment, but OIDC-enrolled devices retain the existing behavior.

#236 changes initial enrollment and explicitly leaves existing connection, refresh, and renewal behavior unchanged.

So I think #241 is still needed for OIDC-enrolled devices: when the controller rejects an access token, the daemon currently deletes the saved identity without first attempting a silent refresh. This PR adds that recovery attempt while preserving re-enrollment if refresh is rejected or the refreshed credentials still fail.

@yuval-k

yuval-k commented Nov 12, 2026

Copy link
Copy Markdown
Contributor

I'm trying to think if it is worth separating out OIDC from enrollment. i.e. device only enroll with tokens, users enroll with OIDC. I'm still figuring out the tradeoffs here, but if we go in that direction, would that be a problem for this PR?

@rhummelmose

Copy link
Copy Markdown
Contributor Author

That separation makes sense to me. We use Intune, so enrolling the device independently and handling user OIDC sessions separately sounds like a good fit. I’m happy to adapt or supersede this PR as that design takes shape.

My immediate concern is having something reliable to roll out to our org. Could we merge this bounded recovery fix for the current OIDC flow while the broader enrollment changes are being worked out? It preserves re-enrollment when refresh is rejected or refreshed credentials still fail, and the regression tests cover those paths

@yuval-k yuval-k left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

makes sense

@yuval-k
yuval-k merged commit 36228c5 into agentdesktop-dev:main Nov 12, 2026
5 checks passed
yuval-k pushed a commit that referenced this pull request Nov 13, 2026
`main` has failed `Test and lint` since a33f11e. #241 added
`auth_recovery_tests.rs`, which calls `run_session` with 10 arguments,
and #231 added an `open_browser: bool` parameter to `run_session`. Each
passed CI on its own; together they don't compile:

```
error[E0061]: this function takes 11 arguments but 10 arguments were supplied
   --> crates/agent/src/remote/auth_recovery_tests.rs:205:13
```

This passes `false`. The flag only decides whether `oidc::enroll`
launches a browser, so it doesn't change what these tests check, and a
test should never open one.

The test module is Linux-only (`cfg(all(test, target_os = "linux"))`),
so the break doesn't show up in a macOS build. It also turns `Test and
lint` red on every open PR, since PR checks build against `main`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Controller access-token rejection discards enrollment before attempting silent refresh

2 participants