blob: e982fa4e172ecb5f89383fb1f2c02eb65ea3eec9 [file] [log] [blame]
// Copyright 2017 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.
#ifndef COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_PRIMARY_ACCOUNT_ACCESS_TOKEN_FETCHER_H_
#define COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_PRIMARY_ACCOUNT_ACCESS_TOKEN_FETCHER_H_
#include <memory>
#include <string>
#include "base/macros.h"
#include "base/scoped_observation.h"
#include "components/signin/public/identity_manager/access_token_fetcher.h"
#include "components/signin/public/identity_manager/consent_level.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "components/signin/public/identity_manager/scope_set.h"
#include "google_apis/gaia/core_account_id.h"
class GoogleServiceAuthError;
namespace signin {
struct AccessTokenInfo;
// Class that supports obtaining OAuth2 access tokens for the user's primary
// account.See ./README.md for the definition of "accounts with OAuth2 refresh
// tokens" and "primary account".
//
// The usage model of this class is as follows: When a
// PrimaryAccountAccessTokenFetcher is created, it will make an access token
// request for the primary account (either immediately or if/once the primary
// account becomes available, based on the value of the specified |Mode|
// parameter). When the access token request is fulfilled the
// PrimaryAccountAccessTokenFetcher will call the specified callback, at which
// point it is safe for the caller to destroy the object. If the object is
// destroyed before the request is fulfilled the request is dropped and the
// callback will never be invoked. This class may only be used on the UI thread.
//
// To drive responses to access token fetches in unittests of clients of this
// class, use IdentityTestEnvironment.
//
// Concrete usage example (related concrete test example follows):
// class MyClass {
// public:
// MyClass(IdentityManager* identity_manager) :
// identity_manager_(identity_manager) {
// // An access token request could also be initiated at any arbitrary
// // point in the lifetime of |MyClass|.
// StartAccessTokenRequestForPrimaryAccount();
// }
//
//
// ~MyClass() {
// // If the access token request is still live, the destruction of
// |access_token_fetcher_| will cause it to be dropped.
// }
//
// private:
// IdentityManager* identity_manager_;
// std::unique_ptr<PrimaryAccountAccessTokenFetcher> access_token_fetcher_;
// std::string access_token_;
// GoogleServiceAuthError access_token_request_error_;
//
// // Most commonly invoked as part of some larger flow to hit a Gaia
// // endpoint for a client-specific purpose (e.g., hitting sync
// // endpoints).
// // Could also be public, but in general, any clients that would need to
// // create access token requests could and should just create
// // PrimaryAccountAccessTokenFetchers directly themselves rather than
// // introducing wrapper API surfaces.
// MyClass::StartAccessTokenRequestForPrimaryAccount() {
// // Choose scopes to obtain for the access token.
// ScopeSet scopes;
// scopes.insert(GaiaConstants::kMyFirstScope);
// scopes.insert(GaiaConstants::kMySecondScope);
// // Choose the mode in which to fetch the access token:
// // see AccessTokenFetcher::Mode below for definitions.
// auto mode =
// PrimaryAccountAccessTokenFetcher::Mode::kWaitUntilAvailable;
// // Create the fetcher.
// access_token_fetcher_ =
// std::make_unique<PrimaryAccountAccessTokenFetcher>(
// /*consumer_name=*/"MyClass",
// identity_manager_,
// scopes,
// base::BindOnce(&MyClass::OnAccessTokenRequestCompleted,
// // It is safe to use base::Unretained as
// // |this| owns |access_token_fetcher_|.
// base::Unretained(this)),
// mode);
//
// }
// void MyClass::OnAccessTokenRequestCompleted(
// GoogleServiceAuthError error, AccessTokenInfo access_token_info) {
// // It is safe to destroy |access_token_fetcher_| from this callback.
// access_token_fetcher_.reset();
//
// if (error.state() == GoogleServiceAuthError::NONE) {
// // The fetcher successfully obtained an access token.
// access_token_ = access_token_info.token;
// // MyClass can now take whatever action required having an access
// // token (e.g.,hitting a given Gaia endpoint).
// ...
// } else {
// // The fetcher failed to obtain a token; |error| specifies why.
// access_token_request_error_ = error;
// // MyClass can now perform any desired error handling.
// ...
// }
// }
// }
//
// Concrete test example:
// TEST(MyClassTest, SuccessfulAccessTokenFetchForPrimaryAccount) {
// IdentityTestEnvironment identity_test_env;
//
//
// MyClass my_class(identity_test_env.identity_manager());
//
// // Make the primary account available, which should result in an access
// // token fetch being made on behalf of |my_class|.
// identity_test_env.MakePrimaryAccountAvailable("test_email");
//
// identity_test_env.
// WaitForAccessTokenRequestIfNecessaryAndRespondWithToken(
// "access_token", base::Time::Max());
//
// // MyClass::OnAccessTokenRequestCompleted() will have been invoked with
// // an AccessTokenInfo object containing the above-specified parameters;
// // the test can now perform any desired validation of expected actions
// // |MyClass| took in response.
// }
class PrimaryAccountAccessTokenFetcher : public IdentityManager::Observer {
public:
// Specifies how this instance should behave:
// |kImmediate|: Makes one-shot immediate request.
// |kWaitUntilAvailable|: Waits for the primary account to be available
// before making the request. In particular, "available" is defined as the
// moment when (a) there is a primary account and (b) that account has a
// refresh token. This semantics is richer than using an AccessTokenFetcher in
// kWaitUntilRefreshTokenAvailable mode, as the latter will make a request
// once the specified account has a refresh token, regardless of whether it's
// the primary account at that point.
// Note that using |kWaitUntilAvailable| can result in waiting forever
// if the user is not signed in and doesn't sign in.
enum class Mode { kImmediate, kWaitUntilAvailable };
// Instantiates a fetcher and immediately starts the process of obtaining an
// OAuth2 access token for the given |scopes|. The |callback| is called once
// the request completes (successful or not). If the
// PrimaryAccountAccessTokenFetcher is destroyed before the process completes,
// the callback is not called.
// |consent| defaults to kSync because historically having an "authenticated"
// account was tied to browser sync. See ./README.md.
PrimaryAccountAccessTokenFetcher(const std::string& oauth_consumer_name,
IdentityManager* identity_manager,
const ScopeSet& scopes,
AccessTokenFetcher::TokenCallback callback,
Mode mode,
ConsentLevel consent = ConsentLevel::kSync);
~PrimaryAccountAccessTokenFetcher() override;
// Exposed for tests.
bool access_token_request_retried() { return access_token_retried_; }
private:
// Returns the primary account ID. If consent is |kNotRequired| this may be
// the "unconsented" primary account ID.
CoreAccountId GetAccountId() const;
// Returns true iff there is a primary account with a refresh token. Should
// only be called in mode |kWaitUntilAvailable|.
bool AreCredentialsAvailable() const;
void StartAccessTokenRequest();
// IdentityManager::Observer implementation.
void OnPrimaryAccountChanged(const PrimaryAccountChangeEvent& event) override;
void OnRefreshTokenUpdatedForAccount(
const CoreAccountInfo& account_info) override;
// Checks whether credentials are now available and starts an access token
// request if so. Should only be called in mode |kWaitUntilAvailable|.
void ProcessSigninStateChange();
// Invoked by |fetcher_| when an access token request completes.
void OnAccessTokenFetchComplete(GoogleServiceAuthError error,
AccessTokenInfo access_token_info);
std::string oauth_consumer_name_;
IdentityManager* identity_manager_;
ScopeSet scopes_;
// Per the contract of this class, it is allowed for clients to delete this
// object as part of the invocation of |callback_|. Hence, this object must
// assume that it is dead after invoking |callback_| and must not run any more
// code.
AccessTokenFetcher::TokenCallback callback_;
base::ScopedObservation<IdentityManager, IdentityManager::Observer>
identity_manager_observation_{this};
// Internal fetcher that does the actual access token request.
std::unique_ptr<AccessTokenFetcher> access_token_fetcher_;
// When a token request gets canceled, we want to retry once.
bool access_token_retried_;
Mode mode_;
const ConsentLevel consent_;
DISALLOW_COPY_AND_ASSIGN(PrimaryAccountAccessTokenFetcher);
};
} // namespace signin
#endif // COMPONENTS_SIGNIN_PUBLIC_IDENTITY_MANAGER_PRIMARY_ACCOUNT_ACCESS_TOKEN_FETCHER_H_