1 // Copyright 2020 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/login_detection/login_detection_tab_helper.h"
6 
7 #include "base/test/metrics/histogram_tester.h"
8 #include "chrome/browser/login_detection/login_detection_util.h"
9 #include "chrome/browser/profiles/profile.h"
10 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
11 #include "components/ukm/test_ukm_recorder.h"
12 #include "content/public/browser/browser_context.h"
13 #include "content/public/browser/web_contents.h"
14 #include "content/public/test/navigation_simulator.h"
15 #include "services/metrics/public/cpp/ukm_builders.h"
16 
17 namespace login_detection {
18 
19 class LoginDetectionTabHelperTest : public ChromeRenderViewHostTestHarness {
20  public:
SetUp()21   void SetUp() override {
22     scoped_feature_list_.InitAndEnableFeature(kLoginDetection);
23     ChromeRenderViewHostTestHarness::SetUp();
24     ResetMetricsTesters();
25     LoginDetectionTabHelper::MaybeCreateForWebContents(web_contents());
26   }
27 
ResetMetricsTesters()28   void ResetMetricsTesters() {
29     histogram_tester_ = std::make_unique<base::HistogramTester>();
30     ukm_recorder_ = std::make_unique<ukm::TestAutoSetUkmRecorder>();
31   }
32 
33   // Verifies the UMA and UKM metrics for the given login detection type to be
34   // recorded.
VerifyLoginDetectionTypeMetrics(LoginDetectionTabHelper::LoginDetectionType type)35   void VerifyLoginDetectionTypeMetrics(
36       LoginDetectionTabHelper::LoginDetectionType type) {
37     histogram_tester_->ExpectUniqueSample("Login.PageLoad.DetectionType", type,
38                                           1);
39 
40     const auto& entries = ukm_recorder_->GetEntriesByName(
41         ukm::builders::LoginDetection::kEntryName);
42     ASSERT_EQ(1U, entries.size());
43     ukm::TestUkmRecorder::ExpectEntryMetric(
44         entries[0], ukm::builders::LoginDetection::kPage_LoginTypeName,
45         static_cast<int64_t>(type));
46   }
47 
48  protected:
49   base::test::ScopedFeatureList scoped_feature_list_;
50   std::unique_ptr<base::HistogramTester> histogram_tester_;
51   std::unique_ptr<ukm::TestAutoSetUkmRecorder> ukm_recorder_;
52 };
53 
TEST_F(LoginDetectionTabHelperTest,NoLogin)54 TEST_F(LoginDetectionTabHelperTest, NoLogin) {
55   NavigateAndCommit(GURL("https://foo.com/page.html"));
56   VerifyLoginDetectionTypeMetrics(
57       LoginDetectionTabHelper::LoginDetectionType::kNoLogin);
58 }
59 
TEST_F(LoginDetectionTabHelperTest,SimpleOAuthLogin)60 TEST_F(LoginDetectionTabHelperTest, SimpleOAuthLogin) {
61   NavigateAndCommit(GURL("https://foo.com/page.html"));
62   VerifyLoginDetectionTypeMetrics(
63       LoginDetectionTabHelper::LoginDetectionType::kNoLogin);
64   ResetMetricsTesters();
65 
66   // OAuth login start
67   NavigateAndCommit(GURL("https://oauth.com/authenticate?client_id=123"));
68   VerifyLoginDetectionTypeMetrics(
69       LoginDetectionTabHelper::LoginDetectionType::kNoLogin);
70 
71   // OAuth login complete
72   ResetMetricsTesters();
73   NavigateAndCommit(GURL("https://foo.com/redirect?code=secret"));
74   VerifyLoginDetectionTypeMetrics(
75       LoginDetectionTabHelper::LoginDetectionType::kOauthFirstTimeLoginFlow);
76 
77   // Subsequent navigations to OAuth signed-in site.
78   ResetMetricsTesters();
79   NavigateAndCommit(GURL("https://images.foo.com/page.html"));
80   VerifyLoginDetectionTypeMetrics(
81       LoginDetectionTabHelper::LoginDetectionType::kOauthLogin);
82 }
83 
TEST_F(LoginDetectionTabHelperTest,NavigationToOAuthLoggedInSite)84 TEST_F(LoginDetectionTabHelperTest, NavigationToOAuthLoggedInSite) {
85   NavigateAndCommit(GURL("https://foo.com/page.html"));
86   VerifyLoginDetectionTypeMetrics(
87       LoginDetectionTabHelper::LoginDetectionType::kNoLogin);
88   ResetMetricsTesters();
89 
90   // Trigger OAuth login so that the site is saved in prefs.
91   NavigateAndCommit(GURL("https://oauth.com/authenticate?client_id=123"));
92   NavigateAndCommit(GURL("https://foo.com/redirect?code=secret"));
93 
94   // Subsequent navigations to OAuth signed-in site.
95   ResetMetricsTesters();
96   NavigateAndCommit(GURL("https://images.foo.com/page.html"));
97   VerifyLoginDetectionTypeMetrics(
98       LoginDetectionTabHelper::LoginDetectionType::kOauthLogin);
99 
100   // Navigation to a non logged-in site.
101   ResetMetricsTesters();
102   NavigateAndCommit(GURL("https://bar.com/page.html"));
103   VerifyLoginDetectionTypeMetrics(
104       LoginDetectionTabHelper::LoginDetectionType::kNoLogin);
105 }
106 
TEST_F(LoginDetectionTabHelperTest,OAuthLoginViaRedirect)107 TEST_F(LoginDetectionTabHelperTest, OAuthLoginViaRedirect) {
108   NavigateAndCommit(GURL("https://foo.com/page.html"));
109   VerifyLoginDetectionTypeMetrics(
110       LoginDetectionTabHelper::LoginDetectionType::kNoLogin);
111   ResetMetricsTesters();
112 
113   // OAuth login start and complete via redirects.
114   auto simulator = content::NavigationSimulator::CreateBrowserInitiated(
115       GURL("https://foo.com/oauth_signin"), web_contents());
116   simulator->SetTransition(ui::PAGE_TRANSITION_LINK);
117   simulator->Start();
118   simulator->Redirect(GURL("https://oauth.com/authenticate?client_id=123"));
119   simulator->Redirect(GURL("https://oauth.com/user_login"));
120   simulator->Redirect(GURL("https://oauth.com/?username=user&password=123"));
121   simulator->Redirect(GURL("https://foo.com/redirect?code=secret"));
122   simulator->Commit();
123 
124   VerifyLoginDetectionTypeMetrics(
125       LoginDetectionTabHelper::LoginDetectionType::kOauthFirstTimeLoginFlow);
126 
127   // Subsequent navigations to OAuth signed-in site.
128   ResetMetricsTesters();
129   NavigateAndCommit(GURL("https://images.foo.com/page.html"));
130   VerifyLoginDetectionTypeMetrics(
131       LoginDetectionTabHelper::LoginDetectionType::kOauthLogin);
132 }
133 
134 // Test that OAuth login is still detected when there are intermediate
135 // navigations to other sites.
TEST_F(LoginDetectionTabHelperTest,InvalidOAuthLogins)136 TEST_F(LoginDetectionTabHelperTest, InvalidOAuthLogins) {
137   NavigateAndCommit(GURL("https://foo.com/page.html"));
138   VerifyLoginDetectionTypeMetrics(
139       LoginDetectionTabHelper::LoginDetectionType::kNoLogin);
140   ResetMetricsTesters();
141 
142   // OAuth login start
143   NavigateAndCommit(GURL("https://oauth.com/authenticate?client_id=123"));
144   VerifyLoginDetectionTypeMetrics(
145       LoginDetectionTabHelper::LoginDetectionType::kNoLogin);
146 
147   // Intermediate navigation just ignored
148   ResetMetricsTesters();
149   NavigateAndCommit(GURL("https://bar.com/page.html"));
150   VerifyLoginDetectionTypeMetrics(
151       LoginDetectionTabHelper::LoginDetectionType::kNoLogin);
152 
153   // OAuth login complete will be detected
154   ResetMetricsTesters();
155   NavigateAndCommit(GURL("https://foo.com/redirect?code=secret"));
156   VerifyLoginDetectionTypeMetrics(
157       LoginDetectionTabHelper::LoginDetectionType::kOauthFirstTimeLoginFlow);
158 
159   ResetMetricsTesters();
160   NavigateAndCommit(GURL("https://images.foo.com/page.html"));
161   VerifyLoginDetectionTypeMetrics(
162       LoginDetectionTabHelper::LoginDetectionType::kOauthLogin);
163 }
164 
165 // Test that OAuth login is still detected when there are intermediate redirect
166 // navigations to other sites.
TEST_F(LoginDetectionTabHelperTest,InvalidOAuthLoginsWithRedirect)167 TEST_F(LoginDetectionTabHelperTest, InvalidOAuthLoginsWithRedirect) {
168   NavigateAndCommit(GURL("https://foo.com/page.html"));
169   VerifyLoginDetectionTypeMetrics(
170       LoginDetectionTabHelper::LoginDetectionType::kNoLogin);
171   ResetMetricsTesters();
172 
173   // OAuth login start and complete via redirects.
174   auto simulator = content::NavigationSimulator::CreateBrowserInitiated(
175       GURL("https://foo.com/oauth_signin"), web_contents());
176   simulator->SetTransition(ui::PAGE_TRANSITION_LINK);
177   simulator->Start();
178   simulator->Redirect(GURL("https://oauth.com/authenticate?client_id=123"));
179   simulator->Redirect(GURL("https://oauth.com/user_login"));
180   simulator->Redirect(GURL("https://oauth.com/?username=user&password=123"));
181   simulator->Redirect(GURL("https://bar.com/page.html"));
182   simulator->Redirect(GURL("https://foo.com/redirect?code=secret"));
183   simulator->Commit();
184 
185   VerifyLoginDetectionTypeMetrics(
186       LoginDetectionTabHelper::LoginDetectionType::kOauthFirstTimeLoginFlow);
187 }
188 
189 }  // namespace login_detection
190