// Copyright (c) 2012 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 GOOGLE_APIS_GAIA_OAUTH2_MINT_TOKEN_FLOW_H_ #define GOOGLE_APIS_GAIA_OAUTH2_MINT_TOKEN_FLOW_H_ #include #include #include #include "base/gtest_prod_util.h" #include "base/memory/raw_ptr.h" #include "base/memory/weak_ptr.h" #include "google_apis/gaia/oauth2_api_call_flow.h" #include "net/cookies/canonical_cookie.h" #include "services/network/public/mojom/url_response_head.mojom-forward.h" #include "url/gurl.h" class GoogleServiceAuthError; class OAuth2MintTokenFlowTest; namespace base { class Value; } extern const char kOAuth2MintTokenApiCallResultHistogram[]; // Values carrying the result of processing a successful API call. // These values are persisted to logs. Entries should not be renumbered and // numeric values should never be reused. enum class OAuth2MintTokenApiCallResult { kMintTokenSuccess = 0, // DEPRECATED: // kIssueAdviceSuccess = 1, kRemoteConsentSuccess = 2, kApiCallFailure = 3, kParseJsonFailure = 4, kIssueAdviceKeyNotFoundFailure = 5, kParseMintTokenFailure = 6, // DEPRECATED: // kParseIssueAdviceFailure = 7, // DEPRECATED: // kRemoteConsentFallback = 8 kParseRemoteConsentFailure = 9, // DEPRECATED: // kMintTokenSuccessWithFallbackScopes = 10, kMaxValue = kParseRemoteConsentFailure }; // Data for the remote consent resolution: // - URL of the consent page to be displayed to the user. // - Cookies that should be set before navigating to that URL. struct RemoteConsentResolutionData { RemoteConsentResolutionData(); ~RemoteConsentResolutionData(); RemoteConsentResolutionData(const RemoteConsentResolutionData& other); RemoteConsentResolutionData& operator=( const RemoteConsentResolutionData& other); GURL url; net::CookieList cookies; bool operator==(const RemoteConsentResolutionData& rhs) const; }; // This class implements the OAuth2 flow to Google to mint an OAuth2 access // token for the given client and the given set of scopes from the OAuthLogin // scoped "master" OAuth2 token for the user logged in to Chrome. class OAuth2MintTokenFlow : public OAuth2ApiCallFlow { public: // There are four different modes when minting a token to grant // access to third-party app for a user. enum Mode { // Get the messages to display to the user without minting a token. MODE_ISSUE_ADVICE, // Record a grant but do not get a token back. MODE_RECORD_GRANT, // Mint a token for an existing grant. MODE_MINT_TOKEN_NO_FORCE, // Mint a token forcefully even if there is no existing grant. MODE_MINT_TOKEN_FORCE, }; // Parameters needed to mint a token. struct Parameters { public: Parameters(); Parameters(const std::string& eid, const std::string& cid, const std::vector& scopes_arg, bool enable_granular_permissions, const std::string& device_id, const std::string& selected_user_id, const std::string& consent_result, const std::string& version, const std::string& channel, Mode mode_arg); Parameters(const Parameters& other); ~Parameters(); std::string extension_id; std::string client_id; std::vector scopes; bool enable_granular_permissions; std::string device_id; std::string selected_user_id; std::string consent_result; std::string version; std::string channel; Mode mode; }; class Delegate { public: virtual void OnMintTokenSuccess(const std::string& access_token, const std::set& granted_scopes, int time_to_live) {} virtual void OnMintTokenFailure(const GoogleServiceAuthError& error) {} virtual void OnRemoteConsentSuccess( const RemoteConsentResolutionData& resolution_data) {} protected: virtual ~Delegate() {} }; OAuth2MintTokenFlow(Delegate* delegate, const Parameters& parameters); OAuth2MintTokenFlow(const OAuth2MintTokenFlow&) = delete; OAuth2MintTokenFlow& operator=(const OAuth2MintTokenFlow&) = delete; ~OAuth2MintTokenFlow() override; protected: // Implementation of template methods in OAuth2ApiCallFlow. GURL CreateApiCallUrl() override; std::string CreateApiCallBody() override; void ProcessApiCallSuccess(const network::mojom::URLResponseHead* head, std::unique_ptr body) override; void ProcessApiCallFailure(int net_error, const network::mojom::URLResponseHead* head, std::unique_ptr body) override; net::PartialNetworkTrafficAnnotationTag GetNetworkTrafficAnnotationTag() override; private: friend class OAuth2MintTokenFlowTest; FRIEND_TEST_ALL_PREFIXES(OAuth2MintTokenFlowTest, CreateApiCallBody); FRIEND_TEST_ALL_PREFIXES(OAuth2MintTokenFlowTest, ParseIssueAdviceResponse); FRIEND_TEST_ALL_PREFIXES(OAuth2MintTokenFlowTest, ParseRemoteConsentResponse); FRIEND_TEST_ALL_PREFIXES(OAuth2MintTokenFlowTest, ParseRemoteConsentResponse_EmptyCookies); FRIEND_TEST_ALL_PREFIXES(OAuth2MintTokenFlowTest, ParseRemoteConsentResponse_NoResolutionData); FRIEND_TEST_ALL_PREFIXES(OAuth2MintTokenFlowTest, ParseRemoteConsentResponse_NoUrl); FRIEND_TEST_ALL_PREFIXES(OAuth2MintTokenFlowTest, ParseRemoteConsentResponse_BadUrl); FRIEND_TEST_ALL_PREFIXES(OAuth2MintTokenFlowTest, ParseRemoteConsentResponse_NoApproach); FRIEND_TEST_ALL_PREFIXES(OAuth2MintTokenFlowTest, ParseRemoteConsentResponse_BadApproach); FRIEND_TEST_ALL_PREFIXES(OAuth2MintTokenFlowTest, ParseRemoteConsentResponse_NoCookies); FRIEND_TEST_ALL_PREFIXES( OAuth2MintTokenFlowTest, ParseRemoteConsentResponse_BadCookie_MissingRequiredField); FRIEND_TEST_ALL_PREFIXES( OAuth2MintTokenFlowTest, ParseRemoteConsentResponse_MissingCookieOptionalField); FRIEND_TEST_ALL_PREFIXES(OAuth2MintTokenFlowTest, ParseRemoteConsentResponse_BadCookie_BadMaxAge); FRIEND_TEST_ALL_PREFIXES(OAuth2MintTokenFlowTest, ParseRemoteConsentResponse_BadCookieList); FRIEND_TEST_ALL_PREFIXES(OAuth2MintTokenFlowTest, ParseMintTokenResponse); void ReportSuccess(const std::string& access_token, const std::set& granted_scopes, int time_to_live); void ReportRemoteConsentSuccess( const RemoteConsentResolutionData& resolution_data); void ReportFailure(const GoogleServiceAuthError& error); static bool ParseRemoteConsentResponse( const base::Value* dict, RemoteConsentResolutionData* resolution_data); // Currently, grantedScopes is a new parameter for an unlaunched feature, so // it may not always be populated in server responses. In those cases, // ParseMintTokenResponse can still succeed and will just leave the // granted_scopes set unmodified. When the grantedScopes parameter is present // and the function returns true, granted_scopes will include the scopes // returned by the server. Once the feature is fully launched, this function // will be updated to fail if the grantedScopes parameter is missing. static bool ParseMintTokenResponse(const base::Value* dict, std::string* access_token, std::set* granted_scopes, int* time_to_live); raw_ptr delegate_; Parameters parameters_; base::WeakPtrFactory weak_factory_{this}; }; #endif // GOOGLE_APIS_GAIA_OAUTH2_MINT_TOKEN_FLOW_H_