• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 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/web_auth_flow.h"
6 
7 #include "base/base64.h"
8 #include "base/debug/trace_event.h"
9 #include "base/location.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "chrome/browser/extensions/component_loader.h"
14 #include "chrome/browser/extensions/extension_service.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/common/extensions/api/identity_private.h"
17 #include "chrome/common/extensions/extension_constants.h"
18 #include "content/public/browser/navigation_details.h"
19 #include "content/public/browser/navigation_entry.h"
20 #include "content/public/browser/notification_details.h"
21 #include "content/public/browser/notification_service.h"
22 #include "content/public/browser/notification_source.h"
23 #include "content/public/browser/notification_types.h"
24 #include "content/public/browser/render_frame_host.h"
25 #include "content/public/browser/resource_request_details.h"
26 #include "content/public/browser/web_contents.h"
27 #include "crypto/random.h"
28 #include "extensions/browser/app_window/app_window.h"
29 #include "extensions/browser/event_router.h"
30 #include "extensions/browser/extension_system.h"
31 #include "extensions/browser/guest_view/guest_view_base.h"
32 #include "grit/browser_resources.h"
33 #include "url/gurl.h"
34 
35 using content::RenderViewHost;
36 using content::ResourceRedirectDetails;
37 using content::WebContents;
38 using content::WebContentsObserver;
39 
40 namespace extensions {
41 
42 namespace identity_private = api::identity_private;
43 
WebAuthFlow(Delegate * delegate,Profile * profile,const GURL & provider_url,Mode mode)44 WebAuthFlow::WebAuthFlow(
45     Delegate* delegate,
46     Profile* profile,
47     const GURL& provider_url,
48     Mode mode)
49     : delegate_(delegate),
50       profile_(profile),
51       provider_url_(provider_url),
52       mode_(mode),
53       embedded_window_created_(false) {
54 }
55 
~WebAuthFlow()56 WebAuthFlow::~WebAuthFlow() {
57   DCHECK(delegate_ == NULL);
58 
59   // Stop listening to notifications first since some of the code
60   // below may generate notifications.
61   registrar_.RemoveAll();
62   WebContentsObserver::Observe(NULL);
63 
64   if (!app_window_key_.empty()) {
65     AppWindowRegistry::Get(profile_)->RemoveObserver(this);
66 
67     if (app_window_ && app_window_->web_contents())
68       app_window_->web_contents()->Close();
69   }
70 }
71 
Start()72 void WebAuthFlow::Start() {
73   AppWindowRegistry::Get(profile_)->AddObserver(this);
74 
75   // Attach a random ID string to the window so we can recoginize it
76   // in OnAppWindowAdded.
77   std::string random_bytes;
78   crypto::RandBytes(WriteInto(&random_bytes, 33), 32);
79   base::Base64Encode(random_bytes, &app_window_key_);
80 
81   // identityPrivate.onWebFlowRequest(app_window_key, provider_url_, mode_)
82   scoped_ptr<base::ListValue> args(new base::ListValue());
83   args->AppendString(app_window_key_);
84   args->AppendString(provider_url_.spec());
85   if (mode_ == WebAuthFlow::INTERACTIVE)
86     args->AppendString("interactive");
87   else
88     args->AppendString("silent");
89 
90   scoped_ptr<Event> event(
91       new Event(identity_private::OnWebFlowRequest::kEventName, args.Pass()));
92   event->restrict_to_browser_context = profile_;
93   ExtensionSystem* system = ExtensionSystem::Get(profile_);
94 
95   extensions::ComponentLoader* component_loader =
96       system->extension_service()->component_loader();
97   if (!component_loader->Exists(extension_misc::kIdentityApiUiAppId)) {
98     component_loader->Add(
99         IDR_IDENTITY_API_SCOPE_APPROVAL_MANIFEST,
100         base::FilePath(FILE_PATH_LITERAL("identity_scope_approval_dialog")));
101   }
102 
103   system->event_router()->DispatchEventWithLazyListener(
104       extension_misc::kIdentityApiUiAppId, event.Pass());
105 }
106 
DetachDelegateAndDelete()107 void WebAuthFlow::DetachDelegateAndDelete() {
108   delegate_ = NULL;
109   base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
110 }
111 
OnAppWindowAdded(AppWindow * app_window)112 void WebAuthFlow::OnAppWindowAdded(AppWindow* app_window) {
113   if (app_window->window_key() == app_window_key_ &&
114       app_window->extension_id() == extension_misc::kIdentityApiUiAppId) {
115     app_window_ = app_window;
116     WebContentsObserver::Observe(app_window->web_contents());
117 
118     registrar_.Add(
119         this,
120         content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED,
121         content::NotificationService::AllBrowserContextsAndSources());
122   }
123 }
124 
OnAppWindowRemoved(AppWindow * app_window)125 void WebAuthFlow::OnAppWindowRemoved(AppWindow* app_window) {
126   if (app_window->window_key() == app_window_key_ &&
127       app_window->extension_id() == extension_misc::kIdentityApiUiAppId) {
128     app_window_ = NULL;
129     registrar_.RemoveAll();
130 
131     if (delegate_)
132       delegate_->OnAuthFlowFailure(WebAuthFlow::WINDOW_CLOSED);
133   }
134 }
135 
BeforeUrlLoaded(const GURL & url)136 void WebAuthFlow::BeforeUrlLoaded(const GURL& url) {
137   if (delegate_ && embedded_window_created_)
138     delegate_->OnAuthFlowURLChange(url);
139 }
140 
AfterUrlLoaded()141 void WebAuthFlow::AfterUrlLoaded() {
142   if (delegate_ && embedded_window_created_ && mode_ == WebAuthFlow::SILENT)
143     delegate_->OnAuthFlowFailure(WebAuthFlow::INTERACTION_REQUIRED);
144 }
145 
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)146 void WebAuthFlow::Observe(int type,
147                           const content::NotificationSource& source,
148                           const content::NotificationDetails& details) {
149   DCHECK(app_window_);
150 
151   if (!delegate_)
152     return;
153 
154   if (!embedded_window_created_) {
155     DCHECK(type == content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED);
156 
157     RenderViewHost* render_view(
158         content::Details<RenderViewHost>(details).ptr());
159     WebContents* web_contents = WebContents::FromRenderViewHost(render_view);
160     GuestViewBase* guest = GuestViewBase::FromWebContents(web_contents);
161     WebContents* embedder = guest ? guest->embedder_web_contents() : NULL;
162     if (web_contents &&
163         (embedder == WebContentsObserver::web_contents())) {
164       // Switch from watching the app window to the guest inside it.
165       embedded_window_created_ = true;
166       WebContentsObserver::Observe(web_contents);
167 
168       registrar_.RemoveAll();
169       registrar_.Add(this,
170                      content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT,
171                      content::Source<WebContents>(web_contents));
172       registrar_.Add(this,
173                      content::NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED,
174                      content::Source<WebContents>(web_contents));
175     }
176   } else {
177     // embedded_window_created_
178     switch (type) {
179       case content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT: {
180         ResourceRedirectDetails* redirect_details =
181             content::Details<ResourceRedirectDetails>(details).ptr();
182         if (redirect_details != NULL)
183           BeforeUrlLoaded(redirect_details->new_url);
184         break;
185       }
186       case content::NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED: {
187         std::pair<content::NavigationEntry*, bool>* title =
188             content::Details<std::pair<content::NavigationEntry*, bool> >(
189                 details).ptr();
190 
191         if (title->first) {
192           delegate_->OnAuthFlowTitleChange(
193               base::UTF16ToUTF8(title->first->GetTitle()));
194         }
195         break;
196       }
197       default:
198         NOTREACHED()
199             << "Got a notification that we did not register for: " << type;
200         break;
201     }
202   }
203 }
204 
RenderProcessGone(base::TerminationStatus status)205 void WebAuthFlow::RenderProcessGone(base::TerminationStatus status) {
206   if (delegate_)
207     delegate_->OnAuthFlowFailure(WebAuthFlow::WINDOW_CLOSED);
208 }
209 
DidStartProvisionalLoadForFrame(content::RenderFrameHost * render_frame_host,const GURL & validated_url,bool is_error_page,bool is_iframe_srcdoc)210 void WebAuthFlow::DidStartProvisionalLoadForFrame(
211     content::RenderFrameHost* render_frame_host,
212     const GURL& validated_url,
213     bool is_error_page,
214     bool is_iframe_srcdoc) {
215   if (!render_frame_host->GetParent())
216     BeforeUrlLoaded(validated_url);
217 }
218 
DidFailProvisionalLoad(content::RenderFrameHost * render_frame_host,const GURL & validated_url,int error_code,const base::string16 & error_description)219 void WebAuthFlow::DidFailProvisionalLoad(
220     content::RenderFrameHost* render_frame_host,
221     const GURL& validated_url,
222     int error_code,
223     const base::string16& error_description) {
224   TRACE_EVENT_ASYNC_STEP_PAST1("identity",
225                                "WebAuthFlow",
226                                this,
227                                "DidFailProvisionalLoad",
228                                "error_code",
229                                error_code);
230   if (delegate_)
231     delegate_->OnAuthFlowFailure(LOAD_FAILED);
232 }
233 
DidStopLoading(RenderViewHost * render_view_host)234 void WebAuthFlow::DidStopLoading(RenderViewHost* render_view_host) {
235   AfterUrlLoaded();
236 }
237 
DidNavigateMainFrame(const content::LoadCommittedDetails & details,const content::FrameNavigateParams & params)238 void WebAuthFlow::DidNavigateMainFrame(
239     const content::LoadCommittedDetails& details,
240     const content::FrameNavigateParams& params) {
241   if (delegate_ && details.http_status_code >= 400)
242     delegate_->OnAuthFlowFailure(LOAD_FAILED);
243 }
244 
245 }  // namespace extensions
246