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