summaryrefslogtreecommitdiff
path: root/chromium/chrome/browser/signin/signin_manager.cc
blob: 9cc73e0e49e856151542a9b12fe13927512f485e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/signin/signin_manager.h"

#include "components/prefs/pref_service.h"
#include "components/signin/public/base/signin_pref_names.h"
#include "components/signin/public/identity_manager/accounts_in_cookie_jar_info.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "components/signin/public/identity_manager/primary_account_mutator.h"

SigninManager::SigninManager(PrefService* prefs,
                             signin::IdentityManager* identity_manager)
    : prefs_(prefs), identity_manager_(identity_manager) {
  signin_allowed_.Init(
      prefs::kSigninAllowed, prefs_,
      base::BindRepeating(&SigninManager::OnSigninAllowedPrefChanged,
                          base::Unretained(this)));

  UpdateUnconsentedPrimaryAccount();
  identity_manager_->AddObserver(this);
}

SigninManager::~SigninManager() {
  identity_manager_->RemoveObserver(this);
}

void SigninManager::UpdateUnconsentedPrimaryAccount() {
  // Only update the unconsented primary account only after accounts are loaded.
  if (!identity_manager_->AreRefreshTokensLoaded()) {
    return;
  }

  absl::optional<CoreAccountInfo> account =
      ComputeUnconsentedPrimaryAccountInfo();

  DCHECK(!account || !account->IsEmpty());
  if (account) {
    if (identity_manager_->GetPrimaryAccountInfo(
            signin::ConsentLevel::kSignin) != account) {
      DCHECK(
          !identity_manager_->HasPrimaryAccount(signin::ConsentLevel::kSync));
      identity_manager_->GetPrimaryAccountMutator()->SetPrimaryAccount(
          account->account_id, signin::ConsentLevel::kSignin);
    }
  } else if (identity_manager_->HasPrimaryAccount(
                 signin::ConsentLevel::kSignin)) {
    DCHECK(!identity_manager_->HasPrimaryAccount(signin::ConsentLevel::kSync));
    identity_manager_->GetPrimaryAccountMutator()->ClearPrimaryAccount(
        signin_metrics::USER_DELETED_ACCOUNT_COOKIES,
        signin_metrics::SignoutDelete::kIgnoreMetric);
  }
}

absl::optional<CoreAccountInfo>
SigninManager::ComputeUnconsentedPrimaryAccountInfo() const {
  DCHECK(identity_manager_->AreRefreshTokensLoaded());

  // UPA is equal to the primary account with sync consent if it exists.
  if (identity_manager_->HasPrimaryAccount(signin::ConsentLevel::kSync)) {
    return identity_manager_->GetPrimaryAccountInfo(
        signin::ConsentLevel::kSync);
  }

  // Clearing the primary sync account when sign-in is not allowed is handled
  // by PrimaryAccountPolicyManager. That flow is extremely hard to follow
  // especially for the case when the user is syncing with a managed account
  // as in that case the whole profile needs to be deleted.
  //
  // It was considered simpler to keep the logic to update the unconsented
  // primary account in a single place.
  if (!signin_allowed_.GetValue())
    return absl::nullopt;

  signin::AccountsInCookieJarInfo cookie_info =
      identity_manager_->GetAccountsInCookieJar();

  std::vector<gaia::ListedAccount> cookie_accounts =
      cookie_info.signed_in_accounts;

  // Fresh cookies and loaded tokens are needed to compute the UPA.
  if (cookie_info.accounts_are_fresh) {
    // Cookies are fresh and tokens are loaded, UPA is the first account
    // in cookies if it exists and has a refresh token.
    if (cookie_accounts.empty()) {
      // Cookies are empty, the UPA is empty.
      return absl::nullopt;
    }

    AccountInfo account_info =
        identity_manager_->FindExtendedAccountInfoByAccountId(
            cookie_accounts[0].id);

    // Verify the first account in cookies has a refresh token that is valid.
    bool error_state =
        account_info.IsEmpty() ||
        identity_manager_->HasAccountWithRefreshTokenInPersistentErrorState(
            account_info.account_id);

    return error_state ? absl::nullopt
                       : absl::make_optional<CoreAccountInfo>(account_info);
  }

  if (!identity_manager_->HasPrimaryAccount(signin::ConsentLevel::kSignin))
    return absl::nullopt;

  // If cookies or tokens are not loaded, it is not possible to fully compute
  // the unconsented primary account. However, if the current unconsented
  // primary account is no longer valid, it has to be removed.
  CoreAccountId current_account =
      identity_manager_->GetPrimaryAccountId(signin::ConsentLevel::kSignin);

  if (!identity_manager_->HasAccountWithRefreshToken(current_account)) {
    // Tokens are loaded, but the current UPA doesn't have a refresh token.
    // Clear the current UPA.
    return absl::nullopt;
  }

  if (cookie_info.accounts_are_fresh) {
    if (cookie_accounts.empty() || cookie_accounts[0].id != current_account) {
      // The current UPA is not the first in fresh cookies. It needs to be
      // cleared.
      return absl::nullopt;
    }
  }

  // No indication that the current UPA is invalid, return current UPA.
  return identity_manager_->GetPrimaryAccountInfo(
      signin::ConsentLevel::kSignin);
}

// signin::IdentityManager::Observer implementation.
void SigninManager::OnPrimaryAccountChanged(
    const signin::PrimaryAccountChangeEvent& event_details) {
  // This is needed for the case where the user chooses to start syncing
  // with an account that is different from the unconsented primary account
  // (not the first in cookies) but then cancels. In that case, the tokens stay
  // the same. In all the other cases, either the token will be revoked which
  // will trigger an update for the unconsented primary account or the
  // primary account stays the same but the sync consent is revoked.
  if (event_details.GetEventTypeFor(signin::ConsentLevel::kSync) !=
      signin::PrimaryAccountChangeEvent::Type::kCleared) {
    return;
  }

  // It is important to update the primary account after all observers process
  // the current OnPrimaryAccountChanged() as all observers should see the same
  // value for the unconsented primary account. Schedule the potential update
  // on the next run loop.
  base::SequencedTaskRunnerHandle::Get()->PostTask(
      FROM_HERE, base::BindOnce(&SigninManager::UpdateUnconsentedPrimaryAccount,
                                weak_ptr_factory_.GetWeakPtr()));
}

void SigninManager::OnRefreshTokenUpdatedForAccount(
    const CoreAccountInfo& account_info) {
  UpdateUnconsentedPrimaryAccount();
}

void SigninManager::OnRefreshTokenRemovedForAccount(
    const CoreAccountId& account_id) {
  UpdateUnconsentedPrimaryAccount();
}

void SigninManager::OnRefreshTokensLoaded() {
  UpdateUnconsentedPrimaryAccount();
}

void SigninManager::OnAccountsInCookieUpdated(
    const signin::AccountsInCookieJarInfo& accounts_in_cookie_jar_info,
    const GoogleServiceAuthError& error) {
  UpdateUnconsentedPrimaryAccount();
}

void SigninManager::OnAccountsCookieDeletedByUserAction() {
  UpdateUnconsentedPrimaryAccount();
}

void SigninManager::OnErrorStateOfRefreshTokenUpdatedForAccount(
    const CoreAccountInfo& account_info,
    const GoogleServiceAuthError& error) {
  CoreAccountInfo current_account =
      identity_manager_->GetPrimaryAccountInfo(signin::ConsentLevel::kSignin);

  bool should_update = false;
  if (error == GoogleServiceAuthError::AuthErrorNone()) {
    should_update = current_account.IsEmpty();
  } else {
    // In error state, update if the account in error is the current UPA.
    should_update = (account_info == current_account);
  }

  if (should_update)
    UpdateUnconsentedPrimaryAccount();
}

void SigninManager::OnSigninAllowedPrefChanged() {
  UpdateUnconsentedPrimaryAccount();
}