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