1 // Copyright (c) 2013 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 #include "chrome/browser/extensions/api/identity/gaia_web_auth_flow.h"
6 
7 #include <vector>
8 
9 #include "base/run_loop.h"
10 #include "content/public/browser/storage_partition.h"
11 #include "content/public/test/browser_task_environment.h"
12 #include "content/public/test/test_storage_partition.h"
13 #include "services/network/test/test_cookie_manager.h"
14 #include "testing/gmock/include/gmock/gmock.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16 
17 namespace extensions {
18 
19 class DeleteCookiesTestCookieManager : public network::TestCookieManager {
20  public:
DeleteCookies(network::mojom::CookieDeletionFilterPtr filter,DeleteCookiesCallback callback)21   void DeleteCookies(network::mojom::CookieDeletionFilterPtr filter,
22                      DeleteCookiesCallback callback) override {
23     cookies_deleted_ = true;
24     std::move(callback).Run(0U);
25   }
cookies_deleted()26   bool cookies_deleted() { return cookies_deleted_; }
27 
28  private:
29   bool cookies_deleted_ = false;
30 };
31 
32 class FakeWebAuthFlow : public WebAuthFlow {
33  public:
FakeWebAuthFlow(WebAuthFlow::Delegate * delegate)34   explicit FakeWebAuthFlow(WebAuthFlow::Delegate* delegate)
35       : WebAuthFlow(delegate,
36                     nullptr,
37                     GURL(),
38                     WebAuthFlow::INTERACTIVE,
39                     WebAuthFlow::GET_AUTH_TOKEN) {
40     storage_partition.set_cookie_manager_for_browser_process(&cookie_manager);
41   }
42 
Start()43   void Start() override { EXPECT_TRUE(cookie_manager.cookies_deleted()); }
44 
GetGuestPartition()45   content::StoragePartition* GetGuestPartition() override {
46     return &storage_partition;
47   }
48 
49  private:
50   content::TestStoragePartition storage_partition;
51   DeleteCookiesTestCookieManager cookie_manager;
52 };
53 
54 class TestGaiaWebAuthFlow : public GaiaWebAuthFlow {
55  public:
TestGaiaWebAuthFlow(GaiaWebAuthFlow::Delegate * delegate,const ExtensionTokenKey * token_key,const std::string oauth2_client_id,GoogleServiceAuthError::State ubertoken_error_state)56   TestGaiaWebAuthFlow(GaiaWebAuthFlow::Delegate* delegate,
57                       const ExtensionTokenKey* token_key,
58                       const std::string oauth2_client_id,
59                       GoogleServiceAuthError::State ubertoken_error_state)
60       : GaiaWebAuthFlow(delegate, NULL, token_key, oauth2_client_id, "en-us"),
61         ubertoken_error_(ubertoken_error_state) {}
62 
Start()63   void Start() override {
64     OnUbertokenFetchComplete(
65         ubertoken_error_,
66         ubertoken_error_.state() == GoogleServiceAuthError::NONE
67             ? "fake_ubertoken"
68             : std::string());
69   }
70 
71  private:
CreateWebAuthFlow(GURL url)72   std::unique_ptr<WebAuthFlow> CreateWebAuthFlow(GURL url) override {
73     return std::unique_ptr<WebAuthFlow>(new FakeWebAuthFlow(this));
74   }
75 
76   GoogleServiceAuthError ubertoken_error_;
77 };
78 
79 class MockGaiaWebAuthFlowDelegate : public GaiaWebAuthFlow::Delegate {
80  public:
81   MOCK_METHOD3(OnGaiaFlowFailure,
82                void(GaiaWebAuthFlow::Failure failure,
83                     GoogleServiceAuthError service_error,
84                     const std::string& oauth_error));
85   MOCK_METHOD2(OnGaiaFlowCompleted,
86                void(const std::string& access_token,
87                     const std::string& expiration));
88 };
89 
90 class IdentityGaiaWebAuthFlowTest : public testing::Test {
91  public:
IdentityGaiaWebAuthFlowTest()92   IdentityGaiaWebAuthFlowTest()
93       : ubertoken_error_state_(GoogleServiceAuthError::NONE) {}
94 
TearDown()95   void TearDown() override {
96     testing::Test::TearDown();
97     base::RunLoop loop;
98     loop.RunUntilIdle();  // Run tasks so FakeWebAuthFlows get deleted.
99   }
100 
CreateTestFlow()101   std::unique_ptr<TestGaiaWebAuthFlow> CreateTestFlow() {
102     CoreAccountInfo user_info;
103     user_info.account_id = CoreAccountId("account_id");
104     user_info.gaia = "account_id";
105     user_info.email = "email";
106 
107     ExtensionTokenKey token_key("extension_id", user_info,
108                                 std::set<std::string>());
109     return std::unique_ptr<TestGaiaWebAuthFlow>(new TestGaiaWebAuthFlow(
110         &delegate_, &token_key, "fake.client.id", ubertoken_error_state_));
111   }
112 
GetFinalTitle(const std::string & fragment)113   std::string GetFinalTitle(const std::string& fragment) {
114     return std::string("Loading id.client.fake:/extension_id#") + fragment;
115   }
116 
GetNoneServiceError()117   GoogleServiceAuthError GetNoneServiceError() {
118     return GoogleServiceAuthError(GoogleServiceAuthError::NONE);
119   }
120 
set_ubertoken_error(GoogleServiceAuthError::State ubertoken_error_state)121   void set_ubertoken_error(
122       GoogleServiceAuthError::State ubertoken_error_state) {
123     ubertoken_error_state_ = ubertoken_error_state;
124   }
125 
126  protected:
127   testing::StrictMock<MockGaiaWebAuthFlowDelegate> delegate_;
128   GoogleServiceAuthError::State ubertoken_error_state_;
129   content::BrowserTaskEnvironment task_environment_;
130 };
131 
TEST_F(IdentityGaiaWebAuthFlowTest,OAuthError)132 TEST_F(IdentityGaiaWebAuthFlowTest, OAuthError) {
133   std::unique_ptr<TestGaiaWebAuthFlow> flow = CreateTestFlow();
134   flow->Start();
135   EXPECT_CALL(delegate_, OnGaiaFlowFailure(
136           GaiaWebAuthFlow::OAUTH_ERROR,
137           GoogleServiceAuthError(GoogleServiceAuthError::NONE),
138           "access_denied"));
139   flow->OnAuthFlowTitleChange(GetFinalTitle("error=access_denied"));
140 }
141 
TEST_F(IdentityGaiaWebAuthFlowTest,Token)142 TEST_F(IdentityGaiaWebAuthFlowTest, Token) {
143   std::unique_ptr<TestGaiaWebAuthFlow> flow = CreateTestFlow();
144   flow->Start();
145   EXPECT_CALL(delegate_, OnGaiaFlowCompleted("fake_access_token", ""));
146   flow->OnAuthFlowTitleChange(GetFinalTitle("access_token=fake_access_token"));
147 }
148 
TEST_F(IdentityGaiaWebAuthFlowTest,TokenAndExpiration)149 TEST_F(IdentityGaiaWebAuthFlowTest, TokenAndExpiration) {
150   std::unique_ptr<TestGaiaWebAuthFlow> flow = CreateTestFlow();
151   flow->Start();
152   EXPECT_CALL(delegate_, OnGaiaFlowCompleted("fake_access_token", "3600"));
153   flow->OnAuthFlowTitleChange(
154       GetFinalTitle("access_token=fake_access_token&expires_in=3600"));
155 }
156 
TEST_F(IdentityGaiaWebAuthFlowTest,ExtraFragmentParametersSuccess)157 TEST_F(IdentityGaiaWebAuthFlowTest, ExtraFragmentParametersSuccess) {
158   std::unique_ptr<TestGaiaWebAuthFlow> flow = CreateTestFlow();
159   flow->Start();
160   EXPECT_CALL(delegate_,
161               OnGaiaFlowCompleted("fake_access_token", "3600"));
162   flow->OnAuthFlowTitleChange(GetFinalTitle("chaff1=stuff&"
163                                             "expires_in=3600&"
164                                             "chaff2=and&"
165                                             "nonerror=fake_error&"
166                                             "chaff3=nonsense&"
167                                             "access_token=fake_access_token&"
168                                             "chaff4="));
169 }
170 
TEST_F(IdentityGaiaWebAuthFlowTest,ExtraFragmentParametersError)171 TEST_F(IdentityGaiaWebAuthFlowTest, ExtraFragmentParametersError) {
172   std::unique_ptr<TestGaiaWebAuthFlow> flow = CreateTestFlow();
173   flow->Start();
174   EXPECT_CALL(delegate_, OnGaiaFlowFailure(
175           GaiaWebAuthFlow::OAUTH_ERROR,
176           GoogleServiceAuthError(GoogleServiceAuthError::NONE),
177           "fake_error"));
178   flow->OnAuthFlowTitleChange(GetFinalTitle("chaff1=stuff&"
179                                             "expires_in=3600&"
180                                             "chaff2=and&"
181                                             "error=fake_error&"
182                                             "chaff3=nonsense&"
183                                             "access_token=fake_access_token&"
184                                             "chaff4="));
185 }
186 
TEST_F(IdentityGaiaWebAuthFlowTest,TitleSpam)187 TEST_F(IdentityGaiaWebAuthFlowTest, TitleSpam) {
188   std::unique_ptr<TestGaiaWebAuthFlow> flow = CreateTestFlow();
189   flow->Start();
190   flow->OnAuthFlowTitleChange(
191       "Loading https://extension_id.chromiumapp.org/#error=non_final_title");
192   flow->OnAuthFlowTitleChange("I'm feeling entitled.");
193   flow->OnAuthFlowTitleChange("");
194   flow->OnAuthFlowTitleChange(
195       "Loading id.client.fake:/bad_extension_id#error=non_final_title");
196   flow->OnAuthFlowTitleChange(
197       "Loading bad.id.client.fake:/extension_id#error=non_final_title");
198   EXPECT_CALL(delegate_, OnGaiaFlowCompleted("fake_access_token", ""));
199   flow->OnAuthFlowTitleChange(GetFinalTitle("access_token=fake_access_token"));
200 }
201 
TEST_F(IdentityGaiaWebAuthFlowTest,EmptyFragment)202 TEST_F(IdentityGaiaWebAuthFlowTest, EmptyFragment) {
203   std::unique_ptr<TestGaiaWebAuthFlow> flow = CreateTestFlow();
204   flow->Start();
205   EXPECT_CALL(
206       delegate_,
207       OnGaiaFlowFailure(
208           GaiaWebAuthFlow::INVALID_REDIRECT,
209           GoogleServiceAuthError(GoogleServiceAuthError::NONE),
210           ""));
211   flow->OnAuthFlowTitleChange(GetFinalTitle(""));
212 }
213 
TEST_F(IdentityGaiaWebAuthFlowTest,JunkFragment)214 TEST_F(IdentityGaiaWebAuthFlowTest, JunkFragment) {
215   std::unique_ptr<TestGaiaWebAuthFlow> flow = CreateTestFlow();
216   flow->Start();
217   EXPECT_CALL(
218       delegate_,
219       OnGaiaFlowFailure(
220           GaiaWebAuthFlow::INVALID_REDIRECT,
221           GoogleServiceAuthError(GoogleServiceAuthError::NONE),
222           ""));
223   flow->OnAuthFlowTitleChange(GetFinalTitle("thisisjustabunchofjunk"));
224 }
225 
TEST_F(IdentityGaiaWebAuthFlowTest,NoFragment)226 TEST_F(IdentityGaiaWebAuthFlowTest, NoFragment) {
227   std::unique_ptr<TestGaiaWebAuthFlow> flow = CreateTestFlow();
228   flow->Start();
229   // This won't be recognized as an interesting title.
230   flow->OnAuthFlowTitleChange("Loading id.client.fake:/extension_id");
231 }
232 
TEST_F(IdentityGaiaWebAuthFlowTest,Host)233 TEST_F(IdentityGaiaWebAuthFlowTest, Host) {
234   std::unique_ptr<TestGaiaWebAuthFlow> flow = CreateTestFlow();
235   flow->Start();
236   // These won't be recognized as interesting titles.
237   flow->OnAuthFlowTitleChange(
238       "Loading id.client.fake://extension_id#access_token=fake_access_token");
239   flow->OnAuthFlowTitleChange(
240       "Loading id.client.fake://extension_id/#access_token=fake_access_token");
241   flow->OnAuthFlowTitleChange(
242       "Loading "
243       "id.client.fake://host/extension_id/#access_token=fake_access_token");
244 }
245 
TEST_F(IdentityGaiaWebAuthFlowTest,UbertokenFailure)246 TEST_F(IdentityGaiaWebAuthFlowTest, UbertokenFailure) {
247   set_ubertoken_error(GoogleServiceAuthError::CONNECTION_FAILED);
248   std::unique_ptr<TestGaiaWebAuthFlow> flow = CreateTestFlow();
249   EXPECT_CALL(
250       delegate_,
251       OnGaiaFlowFailure(
252           GaiaWebAuthFlow::SERVICE_AUTH_ERROR,
253           GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED),
254           ""));
255   flow->Start();
256 }
257 
TEST_F(IdentityGaiaWebAuthFlowTest,AuthFlowFailure)258 TEST_F(IdentityGaiaWebAuthFlowTest, AuthFlowFailure) {
259   std::unique_ptr<TestGaiaWebAuthFlow> flow = CreateTestFlow();
260   flow->Start();
261   EXPECT_CALL(
262       delegate_,
263       OnGaiaFlowFailure(
264           GaiaWebAuthFlow::WINDOW_CLOSED,
265           GoogleServiceAuthError(GoogleServiceAuthError::NONE),
266           ""));
267   flow->OnAuthFlowFailure(WebAuthFlow::WINDOW_CLOSED);
268 }
269 
270 }  // namespace extensions
271