diff --git a/android/java/org/chromium/chrome/browser/settings/BraveAccountSectionController.java b/android/java/org/chromium/chrome/browser/settings/BraveAccountSectionController.java
index 96c2f551b2e..8c34ebb46e8 100644
--- a/android/java/org/chromium/chrome/browser/settings/BraveAccountSectionController.java
+++ b/android/java/org/chromium/chrome/browser/settings/BraveAccountSectionController.java
@@ -192,7 +192,7 @@ public class BraveAccountSectionController
setVisibility(resendConfirmationEmailPref, false);
setVisibility(cancelRegistrationPref, false);
setVisibility(getStartedPref, false);
- } else if (state.which() == AccountState.Tag.Verification) {
+ } else if (state.getLoggedOut().verification != null) {
setVisibility(userInfoPref, false);
setVisibility(signOutPref, false);
setVisibility(almostTherePref, true);
diff --git a/browser/resources/settings/getting_started_page/brave_account_row.ts b/browser/resources/settings/getting_started_page/brave_account_row.ts
index f70e2c20fbe..0c5d68abb4b 100644
--- a/browser/resources/settings/getting_started_page/brave_account_row.ts
+++ b/browser/resources/settings/getting_started_page/brave_account_row.ts
@@ -182,60 +182,62 @@ export class SettingsBraveAccountRowElement extends I18nMixinLit(CrLitElement) {
`
),
- [AccountStateFieldTags.VERIFICATION]: () => html`
- ${this.createFirstRow(
- this.i18n(
- BraveAccountSettingsStrings
- .SETTINGS_BRAVE_ACCOUNT_VERIFICATION_ROW_TITLE),
- [
- html`
- `
- ]
- )}
-
-
- ${this.i18n(
+ [AccountStateFieldTags.LOGGED_OUT]: () =>
+ this.state!.loggedOut!.verification
+ ? html`
+ ${this.createFirstRow(
+ this.i18n(
+ BraveAccountSettingsStrings
+ .SETTINGS_BRAVE_ACCOUNT_VERIFICATION_ROW_TITLE),
+ [
+ html`
+ `
+ ]
+ )}
+
+
+ ${this.i18n(
+ BraveAccountSettingsStrings
+ .SETTINGS_BRAVE_ACCOUNT_ENTER_REGISTRATION_CODE_BUTTON_LABEL)}
+
+
+ ${this.i18n(
+ BraveAccountSettingsStrings
+ .SETTINGS_BRAVE_ACCOUNT_CANCEL_REGISTRATION_BUTTON_LABEL)}
+
+
+ `
+ : this.createFirstRow(
+ this.i18n(
BraveAccountSettingsStrings
- .SETTINGS_BRAVE_ACCOUNT_ENTER_REGISTRATION_CODE_BUTTON_LABEL)}
-
-
- ${this.i18n(
+ .SETTINGS_BRAVE_ACCOUNT_LOGGED_OUT_ROW_TITLE),
+ [this.i18n(
BraveAccountSettingsStrings
- .SETTINGS_BRAVE_ACCOUNT_CANCEL_REGISTRATION_BUTTON_LABEL)}
-
-
- `,
- [AccountStateFieldTags.LOGGED_OUT]: () => this.createFirstRow(
- this.i18n(
- BraveAccountSettingsStrings
- .SETTINGS_BRAVE_ACCOUNT_LOGGED_OUT_ROW_TITLE),
- [this.i18n(
- BraveAccountSettingsStrings
- .BRAVE_ACCOUNT_DESCRIPTION)],
- html`
-
- ${this.i18n(
- BraveAccountSettingsStrings
- .SETTINGS_BRAVE_ACCOUNT_GET_STARTED_BUTTON_LABEL)}
-
- `
- ),
+ .BRAVE_ACCOUNT_DESCRIPTION)],
+ html`
+
+ ${this.i18n(
+ BraveAccountSettingsStrings
+ .SETTINGS_BRAVE_ACCOUNT_GET_STARTED_BUTTON_LABEL)}
+
+ `
+ ),
}
return this.state === undefined
diff --git a/components/brave_account/BUILD.gn b/components/brave_account/BUILD.gn
index 0e21e653113..c8a756231f2 100644
--- a/components/brave_account/BUILD.gn
+++ b/components/brave_account/BUILD.gn
@@ -11,10 +11,8 @@ source_set("brave_account") {
"brave_account_constants.h",
"brave_account_service.cc",
"brave_account_service.h",
- "brave_account_service_constants.h",
"brave_account_ui_base.h",
"brave_account_utils.h",
- "pref_names.h",
"prefs.cc",
"prefs.h",
]
@@ -22,6 +20,7 @@ source_set("brave_account") {
deps = [
"//base",
"//brave/components/brave_account:features",
+ "//brave/components/brave_account:internal",
"//brave/components/brave_account/endpoint_client",
"//brave/components/brave_account/endpoints",
"//brave/components/brave_account/mojom",
@@ -49,6 +48,26 @@ source_set("brave_account") {
}
}
+source_set("internal") {
+ visibility = [
+ ":*",
+ "//brave/components/email_aliases:service",
+ ]
+
+ sources = [
+ "brave_account_service_constants.h",
+ "brave_account_state_prefs.cc",
+ "brave_account_state_prefs.h",
+ "pref_names.h",
+ ]
+
+ public_deps = [
+ "//base",
+ "//brave/components/brave_account/mojom",
+ "//components/prefs",
+ ]
+}
+
source_set("features") {
sources = [
"features.cc",
@@ -95,6 +114,7 @@ source_set("unit_tests") {
"//base/test:test_support",
"//brave/components/brave_account",
"//brave/components/brave_account:features",
+ "//brave/components/brave_account:internal",
"//brave/components/brave_account:test_support",
"//brave/components/brave_account/endpoint_client:test_support",
"//brave/components/brave_account/endpoints",
diff --git a/components/brave_account/brave_account_service.cc b/components/brave_account/brave_account_service.cc
index 41e31c6019d..6f4548f9981 100644
--- a/components/brave_account/brave_account_service.cc
+++ b/components/brave_account/brave_account_service.cc
@@ -15,20 +15,17 @@
#include "base/check_is_test.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
-#include "base/json/values_util.h"
#include "base/no_destructor.h"
#include "base/notimplemented.h"
-#include "base/strings/strcat.h"
#include "base/types/expected.h"
-#include "base/values.h"
#include "brave/components/brave_account/brave_account_service_constants.h"
+#include "brave/components/brave_account/brave_account_state_prefs.h"
#include "brave/components/brave_account/brave_account_utils.h"
#include "brave/components/brave_account/endpoint_client/client.h"
#include "brave/components/brave_account/endpoint_client/with_headers.h"
#include "brave/components/brave_account/endpoints/auth_logout.h"
#include "brave/components/brave_account/endpoints/error_body.h"
#include "brave/components/brave_account/endpoints/verify_delete.h"
-#include "brave/components/brave_account/pref_names.h"
#include "components/os_crypt/async/browser/os_crypt_async.h"
#include "components/prefs/pref_service.h"
#include "net/http/http_request_headers.h"
@@ -159,9 +156,8 @@ BraveAccountService::BraveAccountService(
PrefService* pref_service,
scoped_refptr url_loader_factory,
os_crypt_async::OSCryptAsync* os_crypt_async)
- : pref_service_(pref_service),
+ : account_state_prefs_(CHECK_DEREF(pref_service)),
url_loader_factory_(std::move(url_loader_factory)) {
- CHECK(pref_service_);
CHECK(url_loader_factory_);
CHECK_DEREF(os_crypt_async)
@@ -191,21 +187,9 @@ void BraveAccountService::FinishInitialization(
os_crypt_async::Encryptor encryptor) {
encryptor_ = std::move(encryptor);
- pref_verification_token_.Init(
- prefs::kBraveAccountVerificationToken, pref_service_,
- base::BindRepeating(&BraveAccountService::OnVerificationTokenChanged,
- base::Unretained(this)));
-
- pref_authentication_token_.Init(
- prefs::kBraveAccountAuthenticationToken, pref_service_,
- base::BindRepeating(&BraveAccountService::OnAuthenticationTokenChanged,
- base::Unretained(this)));
- OnAuthenticationTokenChanged();
-
- pref_email_address_.Init(
- prefs::kBraveAccountEmailAddress, pref_service_,
- base::BindRepeating(&BraveAccountService::OnEmailAddressChanged,
- base::Unretained(this)));
+ account_state_prefs_.StartObserving(base::BindRepeating(
+ &BraveAccountService::OnAccountStateChanged, base::Unretained(this)));
+ OnAccountStateChanged();
for (auto& pending_receiver : pending_receivers_) {
authentication_receivers_.Add(this, std::move(pending_receiver));
@@ -217,7 +201,7 @@ void BraveAccountService::AddObserver(
mojo::PendingRemote observer) {
const auto observer_id = observers_.Add(std::move(observer));
CHECK_DEREF(observers_.Get(observer_id))
- .OnAccountStateChanged(GetAccountState());
+ .OnAccountStateChanged(account_state_prefs_.GetAccountState());
}
void BraveAccountService::RegisterInitialize(
@@ -269,11 +253,12 @@ void BraveAccountService::RegisterVerify(const std::string& code,
CHECK(!code.empty());
const auto encrypted_verification_token =
- pref_service_->GetString(prefs::kBraveAccountVerificationToken);
+ account_state_prefs_.GetVerificationToken(
+ mojom::LoggedOutVerificationIntent::kRegistration);
if (encrypted_verification_token.empty()) {
return std::move(callback).Run(
base::unexpected(MakeClientError(
- mojom::RegisterClientErrorCode::kUserNotInTheVerificationState)));
+ mojom::RegisterClientErrorCode::kNoRegistrationInProgress)));
}
const auto verification_token = Decrypt(encrypted_verification_token);
@@ -295,12 +280,13 @@ void BraveAccountService::RegisterVerify(const std::string& code,
void BraveAccountService::ResendConfirmationEmail(
ResendConfirmationEmailCallback callback) {
const auto encrypted_verification_token =
- pref_service_->GetString(prefs::kBraveAccountVerificationToken);
+ account_state_prefs_.GetVerificationToken(
+ mojom::LoggedOutVerificationIntent::kRegistration);
if (encrypted_verification_token.empty()) {
return std::move(callback).Run(
base::unexpected(MakeClientError(
mojom::ResendConfirmationEmailClientErrorCode::
- kUserNotInTheVerificationState)));
+ kNoRegistrationInProgress)));
}
const auto verification_token = Decrypt(encrypted_verification_token);
@@ -325,9 +311,10 @@ void BraveAccountService::ResendConfirmationEmail(
void BraveAccountService::CancelRegistration() {
const auto encrypted_verification_token =
- pref_service_->GetString(prefs::kBraveAccountVerificationToken);
+ account_state_prefs_.GetVerificationToken(
+ mojom::LoggedOutVerificationIntent::kRegistration);
- pref_service_->ClearPref(prefs::kBraveAccountVerificationToken);
+ account_state_prefs_.SetLoggedOut();
const auto verification_token = Decrypt(encrypted_verification_token);
if (verification_token.empty()) {
@@ -385,9 +372,9 @@ void BraveAccountService::LoginFinalize(
void BraveAccountService::LogOut() {
const auto encrypted_authentication_token =
- pref_service_->GetString(prefs::kBraveAccountAuthenticationToken);
+ account_state_prefs_.GetAuthenticationToken();
- pref_service_->ClearPref(prefs::kBraveAccountAuthenticationToken);
+ account_state_prefs_.SetLoggedOut();
const auto authentication_token = Decrypt(encrypted_authentication_token);
if (authentication_token.empty()) {
@@ -406,14 +393,15 @@ void BraveAccountService::GetServiceToken(mojom::Service service,
GetServiceTokenCallback callback) {
CHECK(service != mojom::Service::kAccounts);
std::string service_name(kServiceToString.at(service));
- if (auto service_token = GetCachedServiceToken(service_name);
+ if (auto service_token =
+ Decrypt(account_state_prefs_.GetCachedServiceToken(service_name));
!service_token.empty()) {
return std::move(callback).Run(
mojom::GetServiceTokenResult::New(std::move(service_token)));
}
auto encrypted_authentication_token =
- pref_service_->GetString(prefs::kBraveAccountAuthenticationToken);
+ account_state_prefs_.GetAuthenticationToken();
if (encrypted_authentication_token.empty()) {
return std::move(callback).Run(
base::unexpected(MakeClientError(
@@ -513,8 +501,9 @@ void BraveAccountService::OnRegisterFinalize(
.and_then([&](auto success_body)
-> base::expected {
- pref_service_->SetString(prefs::kBraveAccountVerificationToken,
- encrypted_verification_token);
+ account_state_prefs_.SetLoggedOutWithVerification(
+ encrypted_verification_token,
+ mojom::LoggedOutVerificationIntent::kRegistration);
return mojom::RegisterFinalizeResult::New();
});
@@ -560,11 +549,8 @@ void BraveAccountService::OnRegisterVerify(RegisterVerifyCallback callback,
kAuthenticationTokenEncryptionFailed));
}
- pref_service_->SetString(prefs::kBraveAccountEmailAddress,
- success_body.email);
- pref_service_->SetString(prefs::kBraveAccountAuthenticationToken,
- encrypted_authentication_token);
- pref_service_->ClearPref(prefs::kBraveAccountVerificationToken);
+ account_state_prefs_.SetLoggedIn(success_body.email,
+ encrypted_authentication_token);
return mojom::RegisterVerifyResult::New();
});
@@ -592,10 +578,6 @@ void BraveAccountService::OnResendConfirmationEmail(
std::move(response.body->error()))));
}
-void BraveAccountService::OnVerificationTokenChanged() {
- NotifyObservers();
-}
-
void BraveAccountService::OnLoginInitialize(LoginInitializeCallback callback,
LoginInit::Response response) {
if (!response.body) {
@@ -678,10 +660,8 @@ void BraveAccountService::OnLoginFinalize(LoginFinalizeCallback callback,
kAuthenticationTokenEncryptionFailed));
}
- pref_service_->SetString(prefs::kBraveAccountEmailAddress,
- success_body.email);
- pref_service_->SetString(prefs::kBraveAccountAuthenticationToken,
- encrypted_authentication_token);
+ account_state_prefs_.SetLoggedIn(success_body.email,
+ encrypted_authentication_token);
return mojom::LoginFinalizeResult::New();
});
@@ -689,12 +669,13 @@ void BraveAccountService::OnLoginFinalize(LoginFinalizeCallback callback,
std::move(callback).Run(std::move(result));
}
-void BraveAccountService::OnAuthenticationTokenChanged() {
- NotifyObservers();
+void BraveAccountService::OnAccountStateChanged() {
+ const auto account_state = account_state_prefs_.GetAccountState();
+ for (auto& observer : observers_) {
+ observer->OnAccountStateChanged(account_state.Clone());
+ }
- if (pref_authentication_token_.GetValue().empty()) {
- pref_service_->ClearPref(prefs::kBraveAccountEmailAddress);
- pref_service_->ClearPref(prefs::kBraveAccountServiceTokens);
+ if (!account_state->is_logged_in()) {
return auth_validate_timer_.Stop();
}
@@ -715,7 +696,7 @@ void BraveAccountService::AuthValidate(
current_auth_validate_request.reset();
const auto encrypted_authentication_token =
- pref_service_->GetString(prefs::kBraveAccountAuthenticationToken);
+ account_state_prefs_.GetAuthenticationToken();
if (encrypted_authentication_token.empty()) {
return;
}
@@ -747,51 +728,17 @@ void BraveAccountService::OnAuthValidate(AuthValidate::Response response) {
: "";
if (!email.empty()) {
- pref_service_->SetString(prefs::kBraveAccountEmailAddress, email);
+ account_state_prefs_.UpdateEmail(email);
} else if (response.status_code >= 400 && response.status_code < 500) {
- // Clear the auth token (and stop polling) to prevent
- // presenting invalid state to the user and issuing invalid requests.
- return pref_service_->ClearPref(prefs::kBraveAccountAuthenticationToken);
+ // Force logged-out (and stop polling) to prevent presenting invalid state
+ // to the user and issuing invalid requests.
+ return account_state_prefs_.SetLoggedOut();
}
// Replace watchdog timer with the normal cadence.
ScheduleAuthValidate(kAuthValidatePollInterval);
}
-void BraveAccountService::OnEmailAddressChanged() {
- // Only notify observers if logged in, since the email is only relevant in
- // the LoggedIn state.
- if (!pref_authentication_token_.GetValue().empty()) {
- NotifyObservers();
- }
-}
-
-void BraveAccountService::NotifyObservers() {
- const auto state = GetAccountState();
- for (auto& observer : observers_) {
- observer->OnAccountStateChanged(state.Clone());
- }
-}
-
-mojom::AccountStatePtr BraveAccountService::GetAccountState() const {
- if (!pref_service_->GetString(prefs::kBraveAccountAuthenticationToken)
- .empty()) {
- std::string email =
- pref_service_->GetString(prefs::kBraveAccountEmailAddress);
- CHECK(!email.empty());
- return mojom::AccountState::NewLoggedIn(
- mojom::LoggedInState::New(std::move(email)));
- }
-
- if (!pref_service_->GetString(prefs::kBraveAccountVerificationToken)
- .empty()) {
- return mojom::AccountState::NewVerification(
- mojom::VerificationState::New());
- }
-
- return mojom::AccountState::NewLoggedOut(mojom::LoggedOutState::New());
-}
-
void BraveAccountService::OnGetServiceToken(
const std::string& expected_encrypted_authentication_token,
const std::string& service_name,
@@ -803,7 +750,7 @@ void BraveAccountService::OnGetServiceToken(
// token as it belongs to a different (or no longer valid) authentication
// session.
if (const auto current_encrypted_authentication_token =
- pref_service_->GetString(prefs::kBraveAccountAuthenticationToken);
+ account_state_prefs_.GetAuthenticationToken();
current_encrypted_authentication_token !=
expected_encrypted_authentication_token) {
return std::move(callback).Run(
@@ -849,18 +796,8 @@ void BraveAccountService::OnGetServiceToken(
kServiceTokenEncryptionFailed));
}
- auto service_tokens =
- pref_service_->GetDict(prefs::kBraveAccountServiceTokens)
- .Clone();
- service_tokens.Set(service_name,
- base::DictValue()
- .Set(prefs::keys::kServiceToken,
- std::move(encrypted_service_token))
- .Set(prefs::keys::kLastFetched,
- base::TimeToValue(base::Time::Now())));
-
- pref_service_->SetDict(prefs::kBraveAccountServiceTokens,
- std::move(service_tokens));
+ account_state_prefs_.CacheServiceToken(
+ service_name, std::move(encrypted_service_token));
return mojom::GetServiceTokenResult::New(
std::move(success_body.auth_token));
@@ -869,35 +806,6 @@ void BraveAccountService::OnGetServiceToken(
std::move(callback).Run(std::move(result));
}
-std::string BraveAccountService::GetCachedServiceToken(
- const std::string& service_name) const {
- const auto* service =
- pref_service_->GetDict(prefs::kBraveAccountServiceTokens)
- .FindDict(service_name);
- if (!service) {
- return "";
- }
-
- const auto* encrypted_service_token =
- service->FindString(prefs::keys::kServiceToken);
- const auto* last_fetched_value = service->Find(prefs::keys::kLastFetched);
-
- if (!encrypted_service_token || !last_fetched_value) {
- return "";
- }
-
- const auto last_fetched_time = base::ValueToTime(*last_fetched_value);
- if (!last_fetched_time) {
- return "";
- }
-
- if (base::Time::Now() - *last_fetched_time >= kServiceTokenMaxAge) {
- return "";
- }
-
- return Decrypt(*encrypted_service_token);
-}
-
std::string BraveAccountService::Encrypt(const std::string& plain_text) const {
if (plain_text.empty()) {
return std::string();
diff --git a/components/brave_account/brave_account_service.h b/components/brave_account/brave_account_service.h
index 7f67c4115c9..aa88b8fe629 100644
--- a/components/brave_account/brave_account_service.h
+++ b/components/brave_account/brave_account_service.h
@@ -11,11 +11,11 @@
#include
#include "base/functional/callback.h"
-#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
+#include "brave/components/brave_account/brave_account_state_prefs.h"
#include "brave/components/brave_account/endpoint_client/request_handle.h"
#include "brave/components/brave_account/endpoints/auth_validate.h"
#include "brave/components/brave_account/endpoints/login_finalize.h"
@@ -28,7 +28,6 @@
#include "brave/components/brave_account/mojom/brave_account.mojom.h"
#include "components/keyed_service/core/keyed_service.h"
#include "components/os_crypt/async/common/encryptor.h"
-#include "components/prefs/pref_member.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
@@ -125,15 +124,13 @@ class BraveAccountService : public KeyedService, public mojom::Authentication {
void OnResendConfirmationEmail(ResendConfirmationEmailCallback callback,
endpoints::VerifyResend::Response response);
- void OnVerificationTokenChanged();
-
void OnLoginInitialize(LoginInitializeCallback callback,
endpoints::LoginInit::Response response);
void OnLoginFinalize(LoginFinalizeCallback callback,
endpoints::LoginFinalize::Response response);
- void OnAuthenticationTokenChanged();
+ void OnAccountStateChanged();
void ScheduleAuthValidate(
base::TimeDelta delay = base::Seconds(0),
@@ -144,33 +141,22 @@ class BraveAccountService : public KeyedService, public mojom::Authentication {
void OnAuthValidate(endpoints::AuthValidate::Response response);
- void OnEmailAddressChanged();
-
- void NotifyObservers();
-
- mojom::AccountStatePtr GetAccountState() const;
-
void OnGetServiceToken(
const std::string& expected_encrypted_authentication_token,
const std::string& service_name,
GetServiceTokenCallback callback,
endpoints::ServiceToken::Response response);
- std::string GetCachedServiceToken(const std::string& service_name) const;
-
std::string Encrypt(const std::string& plain_text) const;
std::string Decrypt(const std::string& base64) const;
- const raw_ptr pref_service_;
+ AccountStatePrefs account_state_prefs_;
scoped_refptr url_loader_factory_;
std::optional encryptor_;
std::vector> pending_receivers_;
mojo::ReceiverSet authentication_receivers_;
mojo::RemoteSet observers_;
- StringPrefMember pref_verification_token_;
- StringPrefMember pref_authentication_token_;
- StringPrefMember pref_email_address_;
base::OneShotTimer auth_validate_timer_;
base::WeakPtrFactory weak_factory_{this};
};
diff --git a/components/brave_account/brave_account_service_constants.h b/components/brave_account/brave_account_service_constants.h
index a8c8493a67d..b43be905c16 100644
--- a/components/brave_account/brave_account_service_constants.h
+++ b/components/brave_account/brave_account_service_constants.h
@@ -10,11 +10,10 @@
namespace brave_account {
-inline constexpr base::TimeDelta kVerifyResultPollInterval = base::Seconds(5);
inline constexpr base::TimeDelta kAuthValidatePollInterval = base::Minutes(2);
inline constexpr base::TimeDelta kVerifyResendTimeout = base::Seconds(10);
-// If a polling request (VerifyResult or AuthValidate) doesn't complete within
+// If a polling request (AuthValidate) doesn't complete within
// this interval, it will be canceled and retried to prevent hung requests from
// stopping the periodic polling indefinitely.
inline constexpr base::TimeDelta kWatchdogInterval = base::Seconds(15);
diff --git a/components/brave_account/brave_account_service_unittest.cc b/components/brave_account/brave_account_service_unittest.cc
index a2a7f47cff0..31c0cc11883 100644
--- a/components/brave_account/brave_account_service_unittest.cc
+++ b/components/brave_account/brave_account_service_unittest.cc
@@ -18,6 +18,7 @@
#include "base/values.h"
#include "brave/components/brave_account/brave_account_service_constants.h"
#include "brave/components/brave_account/brave_account_service_test.h"
+#include "brave/components/brave_account/brave_account_state_prefs.h"
#include "brave/components/brave_account/endpoints/auth_validate.h"
#include "brave/components/brave_account/endpoints/login_finalize.h"
#include "brave/components/brave_account/endpoints/login_init.h"
@@ -29,6 +30,7 @@
#include "brave/components/brave_account/mojom/brave_account.mojom.h"
#include "brave/components/brave_account/pref_names.h"
#include "components/prefs/pref_service.h"
+#include "components/prefs/scoped_user_pref_update.h"
#include "net/http/http_status_code.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -82,6 +84,7 @@ struct AuthenticationObserverTestCase {
static void Run(const AuthenticationObserverTestCase& test_case,
PrefService& pref_service,
mojom::Authentication& authentication) {
+ AccountStatePrefs account_state_prefs(pref_service);
const auto account_state_eq = [](const mojom::AccountStatePtr& expected) {
return testing::Truly([&](const mojom::AccountStatePtr& state) {
return state.Equals(expected);
@@ -90,15 +93,15 @@ struct AuthenticationObserverTestCase {
switch (CHECK_DEREF(test_case.from).which()) {
case mojom::AccountState::Tag::kLoggedOut:
- break;
- case mojom::AccountState::Tag::kVerification:
- pref_service.SetString(prefs::kBraveAccountVerificationToken,
- EncryptedVerificationToken());
+ if (test_case.from->get_logged_out()->verification) {
+ account_state_prefs.SetLoggedOutWithVerification(
+ EncryptedVerificationToken(),
+ mojom::LoggedOutVerificationIntent::kRegistration);
+ }
break;
case mojom::AccountState::Tag::kLoggedIn:
- pref_service.SetString(prefs::kBraveAccountEmailAddress, kEmailAddress);
- pref_service.SetString(prefs::kBraveAccountAuthenticationToken,
- EncryptedAuthenticationToken());
+ account_state_prefs.SetLoggedIn(kEmailAddress,
+ EncryptedAuthenticationToken());
break;
}
@@ -118,19 +121,19 @@ struct AuthenticationObserverTestCase {
switch (test_case.action) {
case StateAction::kSwitchToVerification:
- pref_service.SetString(prefs::kBraveAccountVerificationToken,
- EncryptedVerificationToken());
+ account_state_prefs.SetLoggedOutWithVerification(
+ EncryptedVerificationToken(),
+ mojom::LoggedOutVerificationIntent::kRegistration);
break;
case StateAction::kSwitchToLoggedIn:
- pref_service.SetString(prefs::kBraveAccountEmailAddress, kEmailAddress);
- pref_service.SetString(prefs::kBraveAccountAuthenticationToken,
- EncryptedAuthenticationToken());
+ account_state_prefs.SetLoggedIn(kEmailAddress,
+ EncryptedAuthenticationToken());
break;
case StateAction::kSwitchToLoggedOut:
- pref_service.ClearPref(prefs::kBraveAccountAuthenticationToken);
+ account_state_prefs.SetLoggedOut();
break;
case StateAction::kUpdateEmailAddress:
- pref_service.SetString(prefs::kBraveAccountEmailAddress, "new_email");
+ account_state_prefs.UpdateEmail("new_email");
break;
}
@@ -145,17 +148,30 @@ struct AuthenticationObserverTestCase {
namespace {
+mojom::AccountStatePtr LoggedOut() {
+ return mojom::AccountState::NewLoggedOut(mojom::LoggedOutState::New(nullptr));
+}
+
+mojom::AccountStatePtr LoggedOutWithVerification() {
+ return mojom::AccountState::NewLoggedOut(
+ mojom::LoggedOutState::New(mojom::LoggedOutVerification::New(
+ mojom::LoggedOutVerificationIntent::kRegistration)));
+}
+
+mojom::AccountStatePtr LoggedIn(const std::string& email) {
+ return mojom::AccountState::NewLoggedIn(
+ mojom::LoggedInState::New(email, nullptr));
+}
+
const AuthenticationObserverTestCase*
AuthenticationObserverLoggedOutToVerification() {
static const base::NoDestructor
kAuthenticationObserverLoggedOutToVerification(
{.test_name = "authentication_observer_logged_out_to_verification",
- .from =
- mojom::AccountState::NewLoggedOut(mojom::LoggedOutState::New()),
+ .from = LoggedOut(),
.action = AuthenticationObserverTestCase::StateAction::
kSwitchToVerification,
- .to = mojom::AccountState::NewVerification(
- mojom::VerificationState::New())});
+ .to = LoggedOutWithVerification()});
return kAuthenticationObserverLoggedOutToVerification.get();
}
@@ -164,12 +180,10 @@ AuthenticationObserverVerificationToLoggedIn() {
static const base::NoDestructor
kAuthenticationObserverVerificationToLoggedIn(
{.test_name = "authentication_observer_verification_to_logged_in",
- .from = mojom::AccountState::NewVerification(
- mojom::VerificationState::New()),
+ .from = LoggedOutWithVerification(),
.action =
AuthenticationObserverTestCase::StateAction::kSwitchToLoggedIn,
- .to = mojom::AccountState::NewLoggedIn(
- mojom::LoggedInState::New(kEmailAddress))});
+ .to = LoggedIn(kEmailAddress)});
return kAuthenticationObserverVerificationToLoggedIn.get();
}
@@ -178,12 +192,10 @@ AuthenticationObserverLoggedInToLoggedOut() {
static const base::NoDestructor
kAuthenticationObserverLoggedInToLoggedOut(
{.test_name = "authentication_observer_logged_in_to_logged_out",
- .from = mojom::AccountState::NewLoggedIn(
- mojom::LoggedInState::New(kEmailAddress)),
+ .from = LoggedIn(kEmailAddress),
.action =
AuthenticationObserverTestCase::StateAction::kSwitchToLoggedOut,
- .to = mojom::AccountState::NewLoggedOut(
- mojom::LoggedOutState::New())});
+ .to = LoggedOut()});
return kAuthenticationObserverLoggedInToLoggedOut.get();
}
@@ -192,12 +204,10 @@ AuthenticationObserverLoggedOutToLoggedIn() {
static const base::NoDestructor
kAuthenticationObserverLoggedOutToLoggedIn(
{.test_name = "authentication_observer_logged_out_to_logged_in",
- .from =
- mojom::AccountState::NewLoggedOut(mojom::LoggedOutState::New()),
+ .from = LoggedOut(),
.action =
AuthenticationObserverTestCase::StateAction::kSwitchToLoggedIn,
- .to = mojom::AccountState::NewLoggedIn(
- mojom::LoggedInState::New(kEmailAddress))});
+ .to = LoggedIn(kEmailAddress)});
return kAuthenticationObserverLoggedOutToLoggedIn.get();
}
@@ -207,12 +217,10 @@ AuthenticationObserverLoggedInToLoggedInEmailChange() {
kAuthenticationObserverLoggedInToLoggedInEmailChange(
{.test_name =
"authentication_observer_logged_in_to_logged_in_email_change",
- .from = mojom::AccountState::NewLoggedIn(
- mojom::LoggedInState::New(kEmailAddress)),
+ .from = LoggedIn(kEmailAddress),
.action =
AuthenticationObserverTestCase::StateAction::kUpdateEmailAddress,
- .to = mojom::AccountState::NewLoggedIn(
- mojom::LoggedInState::New("new_email"))});
+ .to = LoggedIn("new_email")});
return kAuthenticationObserverLoggedInToLoggedInEmailChange.get();
}
@@ -900,7 +908,14 @@ TEST_P(BraveAccountServiceRegisterFinalizeTest,
if (const auto& test_case = CHECK_DEREF(this->GetParam());
test_case.mojo_expected.has_value()) {
- EXPECT_EQ(pref_service_.GetString(prefs::kBraveAccountVerificationToken),
+ AccountStatePrefs account_state_prefs(pref_service_);
+ const auto state = account_state_prefs.GetAccountState();
+ ASSERT_TRUE(state->is_logged_out());
+ ASSERT_TRUE(state->get_logged_out()->verification);
+ EXPECT_EQ(state->get_logged_out()->verification->intent,
+ mojom::LoggedOutVerificationIntent::kRegistration);
+ EXPECT_EQ(account_state_prefs.GetVerificationToken(
+ mojom::LoggedOutVerificationIntent::kRegistration),
test_case.encrypted_verification_token);
}
}
@@ -932,8 +947,10 @@ struct RegisterVerifyTestCase {
mojom::Authentication& authentication,
base::OnceCallback callback) {
if (!test_case.encrypted_verification_token.empty()) {
- pref_service.SetString(prefs::kBraveAccountVerificationToken,
- test_case.encrypted_verification_token);
+ AccountStatePrefs(pref_service)
+ .SetLoggedOutWithVerification(
+ test_case.encrypted_verification_token,
+ mojom::LoggedOutVerificationIntent::kRegistration);
}
authentication.RegisterVerify(
@@ -941,28 +958,27 @@ struct RegisterVerifyTestCase {
std::move(callback).Then(base::BindOnce(
[](PrefService* pref_service,
std::string initial_verification_token, bool success) {
+ AccountStatePrefs account_state_prefs(*pref_service);
+ const auto state = account_state_prefs.GetAccountState();
if (success) {
- EXPECT_EQ(
- pref_service->GetString(prefs::kBraveAccountEmailAddress),
- kEmailAddress);
- EXPECT_EQ(pref_service->GetString(
- prefs::kBraveAccountAuthenticationToken),
+ ASSERT_TRUE(state->is_logged_in());
+ EXPECT_EQ(state->get_logged_in()->email, kEmailAddress);
+ EXPECT_FALSE(state->get_logged_in()->verification);
+ EXPECT_EQ(account_state_prefs.GetAuthenticationToken(),
EncryptedAuthenticationToken());
- EXPECT_TRUE(
- pref_service
- ->GetString(prefs::kBraveAccountVerificationToken)
- .empty());
} else {
- EXPECT_TRUE(
- pref_service->GetString(prefs::kBraveAccountEmailAddress)
- .empty());
- EXPECT_TRUE(
- pref_service
- ->GetString(prefs::kBraveAccountAuthenticationToken)
- .empty());
- EXPECT_EQ(pref_service->GetString(
- prefs::kBraveAccountVerificationToken),
- initial_verification_token);
+ ASSERT_TRUE(state->is_logged_out());
+ if (initial_verification_token.empty()) {
+ EXPECT_FALSE(state->get_logged_out()->verification);
+ } else {
+ ASSERT_TRUE(state->get_logged_out()->verification);
+ EXPECT_EQ(state->get_logged_out()->verification->intent,
+ mojom::LoggedOutVerificationIntent::kRegistration);
+ EXPECT_EQ(
+ account_state_prefs.GetVerificationToken(
+ mojom::LoggedOutVerificationIntent::kRegistration),
+ initial_verification_token);
+ }
}
},
base::Unretained(&pref_service),
@@ -994,7 +1010,7 @@ const RegisterVerifyTestCase* RegisterVerifyVerificationTokenEmpty() {
base::unexpected(mojom::RegisterError::NewClientError(
mojom::RegisterClientError::New(
mojom::RegisterClientErrorCode::
- kUserNotInTheVerificationState))),
+ kNoRegistrationInProgress))),
});
return kRegisterVerifyVerificationTokenEmpty.get();
}
@@ -1353,8 +1369,10 @@ struct ResendConfirmationEmailTestCase {
mojom::Authentication& authentication,
base::OnceCallback callback) {
if (!test_case.encrypted_verification_token.empty()) {
- pref_service.SetString(prefs::kBraveAccountVerificationToken,
- test_case.encrypted_verification_token);
+ AccountStatePrefs(pref_service)
+ .SetLoggedOutWithVerification(
+ test_case.encrypted_verification_token,
+ mojom::LoggedOutVerificationIntent::kRegistration);
}
authentication.ResendConfirmationEmail(std::move(callback));
@@ -1381,7 +1399,7 @@ ResendConfirmationEmailVerificationTokenEmpty() {
mojom::ResendConfirmationEmailError::NewClientError(
mojom::ResendConfirmationEmailClientError::New(
mojom::ResendConfirmationEmailClientErrorCode::
- kUserNotInTheVerificationState))),
+ kNoRegistrationInProgress))),
});
return kResendConfirmationEmailVerificationTokenEmpty.get();
}
@@ -1609,19 +1627,24 @@ struct AuthValidateTestCase {
PrefService& pref_service,
base::test::TaskEnvironment& task_environment,
base::OneShotTimer& auth_validate_timer) {
+ AccountStatePrefs account_state_prefs(pref_service);
if (test_case.logged_in) {
- pref_service.SetString(prefs::kBraveAccountEmailAddress, kEmailAddress);
- pref_service.SetString(prefs::kBraveAccountAuthenticationToken,
- EncryptedAuthenticationToken());
+ account_state_prefs.SetLoggedIn(kEmailAddress,
+ EncryptedAuthenticationToken());
}
task_environment.FastForwardBy(kAuthValidatePollInterval -
base::Seconds(1));
- EXPECT_EQ(pref_service.GetString(prefs::kBraveAccountEmailAddress),
- test_case.expected_email);
- EXPECT_EQ(pref_service.GetString(prefs::kBraveAccountAuthenticationToken),
- test_case.expected_authentication_token);
+ const auto state = account_state_prefs.GetAccountState();
+ if (test_case.expected_authentication_token.empty()) {
+ EXPECT_TRUE(state->is_logged_out());
+ } else {
+ ASSERT_TRUE(state->is_logged_in());
+ EXPECT_EQ(state->get_logged_in()->email, test_case.expected_email);
+ EXPECT_EQ(account_state_prefs.GetAuthenticationToken(),
+ test_case.expected_authentication_token);
+ }
if (test_case.expected_auth_validate_timer_delay.is_zero()) {
EXPECT_FALSE(auth_validate_timer.IsRunning());
} else {
@@ -1813,16 +1836,20 @@ struct CancelRegistrationTestCase {
static void Run(const CancelRegistrationTestCase& test_case,
PrefService& pref_service,
mojom::Authentication& authentication) {
- pref_service.SetString(prefs::kBraveAccountVerificationToken,
- test_case.encrypted_verification_token);
+ AccountStatePrefs account_state_prefs(pref_service);
+ if (!test_case.encrypted_verification_token.empty()) {
+ account_state_prefs.SetLoggedOutWithVerification(
+ test_case.encrypted_verification_token,
+ mojom::LoggedOutVerificationIntent::kRegistration);
+ }
authentication.CancelRegistration();
- EXPECT_EQ(pref_service.GetString(prefs::kBraveAccountVerificationToken),
- test_case.expected_verification_token);
+ const auto state = account_state_prefs.GetAccountState();
+ ASSERT_TRUE(state->is_logged_out());
+ EXPECT_FALSE(state->get_logged_out()->verification);
}
std::string test_name;
std::string encrypted_verification_token;
- std::string expected_verification_token;
};
namespace {
@@ -1832,7 +1859,6 @@ const CancelRegistrationTestCase* CancelRegistrationVerificationTokenEmpty() {
kCancelRegistrationVerificationTokenEmpty({
.test_name = "cancel_registration_verification_token_empty",
.encrypted_verification_token = "",
- .expected_verification_token = "",
});
return kCancelRegistrationVerificationTokenEmpty.get();
}
@@ -1843,7 +1869,6 @@ CancelRegistrationVerificationTokenNonEmpty() {
kCancelRegistrationVerificationTokenNonEmpty({
.test_name = "cancel_registration_verification_token_non_empty",
.encrypted_verification_token = EncryptedVerificationToken(),
- .expected_verification_token = "",
});
return kCancelRegistrationVerificationTokenNonEmpty.get();
}
@@ -1869,19 +1894,20 @@ struct LogOutTestCase {
static void Run(const LogOutTestCase& test_case,
PrefService& pref_service,
mojom::Authentication& authentication) {
- pref_service.SetString(prefs::kBraveAccountEmailAddress,
- test_case.email_address);
- pref_service.SetString(prefs::kBraveAccountAuthenticationToken,
- test_case.encrypted_authentication_token);
+ AccountStatePrefs account_state_prefs(pref_service);
+ if (!test_case.encrypted_authentication_token.empty()) {
+ account_state_prefs.SetLoggedIn(test_case.email_address,
+ test_case.encrypted_authentication_token);
+ }
authentication.LogOut();
- EXPECT_EQ(pref_service.GetString(prefs::kBraveAccountAuthenticationToken),
- test_case.expected_authentication_token);
+ const auto state = account_state_prefs.GetAccountState();
+ ASSERT_TRUE(state->is_logged_out());
+ EXPECT_FALSE(state->get_logged_out()->verification);
}
std::string test_name;
std::string email_address;
std::string encrypted_authentication_token;
- std::string expected_authentication_token;
};
namespace {
@@ -1892,7 +1918,6 @@ const LogOutTestCase* LogOutAuthenticationTokenEmpty() {
.test_name = "log_out_authentication_token_empty",
.email_address = "",
.encrypted_authentication_token = "",
- .expected_authentication_token = "",
});
return kLogOutAuthenticationTokenEmpty.get();
}
@@ -1903,7 +1928,6 @@ const LogOutTestCase* LogOutAuthenticationTokenNonEmpty() {
.test_name = "log_out_authentication_token_non_empty",
.email_address = kEmailAddress,
.encrypted_authentication_token = EncryptedAuthenticationToken(),
- .expected_authentication_token = "",
});
return kLogOutAuthenticationTokenNonEmpty.get();
}
@@ -2267,12 +2291,16 @@ struct LoginFinalizeTestCase {
std::move(callback).Then(base::BindOnce(
[](PrefService* pref_service, std::string expected_email,
std::string expected_authentication_token) {
- EXPECT_EQ(
- pref_service->GetString(prefs::kBraveAccountEmailAddress),
- expected_email);
- EXPECT_EQ(pref_service->GetString(
- prefs::kBraveAccountAuthenticationToken),
- expected_authentication_token);
+ AccountStatePrefs account_state_prefs(*pref_service);
+ const auto state = account_state_prefs.GetAccountState();
+ if (expected_authentication_token.empty()) {
+ EXPECT_TRUE(state->is_logged_out());
+ } else {
+ ASSERT_TRUE(state->is_logged_in());
+ EXPECT_EQ(state->get_logged_in()->email, expected_email);
+ EXPECT_EQ(account_state_prefs.GetAuthenticationToken(),
+ expected_authentication_token);
+ }
},
base::Unretained(&pref_service), test_case.expected_email,
test_case.expected_authentication_token)));
@@ -2701,14 +2729,13 @@ struct GetServiceTokenTestCase {
base::test::TaskEnvironment& task_environment,
mojom::Authentication& authentication,
base::OnceCallback callback) {
- pref_service.SetDict(
- prefs::kBraveAccountServiceTokens,
- std::move(test_case.service_tokens_dict).Run(base::Time::Now()));
-
if (test_case.logged_in) {
- pref_service.SetString(prefs::kBraveAccountEmailAddress, kEmailAddress);
- pref_service.SetString(prefs::kBraveAccountAuthenticationToken,
- EncryptedAuthenticationToken());
+ AccountStatePrefs(pref_service)
+ .SetLoggedIn(kEmailAddress, EncryptedAuthenticationToken());
+ ScopedDictPrefUpdate(&pref_service, prefs::kBraveAccountState)
+ ->Set(
+ prefs::keys::kServiceTokens,
+ std::move(test_case.service_tokens_dict).Run(base::Time::Now()));
}
task_environment.FastForwardBy(test_case.time_advance);
@@ -2726,13 +2753,16 @@ struct GetServiceTokenTestCase {
std::move(callback).Then(base::BindOnce(
[](PrefService* pref_service, std::string expected_service_token) {
if (!expected_service_token.empty()) {
+ const auto* service_tokens =
+ pref_service->GetDict(prefs::kBraveAccountState)
+ .FindDict(prefs::keys::kServiceTokens);
+ ASSERT_TRUE(service_tokens);
const auto* email_aliases =
- pref_service->GetDict(prefs::kBraveAccountServiceTokens)
- .FindDict("email-aliases");
- EXPECT_TRUE(email_aliases);
+ service_tokens->FindDict("email-aliases");
+ ASSERT_TRUE(email_aliases);
const auto* service_token =
email_aliases->FindString(prefs::keys::kServiceToken);
- EXPECT_TRUE(service_token);
+ ASSERT_TRUE(service_token);
EXPECT_EQ(*service_token,
base::Base64Encode(expected_service_token));
}
@@ -2744,7 +2774,7 @@ struct GetServiceTokenTestCase {
// be processed on the next message pump iteration, so this runs before the
// request completes.
if (test_case.clear_authentication_token) {
- pref_service.ClearPref(prefs::kBraveAccountAuthenticationToken);
+ AccountStatePrefs(pref_service).SetLoggedOut();
}
}
@@ -2778,7 +2808,7 @@ const GetServiceTokenTestCase* GetServiceTokenCacheHit() {
.Set(prefs::keys::kLastFetched,
base::TimeToValue(mock_now)));
}),
- .logged_in = {}, // not used
+ .logged_in = true,
.fail_decryption = {}, // not used
.clear_authentication_token = {}, // not used
.fail_encryption = {}, // not used
diff --git a/components/brave_account/brave_account_state_prefs.cc b/components/brave_account/brave_account_state_prefs.cc
new file mode 100644
index 00000000000..3991cfccafa
--- /dev/null
+++ b/components/brave_account/brave_account_state_prefs.cc
@@ -0,0 +1,159 @@
+/* Copyright (c) 2026 The Brave Authors. All rights reserved.
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#include "brave/components/brave_account/brave_account_state_prefs.h"
+
+#include
+
+#include "base/check.h"
+#include "base/json/values_util.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "brave/components/brave_account/brave_account_service_constants.h"
+#include "components/prefs/scoped_user_pref_update.h"
+
+namespace brave_account {
+
+namespace {
+
+template
+auto MakeVerification(std::optional verification_intent) {
+ VerificationPtr verification;
+
+ if (verification_intent) {
+ if (const auto intent =
+ static_castintent)>(*verification_intent);
+ mojom::IsKnownEnumValue(intent)) {
+ verification = VerificationPtr::Struct::New(intent);
+ }
+ }
+
+ return verification;
+}
+
+} // namespace
+
+void AccountStatePrefs::StartObserving(base::RepeatingClosure on_change) {
+ pref_change_registrar_.Init(&*pref_service_);
+ pref_change_registrar_.Add(prefs::kBraveAccountState, std::move(on_change));
+}
+
+void AccountStatePrefs::SetLoggedOut() {
+ pref_service_->SetDict(prefs::kBraveAccountState,
+ base::DictValue().Set(prefs::keys::kKind,
+ prefs::state_kinds::kLoggedOut));
+}
+
+void AccountStatePrefs::SetLoggedOutWithVerification(
+ const std::string& encrypted_verification_token,
+ mojom::LoggedOutVerificationIntent intent) {
+ CHECK(!encrypted_verification_token.empty());
+
+ pref_service_->SetDict(
+ prefs::kBraveAccountState,
+ base::DictValue()
+ .Set(prefs::keys::kKind, prefs::state_kinds::kLoggedOut)
+ .Set(prefs::keys::kVerification,
+ base::DictValue()
+ .Set(prefs::keys::kVerificationToken,
+ encrypted_verification_token)
+ .Set(prefs::keys::kVerificationIntent,
+ static_cast(intent))));
+}
+
+void AccountStatePrefs::SetLoggedIn(
+ const std::string& email,
+ const std::string& encrypted_authentication_token) {
+ CHECK(!email.empty());
+ CHECK(!encrypted_authentication_token.empty());
+
+ pref_service_->SetDict(
+ prefs::kBraveAccountState,
+ base::DictValue()
+ .Set(prefs::keys::kKind, prefs::state_kinds::kLoggedIn)
+ .Set(prefs::keys::kEmail, email)
+ .Set(prefs::keys::kAuthenticationToken,
+ encrypted_authentication_token));
+}
+
+mojom::AccountStatePtr AccountStatePrefs::GetAccountState() const {
+ const auto& account_state = pref_service_->GetDict(prefs::kBraveAccountState);
+ const auto* kind = account_state.FindString(prefs::keys::kKind);
+ const auto* verification = account_state.FindDict(prefs::keys::kVerification);
+ const auto intent =
+ verification ? verification->FindInt(prefs::keys::kVerificationIntent)
+ : std::nullopt;
+
+ if (kind && *kind == prefs::state_kinds::kLoggedIn) {
+ const auto* email = account_state.FindString(prefs::keys::kEmail);
+ CHECK(email && !email->empty());
+ return mojom::AccountState::NewLoggedIn(mojom::LoggedInState::New(
+ *email, MakeVerification(intent)));
+ }
+
+ return mojom::AccountState::NewLoggedOut(mojom::LoggedOutState::New(
+ MakeVerification(intent)));
+}
+
+std::string AccountStatePrefs::GetAuthenticationToken() const {
+ const auto* token = pref_service_->GetDict(prefs::kBraveAccountState)
+ .FindString(prefs::keys::kAuthenticationToken);
+ return token ? *token : "";
+}
+
+std::string AccountStatePrefs::GetCachedServiceToken(
+ const std::string& service_name) const {
+ CHECK(!service_name.empty());
+
+ const auto* service_tokens = pref_service_->GetDict(prefs::kBraveAccountState)
+ .FindDict(prefs::keys::kServiceTokens);
+ const auto* service =
+ service_tokens ? service_tokens->FindDict(service_name) : nullptr;
+ if (!service) {
+ return "";
+ }
+
+ const auto* encrypted_service_token =
+ service->FindString(prefs::keys::kServiceToken);
+ const auto* last_fetched_value = service->Find(prefs::keys::kLastFetched);
+
+ if (!encrypted_service_token || !last_fetched_value) {
+ return "";
+ }
+
+ const auto last_fetched_time = base::ValueToTime(*last_fetched_value);
+ if (!last_fetched_time) {
+ return "";
+ }
+
+ if (base::Time::Now() - *last_fetched_time >= kServiceTokenMaxAge) {
+ return "";
+ }
+
+ return *encrypted_service_token;
+}
+
+void AccountStatePrefs::UpdateEmail(const std::string& email) {
+ CHECK(!email.empty());
+
+ ScopedDictPrefUpdate(&*pref_service_, prefs::kBraveAccountState)
+ ->Set(prefs::keys::kEmail, email);
+}
+
+void AccountStatePrefs::CacheServiceToken(const std::string& service_name,
+ std::string encrypted_service_token) {
+ CHECK(!service_name.empty());
+ CHECK(!encrypted_service_token.empty());
+
+ ScopedDictPrefUpdate(&*pref_service_, prefs::kBraveAccountState)
+ ->EnsureDict(prefs::keys::kServiceTokens)
+ ->Set(service_name, base::DictValue()
+ .Set(prefs::keys::kServiceToken,
+ std::move(encrypted_service_token))
+ .Set(prefs::keys::kLastFetched,
+ base::TimeToValue(base::Time::Now())));
+}
+
+} // namespace brave_account
diff --git a/components/brave_account/brave_account_state_prefs.h b/components/brave_account/brave_account_state_prefs.h
new file mode 100644
index 00000000000..5132f0cd2d2
--- /dev/null
+++ b/components/brave_account/brave_account_state_prefs.h
@@ -0,0 +1,91 @@
+/* Copyright (c) 2026 The Brave Authors. All rights reserved.
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#ifndef BRAVE_COMPONENTS_BRAVE_ACCOUNT_BRAVE_ACCOUNT_STATE_PREFS_H_
+#define BRAVE_COMPONENTS_BRAVE_ACCOUNT_BRAVE_ACCOUNT_STATE_PREFS_H_
+
+#include
+#include
+#include
+
+#include "base/check_deref.h"
+#include "base/functional/callback.h"
+#include "base/memory/raw_ref.h"
+#include "brave/components/brave_account/mojom/brave_account.mojom.h"
+#include "brave/components/brave_account/pref_names.h"
+#include "components/prefs/pref_change_registrar.h"
+#include "components/prefs/pref_service.h"
+
+namespace brave_account {
+
+// Typed view onto the kBraveAccountState pref dict.
+// Encapsulates the storage schema so callers (the service, tests)
+// don't have to know how the dict is laid out.
+class AccountStatePrefs {
+ public:
+ explicit AccountStatePrefs(PrefService& pref_service)
+ : pref_service_(pref_service) {}
+
+ AccountStatePrefs(const AccountStatePrefs&) = delete;
+ AccountStatePrefs& operator=(const AccountStatePrefs&) = delete;
+
+ void StartObserving(base::RepeatingClosure on_change);
+
+ void SetLoggedOut();
+
+ void SetLoggedOutWithVerification(
+ const std::string& encrypted_verification_token,
+ mojom::LoggedOutVerificationIntent intent);
+
+ void SetLoggedIn(const std::string& email,
+ const std::string& encrypted_authentication_token);
+
+ mojom::AccountStatePtr GetAccountState() const;
+
+ std::string GetAuthenticationToken() const;
+
+ template
+ requires std::same_as ||
+ std::same_as
+ std::string GetVerificationToken(Intent intent) const {
+ const auto verification = [&] {
+ const auto account_state = GetAccountState();
+ if constexpr (std::same_as) {
+ return account_state->is_logged_out()
+ ? std::move(account_state->get_logged_out()->verification)
+ : nullptr;
+ } else {
+ return account_state->is_logged_in()
+ ? std::move(account_state->get_logged_in()->verification)
+ : nullptr;
+ }
+ }();
+
+ if (!verification || verification->intent != intent) {
+ return "";
+ }
+
+ const auto* token =
+ CHECK_DEREF(pref_service_->GetDict(prefs::kBraveAccountState)
+ .FindDict(prefs::keys::kVerification))
+ .FindString(prefs::keys::kVerificationToken);
+ return token ? *token : "";
+ }
+
+ std::string GetCachedServiceToken(const std::string& service_name) const;
+
+ void UpdateEmail(const std::string& email);
+
+ void CacheServiceToken(const std::string& service_name,
+ std::string encrypted_service_token);
+
+ private:
+ const raw_ref pref_service_;
+ PrefChangeRegistrar pref_change_registrar_;
+};
+
+} // namespace brave_account
+
+#endif // BRAVE_COMPONENTS_BRAVE_ACCOUNT_BRAVE_ACCOUNT_STATE_PREFS_H_
diff --git a/components/brave_account/mojom/brave_account.mojom b/components/brave_account/mojom/brave_account.mojom
index 1ebf5b50898..2fc2687b47c 100644
--- a/components/brave_account/mojom/brave_account.mojom
+++ b/components/brave_account/mojom/brave_account.mojom
@@ -5,17 +5,40 @@
module brave_account.mojom;
-struct LoggedOutState {};
+// These enum values are persisted to Preferences. Never reorder, repurpose,
+// or recycle a value — only append new ones with the next unused number.
+// When removing an enumerator, leave its number reserved as a comment.
+enum LoggedOutVerificationIntent {
+ kRegistration = 0,
+ kResetPassword = 1,
+};
-struct VerificationState {};
+// These enum values are persisted to Preferences. Never reorder, repurpose,
+// or recycle a value — only append new ones with the next unused number.
+// When removing an enumerator, leave its number reserved as a comment.
+enum LoggedInVerificationIntent {
+ kChangePassword = 0,
+};
+
+struct LoggedOutVerification {
+ LoggedOutVerificationIntent intent;
+};
+
+struct LoggedInVerification {
+ LoggedInVerificationIntent intent;
+};
+
+struct LoggedOutState {
+ LoggedOutVerification? verification;
+};
struct LoggedInState {
string email;
+ LoggedInVerification? verification;
};
union AccountState {
LoggedOutState logged_out;
- VerificationState verification;
LoggedInState logged_in;
};
@@ -39,7 +62,7 @@ enum RegisterClientErrorCode {
kOpaqueError,
kVerificationTokenEncryptionFailed,
kVerificationTokenDecryptionFailed,
- kUserNotInTheVerificationState,
+ kNoRegistrationInProgress,
kAuthenticationTokenEncryptionFailed,
};
@@ -158,7 +181,7 @@ struct ResendConfirmationEmailResult {
enum ResendConfirmationEmailClientErrorCode {
kUnexpected,
- kUserNotInTheVerificationState,
+ kNoRegistrationInProgress,
kVerificationTokenDecryptionFailed,
};
diff --git a/components/brave_account/pref_names.h b/components/brave_account/pref_names.h
index 09459f0b99d..937bb958779 100644
--- a/components/brave_account/pref_names.h
+++ b/components/brave_account/pref_names.h
@@ -8,57 +8,102 @@
namespace brave_account::prefs {
-// This preference will store (`OSCrypt`-encrypted) the `verificationToken`
-// returned by the POST /v2/accounts/password/init endpoint.
-// It is then used for:
-// - calling POST /v2/accounts/password/finalize
-// to complete the registration flow
-// - polling POST /v2/verify/result
-// to exchange the `verificationToken` for the `authToken`
-// after the user has verified their email
-inline constexpr char kBraveAccountVerificationToken[] =
- "brave.account.verification_token";
-
-// This preference will store (`OSCrypt`-encrypted) the (JWT) `authToken`
-// returned by the POST /v2/verify/result endpoint.
-// It is then used for:
-// - authenticating subsequent Brave Account requests
-// - creating new service tokens
-inline constexpr char kBraveAccountAuthenticationToken[] =
- "brave.account.authentication_token";
-
-// This preference will store the email address of the authenticated user
-// returned by the GET /v2/auth/validate endpoint.
-inline constexpr char kBraveAccountEmailAddress[] =
- "brave.account.email_address";
-
-// This preference will store a dictionary of service tokens with their
-// fetch times. Each entry maps a service name to a dict containing:
-// - "service_token": the service token (`OSCrypt`-encrypted)
-// - "last_fetched": when the service token was retrieved (base::Time as
-// base::Value via base::TimeToValue())
+// This preference stores the entire account state as a single dictionary,
+// keyed by "kind" ("logged-out" or "logged-in"). All state transitions write
+// the whole dictionary in one shot, so observers see one consistent state per
+// notification.
//
-// Example:
-// ...
-// "service_tokens": {
-// "email-aliases": {
-// "last_fetched": "...",
-// "service_token": "..."
-// },
-// ...
+// First launch, no Brave Account ever used — equivalent to logged-out:
+// The "account" dict in Preferences doesn't exist.
+//
+// Registration in progress — logged-out, with verification:
+// "account": {
+// "state": {
+// "kind": "logged-out",
+// "verification": {
+// "intent": 0,
+// "token": "..."
+// }
+// }
// },
-// ...
-inline constexpr char kBraveAccountServiceTokens[] =
- "brave.account.service_tokens";
+//
+// Email verified, or returning user logged in — logged-in,
+// optionally including service tokens:
+// "account": {
+// "state": {
+// "authentication_token": "...",
+// "email": "...",
+// "kind": "logged-in",
+// "service_tokens": {
+// "email-aliases": {
+// "last_fetched": "13422130435353472",
+// "service_token": "..."
+// }
+// }
+// }
+// },
+//
+// Password change in progress — logged-in, with verification:
+// "account": {
+// "state": {
+// "authentication_token": "...",
+// "email": "...",
+// "kind": "logged-in"
+// "verification": {
+// "intent": 0,
+// "token": "..."
+// }
+// }
+// },
+//
+// Logged out — logged-out:
+// "account": {
+// "state": {
+// "kind": "logged-out"
+// }
+// },
+//
+// Password reset in progress — logged-out, with verification:
+// "account": {
+// "state": {
+// "kind": "logged-out",
+// "verification": {
+// "intent": 1,
+// "token": "..."
+// }
+// }
+// },
+inline constexpr char kBraveAccountState[] = "brave.account.state";
namespace keys {
-// Dictionary keys used within kBraveAccountServiceTokens.
-inline constexpr char kServiceToken[] = "service_token";
+// Top-level dictionary keys used within kBraveAccountState.
+inline constexpr char kAuthenticationToken[] = "authentication_token";
+inline constexpr char kEmail[] = "email";
+inline constexpr char kKind[] = "kind";
+inline constexpr char kServiceTokens[] = "service_tokens";
+inline constexpr char kVerification[] = "verification";
+
+// Dictionary keys used within the "verification" sub-dict of
+// kBraveAccountState.
+inline constexpr char kVerificationIntent[] = "intent";
+inline constexpr char kVerificationToken[] = "token";
+
+// Dictionary keys used within entries of the "service_tokens" sub-dict of
+// kBraveAccountState.
inline constexpr char kLastFetched[] = "last_fetched";
+inline constexpr char kServiceToken[] = "service_token";
} // namespace keys
+namespace state_kinds {
+
+// Values for the kKind field within kBraveAccountState.
+inline constexpr char kLoggedIn[] = "logged-in";
+inline constexpr char kLoggedOut[] = "logged-out";
+
+} // namespace state_kinds
+
} // namespace brave_account::prefs
#endif // BRAVE_COMPONENTS_BRAVE_ACCOUNT_PREF_NAMES_H_
diff --git a/components/brave_account/prefs.cc b/components/brave_account/prefs.cc
index dd543f2662f..935f7bb0a89 100644
--- a/components/brave_account/prefs.cc
+++ b/components/brave_account/prefs.cc
@@ -5,8 +5,6 @@
#include "brave/components/brave_account/prefs.h"
-#include
-
#include "base/values.h"
#include "brave/components/brave_account/features.h"
#include "brave/components/brave_account/pref_names.h"
@@ -16,12 +14,7 @@ namespace brave_account::prefs {
void RegisterPrefs(PrefRegistrySimple* registry) {
if (features::IsBraveAccountEnabled()) {
- registry->RegisterStringPref(kBraveAccountVerificationToken, std::string());
- registry->RegisterStringPref(kBraveAccountAuthenticationToken,
- std::string());
- registry->RegisterStringPref(kBraveAccountEmailAddress, std::string());
- registry->RegisterDictionaryPref(kBraveAccountServiceTokens,
- base::DictValue());
+ registry->RegisterDictionaryPref(kBraveAccountState, base::DictValue());
}
}
diff --git a/components/brave_account/resources/brave_account_dialogs.ts b/components/brave_account/resources/brave_account_dialogs.ts
index e55848d4466..b4ad3454119 100644
--- a/components/brave_account/resources/brave_account_dialogs.ts
+++ b/components/brave_account/resources/brave_account_dialogs.ts
@@ -68,8 +68,8 @@ export class BraveAccountDialogsElement extends CrLitElement {
//
// Handle account state changes.
- // LOGGED_OUT: show the ENTRY dialog
- // VERIFICATION: show the OTP dialog
+ // LOGGED_OUT (no verification): show the ENTRY dialog
+ // LOGGED_OUT (with verification): show the OTP dialog
// LOGGED_IN: close the native dialog
// Since account state is profile-wide, this automatically updates dialogs
// across all tabs.
@@ -78,10 +78,7 @@ export class BraveAccountDialogsElement extends CrLitElement {
(state: AccountState) => {
switch (whichAccountState(state)) {
case AccountStateFieldTags.LOGGED_OUT:
- this.dialog = 'ENTRY'
- break
- case AccountStateFieldTags.VERIFICATION:
- this.dialog = 'OTP'
+ this.dialog = state.loggedOut!.verification ? 'OTP' : 'ENTRY'
break
case AccountStateFieldTags.LOGGED_IN:
this.onCloseDialog()
diff --git a/components/email_aliases/BUILD.gn b/components/email_aliases/BUILD.gn
index e2a39c9f579..ecd3441fa50 100644
--- a/components/email_aliases/BUILD.gn
+++ b/components/email_aliases/BUILD.gn
@@ -53,6 +53,7 @@ static_library("service") {
"//base",
"//brave/brave_domains",
"//brave/components/brave_account",
+ "//brave/components/brave_account:internal",
"//brave/components/brave_account/endpoint_client",
"//brave/components/brave_account/mojom",
"//brave/components/constants",
diff --git a/components/email_aliases/email_aliases_auth.cc b/components/email_aliases/email_aliases_auth.cc
index 865361e01d5..c2498df9782 100644
--- a/components/email_aliases/email_aliases_auth.cc
+++ b/components/email_aliases/email_aliases_auth.cc
@@ -28,17 +28,10 @@ EmailAliasesAuth::EmailAliasesAuth(
base::Unretained(
this))); // Unretained is safe because we own the remote<>
+ // TODO(https://github.com/brave/brave-browser/issues/55179)
pref_change_registrar_.Init(&prefs_service_.get());
pref_change_registrar_.Add(
- brave_account::prefs::kBraveAccountServiceTokens,
- base::BindRepeating(&EmailAliasesAuth::OnPrefChanged,
- base::Unretained(this)));
- pref_change_registrar_.Add(
- brave_account::prefs::kBraveAccountAuthenticationToken,
- base::BindRepeating(&EmailAliasesAuth::OnPrefChanged,
- base::Unretained(this)));
- pref_change_registrar_.Add(
- brave_account::prefs::kBraveAccountEmailAddress,
+ brave_account::prefs::kBraveAccountState,
base::BindRepeating(&EmailAliasesAuth::OnPrefChanged,
base::Unretained(this)));
}
@@ -54,8 +47,10 @@ std::string EmailAliasesAuth::GetAuthEmail() const {
CHECK_IS_TEST();
return auth_email_for_testing_.value();
}
- return prefs_service_->GetString(
- brave_account::prefs::kBraveAccountEmailAddress);
+ const auto* email =
+ prefs_service_->GetDict(brave_account::prefs::kBraveAccountState)
+ .FindString(brave_account::prefs::keys::kEmail);
+ return email ? *email : "";
}
void EmailAliasesAuth::GetServiceToken(
diff --git a/ios/brave-ios/Sources/Brave/Frontend/Settings/SettingsViewController.swift b/ios/brave-ios/Sources/Brave/Frontend/Settings/SettingsViewController.swift
index 818d576ebc4..7032706ab78 100644
--- a/ios/brave-ios/Sources/Brave/Frontend/Settings/SettingsViewController.swift
+++ b/ios/brave-ios/Sources/Brave/Frontend/Settings/SettingsViewController.swift
@@ -447,7 +447,7 @@ class SettingsViewController: TableViewController, BraveAccountAuthenticationObs
),
]
)
- case .verification:
+ case .loggedOut where braveAccountState.loggedOut!.verification != nil:
return Static.Section(
header: .title(L10nUtils.string(messageId: .BRAVE_ACCOUNT_TITLE)),
rows: [