[PM-31877] Migrate LoginMethod+Tokens to Setting#973
Conversation
Replace InternalClient.login_method with state registry settings Removes the in-memory login_method Arc<RwLock> field from InternalClient and the duplicated token fields from PasswordManagerTokenHandler, routing both through the USER_LOGIN_METHOD and AUTHENTICATION_TOKENS settings already registered in persisted_state. TokenHandler::initialize_middleware now takes &StateRegistry instead of the shared login_method lock, and gains a set_sm_login_method hook so SecretsManagerTokenHandler can keep its login method in memory (SM key material deliberately does not implement Clone).
| login_method: Option<Arc<RwLock<Option<Arc<LoginMethod>>>>>, | ||
| // These are defined as optional as they are filled in when instantiating the middleware. | ||
| tokens: Option<Setting<AuthenticationTokens>>, | ||
| login_method: Option<Setting<UserLoginMethod>>, |
There was a problem hiding this comment.
@dani-garcia Would it be more appropriate to make these required? Since they are Settings Keys, the absent indicates we have no state which feels like a fatal error for a token handler.
We would have to change how the middleware is wired up though which might be tricky.
There was a problem hiding this comment.
@Hinton Yeah the reason we had to do it like that is that the token handler needed access to some of the client internals, which means we had to do this two-step initialization.
With the rehydration work we're moving towards letting the Client receive the StateRegistry directly rather than creating it internally, so my thinking is that once we're done with rehydration we can change the API to be something like this:
let registry = StateRegistry::new(...);
let handler = PMTokenHandler::new(registry); // the handler can now have all the fields initialized straight away
let client = Client::new(registry, handler); // Client now doesn't need to pass the registry to initialize_middleware, as it's already constructed.I didn't want to include these changes on this ticket as it's a bigger lift.
|
Great job! No new security vulnerabilities introduced in this pull request |
🔍 SDK Breaking Change Detection ResultsSDK Version:
Breaking change detection completed. View SDK workflow |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #973 +/- ##
=======================================
Coverage 84.20% 84.21%
=======================================
Files 390 390
Lines 47882 47904 +22
=======================================
+ Hits 40319 40342 +23
+ Misses 7563 7562 -1 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
…tion/login-tokens # Conflicts: # crates/bitwarden-core/src/client/builder.rs # crates/bitwarden-core/src/client/internal.rs
Removes the in-memory `login_method:
Arc<RwLock<Option<Arc<LoginMethod>>>>` field from `InternalClient` and
the duplicated token fields (`access_token`, `expires_on`,
`refresh_token`) from `PasswordManagerTokenHandlerInner`, replacing both
with the `Setting<T>` surface already registered in
[persisted_state.rs](crates/bitwarden-core/src/client/persisted_state.rs)
(`USER_LOGIN_METHOD`, `AUTHENTICATION_TOKENS`). The state registry is
now the single source of truth for PM user login method and tokens,
which also makes them persist across process restarts once a durable
database is wired up.
Key changes:
- `Setting<T>` now derives `Clone`
([setting.rs](crates/bitwarden-state/src/settings/setting.rs)) so token
handlers can cache their handles cheaply.
- `TokenHandler::initialize_middleware` now takes `&StateRegistry`
instead of the old `Arc<RwLock<Option<Arc<LoginMethod>>>>`. A new
default `async fn set_sm_login_method(&self, _:
ServiceAccountLoginMethod)` hook is added for Secrets Manager
([auth_tokens.rs](crates/bitwarden-core/src/auth/auth_tokens.rs)).
- `InternalClient::get_login_method` / `set_login_method` now dispatch
through the state registry for PM user logins and through the token
handler for SM logins. `get_kdf` routes through `get_login_method`. The
secret-leaking `debug!(?login_method, …)` log was dropped
([internal.rs](crates/bitwarden-core/src/client/internal.rs)).
- `ClientBuilder::build()` drops the local `login_method` `RwLock` and
passes the hoisted `state_registry` into `initialize_middleware`
([builder.rs](crates/bitwarden-core/src/client/builder.rs)).
- `PasswordManagerTokenHandler` inner struct now holds
`Option<Setting<AuthenticationTokens>>` and
`Option<Setting<UserLoginMethod>>` plus `identity_config`; `set_tokens`
/ `get_token` read & write through the settings
([password_manager_token_handler.rs](crates/bitwarden-auth/src/token_management/password_manager_token_handler.rs)).
- `SecretsManagerTokenHandler` keeps its login method in memory as
`Option<Arc<ServiceAccountLoginMethod>>` (SM key material deliberately
doesn't `Clone`), populated via `set_sm_login_method`
([secrets_manager_token_handler.rs](crates/bitwarden-auth/src/token_management/secrets_manager_token_handler.rs)).
- `bitwarden-auth` gains a direct `bitwarden-state` dependency.
## 🚨 Breaking Changes
`TokenHandler::initialize_middleware` changed signature:
```rust
// before
fn initialize_middleware(
&self,
login_method: Arc<RwLock<Option<Arc<LoginMethod>>>>,
identity_config: bitwarden_api_base::Configuration,
key_store: KeyStore<KeySlotIds>,
) -> Arc<dyn reqwest_middleware::Middleware>;
// after
fn initialize_middleware(
&self,
state_registry: &StateRegistry,
identity_config: bitwarden_api_base::Configuration,
key_store: KeyStore<KeySlotIds>,
) -> Arc<dyn reqwest_middleware::Middleware>;
```
Any out-of-tree `TokenHandler` implementers must update their signature.
Migration path: replace the `Arc<RwLock<Option<Arc<LoginMethod>>>>`
parameter with `&StateRegistry` and, if the handler previously read the
user login method through the lock, read it via
`state_registry.setting(USER_LOGIN_METHOD)`. SM-style handlers that need
a `ServiceAccountLoginMethod` should override the new
`set_sm_login_method` default and store it in-memory. All in-tree
callers (the built-in `NoopTokenHandler`, `ClientManagedTokenHandler`,
`PasswordManagerTokenHandler`, `SecretsManagerTokenHandler`, and
`ClientBuilder::build()`) are updated in this PR.
## 🎟️ Tracking https://bitwarden.atlassian.net/browse/PM-31879 ## 📔 Objective Adds `save_to_state` and `load_from_state` to `Client` in `bitwarden-core`, enabling the CLI to reconstruct a locked client from a persistent `StateRegistry` without re-authenticating. `PasswordManagerClient` exposes thin delegation wrappers for `load_from_state` and `save_to_state` for callers working at the PM layer. Some supporting types were added in `crates/bitwarden-core/src/client/rehydration.rs`: `RehydrationError` and `SaveStateData`. All foundation (`StateRegistry`, setting keys, `ClientBuilder::with_state`, crypto persistence) was committed to main in prior PRs (#871, #917, #906, #912, #957, #965, #983, #973). This PR adds the rehydration entry points on top of that infrastructure.




🎟️ Tracking
https://bitwarden.atlassian.net/browse/PM-31877
📔 Objective
Removes the in-memory
login_method: Arc<RwLock<Option<Arc<LoginMethod>>>>field fromInternalClientand the duplicated token fields (access_token,expires_on,refresh_token) fromPasswordManagerTokenHandlerInner, replacing both with theSetting<T>surface already registered in persisted_state.rs (USER_LOGIN_METHOD,AUTHENTICATION_TOKENS). The state registry is now the single source of truth for PM user login method and tokens, which also makes them persist across process restarts once a durable database is wired up.Key changes:
Setting<T>now derivesClone(setting.rs) so token handlers can cache their handles cheaply.TokenHandler::initialize_middlewarenow takes&StateRegistryinstead of the oldArc<RwLock<Option<Arc<LoginMethod>>>>. A new defaultasync fn set_sm_login_method(&self, _: ServiceAccountLoginMethod)hook is added for Secrets Manager (auth_tokens.rs).InternalClient::get_login_method/set_login_methodnow dispatch through the state registry for PM user logins and through the token handler for SM logins.get_kdfroutes throughget_login_method. The secret-leakingdebug!(?login_method, …)log was dropped (internal.rs).ClientBuilder::build()drops the locallogin_methodRwLockand passes the hoistedstate_registryintoinitialize_middleware(builder.rs).PasswordManagerTokenHandlerinner struct now holdsOption<Setting<AuthenticationTokens>>andOption<Setting<UserLoginMethod>>plusidentity_config;set_tokens/get_tokenread & write through the settings (password_manager_token_handler.rs).SecretsManagerTokenHandlerkeeps its login method in memory asOption<Arc<ServiceAccountLoginMethod>>(SM key material deliberately doesn'tClone), populated viaset_sm_login_method(secrets_manager_token_handler.rs).bitwarden-authgains a directbitwarden-statedependency.🚨 Breaking Changes
TokenHandler::initialize_middlewarechanged signature:Any out-of-tree
TokenHandlerimplementers must update their signature. Migration path: replace theArc<RwLock<Option<Arc<LoginMethod>>>>parameter with&StateRegistryand, if the handler previously read the user login method through the lock, read it viastate_registry.setting(USER_LOGIN_METHOD). SM-style handlers that need aServiceAccountLoginMethodshould override the newset_sm_login_methoddefault and store it in-memory. All in-tree callers (the built-inNoopTokenHandler,ClientManagedTokenHandler,PasswordManagerTokenHandler,SecretsManagerTokenHandler, andClientBuilder::build()) are updated in this PR.