1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_PRIMARY_ACCOUNT_ACCESS_TOKEN_FETCHER_H_
6 #define COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_PRIMARY_ACCOUNT_ACCESS_TOKEN_FETCHER_H_
7 
8 #include <memory>
9 #include <string>
10 
11 #include "base/macros.h"
12 #include "base/scoped_observer.h"
13 #include "components/signin/public/identity_manager/access_token_fetcher.h"
14 #include "components/signin/public/identity_manager/consent_level.h"
15 #include "components/signin/public/identity_manager/identity_manager.h"
16 #include "components/signin/public/identity_manager/scope_set.h"
17 #include "google_apis/gaia/core_account_id.h"
18 
19 class GoogleServiceAuthError;
20 
21 namespace signin {
22 struct AccessTokenInfo;
23 
24 // Class that supports obtaining OAuth2 access tokens for the user's primary
25 // account.See ./README.md for the definition of "accounts with OAuth2 refresh
26 // tokens" and "primary account".
27 //
28 // The usage model of this class is as follows: When a
29 // PrimaryAccountAccessTokenFetcher is created, it will make an access token
30 // request for the primary account (either immediately or if/once the primary
31 // account becomes available, based on the value of the specified |Mode|
32 // parameter). When the access token request is fulfilled the
33 // PrimaryAccountAccessTokenFetcher will call the specified callback, at which
34 // point it is safe for the caller to destroy the object. If the object is
35 // destroyed before the request is fulfilled the request is dropped and the
36 // callback will never be invoked. This class may only be used on the UI thread.
37 //
38 // To drive responses to access token fetches in unittests of clients of this
39 // class, use IdentityTestEnvironment.
40 //
41 // Concrete usage example (related concrete test example follows):
42 //   class MyClass {
43 //    public:
44 //     MyClass(IdentityManager* identity_manager) :
45 //       identity_manager_(identity_manager) {
46 //         // An access token request could also be initiated at any arbitrary
47 //         // point in the lifetime of |MyClass|.
48 //         StartAccessTokenRequestForPrimaryAccount();
49 //       }
50 //
51 //
52 //     ~MyClass() {
53 //       // If the access token request is still live, the destruction of
54 //       |access_token_fetcher_| will cause it to be dropped.
55 //     }
56 //
57 //    private:
58 //     IdentityManager* identity_manager_;
59 //     std::unique_ptr<PrimaryAccountAccessTokenFetcher> access_token_fetcher_;
60 //     std::string access_token_;
61 //     GoogleServiceAuthError access_token_request_error_;
62 //
63 //     // Most commonly invoked as part of some larger flow to hit a Gaia
64 //     // endpoint for a client-specific purpose (e.g., hitting sync
65 //     // endpoints).
66 //     // Could also be public, but in general, any clients that would need to
67 //     // create access token requests could and should just create
68 //     // PrimaryAccountAccessTokenFetchers directly themselves rather than
69 //     // introducing wrapper API surfaces.
70 //     MyClass::StartAccessTokenRequestForPrimaryAccount() {
71 //       // Choose scopes to obtain for the access token.
72 //       ScopeSet scopes;
73 //       scopes.insert(GaiaConstants::kMyFirstScope);
74 //       scopes.insert(GaiaConstants::kMySecondScope);
75 
76 //       // Choose the mode in which to fetch the access token:
77 //       // see AccessTokenFetcher::Mode below for definitions.
78 //       auto mode =
79 //         signin::PrimaryAccountAccessTokenFetcher::Mode::kWaitUntilAvailable;
80 
81 //       // Create the fetcher.
82 //       access_token_fetcher_ =
83 //           std::make_unique<PrimaryAccountAccessTokenFetcher(
84 //               /*consumer_name=*/"MyClass",
85 //               identity_manager_,
86 //               scopes,
87 //               base::BindOnce(&MyClass::OnAccessTokenRequestCompleted,
88 //                              // It is safe to use base::Unretained as
89 //                              // |this| owns |access_token_fetcher_|.
90 //                              base::Unretained(this)),
91 //                              mode);
92 //
93 //     }
94 //     MyClass::OnAccessTokenRequestCompleted(
95 //         GoogleServiceAuthError error, AccessTokenInfo access_token_info) {
96 //       // It is safe to destroy |access_token_fetcher_| from this callback.
97 //       access_token_fetcher_.reset();
98 //
99 //       if (error.state() == GoogleServiceAuthError::NONE) {
100 //         // The fetcher successfully obtained an access token.
101 //         access_token_ = access_token_info.token;
102 //         // MyClass can now take whatever action required having an access
103 //         // token (e.g.,hitting a given Gaia endpoint).
104 //         ...
105 //       } else {
106 //         // The fetcher failed to obtain a token; |error| specifies why.
107 //         access_token_request_error_ = error;
108 //         // MyClass can now perform any desired error handling.
109 //         ...
110 //       }
111 //     }
112 //   }
113 //
114 //   Concrete test example:
115 //   TEST(MyClassTest, SuccessfulAccessTokenFetchForPrimaryAccount) {
116 //     IdentityTestEnvironment identity_test_env;
117 //
118 //
119 //     MyClass my_class(identity_test_env.identity_manager());
120 //
121 //     // Make the primary account available, which should result in an access
122 //     // token fetch being made on behalf of |my_class|.
123 //     identity_test_env.MakePrimaryAccountAvailable("test_email");
124 //
125 //     identity_test_env.
126 //         WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
127 //             "access_token", base::Time::Max());
128 //
129 //     // MyClass::OnAccessTokenRequestCompleted() will have been invoked with
130 //     // an AccessTokenInfo object containing the above-specified parameters;
131 //     // the test can now perform any desired validation of expected actions
132 //     // |MyClass| took in response.
133 //   }
134 class PrimaryAccountAccessTokenFetcher : public IdentityManager::Observer {
135  public:
136   // Specifies how this instance should behave:
137   // |kImmediate|: Makes one-shot immediate request.
138   // |kWaitUntilAvailable|: Waits for the primary account to be available
139   // before making the request. In particular, "available" is defined as the
140   // moment when (a) there is a primary account and (b) that account has a
141   // refresh token. This semantics is richer than using an AccessTokenFetcher in
142   // kWaitUntilRefreshTokenAvailable mode, as the latter will make a request
143   // once the specified account has a refresh token, regardless of whether it's
144   // the primary account at that point.
145   // Note that using |kWaitUntilAvailable| can result in waiting forever
146   // if the user is not signed in and doesn't sign in.
147   enum class Mode { kImmediate, kWaitUntilAvailable };
148 
149   // Instantiates a fetcher and immediately starts the process of obtaining an
150   // OAuth2 access token for the given |scopes|. The |callback| is called once
151   // the request completes (successful or not). If the
152   // PrimaryAccountAccessTokenFetcher is destroyed before the process completes,
153   // the callback is not called.
154   // |consent| defaults to kSync because historically having an "authenticated"
155   // account was tied to browser sync. See ./README.md.
156   PrimaryAccountAccessTokenFetcher(const std::string& oauth_consumer_name,
157                                    IdentityManager* identity_manager,
158                                    const ScopeSet& scopes,
159                                    AccessTokenFetcher::TokenCallback callback,
160                                    Mode mode,
161                                    ConsentLevel consent = ConsentLevel::kSync);
162 
163   ~PrimaryAccountAccessTokenFetcher() override;
164 
165   // Exposed for tests.
access_token_request_retried()166   bool access_token_request_retried() { return access_token_retried_; }
167 
168  private:
169   // Returns the primary account ID. If consent is |kNotRequired| this may be
170   // the "unconsented" primary account ID.
171   CoreAccountId GetAccountId() const;
172 
173   // Returns true iff there is a primary account with a refresh token. Should
174   // only be called in mode |kWaitUntilAvailable|.
175   bool AreCredentialsAvailable() const;
176 
177   void StartAccessTokenRequest();
178 
179   // IdentityManager::Observer implementation.
180   void OnPrimaryAccountSet(
181       const CoreAccountInfo& primary_account_info) override;
182   void OnUnconsentedPrimaryAccountChanged(
183       const CoreAccountInfo& primary_account_info) override;
184   void OnRefreshTokenUpdatedForAccount(
185       const CoreAccountInfo& account_info) override;
186 
187   // Checks whether credentials are now available and starts an access token
188   // request if so. Should only be called in mode |kWaitUntilAvailable|.
189   void ProcessSigninStateChange();
190 
191   // Invoked by |fetcher_| when an access token request completes.
192   void OnAccessTokenFetchComplete(GoogleServiceAuthError error,
193                                   AccessTokenInfo access_token_info);
194 
195   std::string oauth_consumer_name_;
196   IdentityManager* identity_manager_;
197   ScopeSet scopes_;
198 
199   // Per the contract of this class, it is allowed for clients to delete this
200   // object as part of the invocation of |callback_|. Hence, this object must
201   // assume that it is dead after invoking |callback_| and must not run any more
202   // code.
203   AccessTokenFetcher::TokenCallback callback_;
204 
205   ScopedObserver<IdentityManager, IdentityManager::Observer>
206       identity_manager_observer_{this};
207 
208   // Internal fetcher that does the actual access token request.
209   std::unique_ptr<AccessTokenFetcher> access_token_fetcher_;
210 
211   // When a token request gets canceled, we want to retry once.
212   bool access_token_retried_;
213 
214   Mode mode_;
215 
216   const ConsentLevel consent_;
217 
218   DISALLOW_COPY_AND_ASSIGN(PrimaryAccountAccessTokenFetcher);
219 };
220 
221 }  // namespace signin
222 
223 #endif  // COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_PRIMARY_ACCOUNT_ACCESS_TOKEN_FETCHER_H_
224