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.
5 #include "chrome/browser/extensions/api/identity/web_auth_flow.h"
7 #include "apps/shell_window.h"
8 #include "base/base64.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/extensions/extension_system.h"
16 #include "chrome/browser/extensions/extension_system.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/common/extensions/api/identity_private.h"
19 #include "chrome/common/extensions/extension_constants.h"
20 #include "content/public/browser/navigation_details.h"
21 #include "content/public/browser/navigation_entry.h"
22 #include "content/public/browser/notification_details.h"
23 #include "content/public/browser/notification_service.h"
24 #include "content/public/browser/notification_source.h"
25 #include "content/public/browser/notification_types.h"
26 #include "content/public/browser/render_view_host.h"
27 #include "content/public/browser/resource_request_details.h"
28 #include "content/public/browser/web_contents.h"
29 #include "crypto/random.h"
30 #include "extensions/browser/event_router.h"
31 #include "grit/browser_resources.h"
34 using apps::ShellWindow
;
35 using content::RenderViewHost
;
36 using content::ResourceRedirectDetails
;
37 using content::WebContents
;
38 using content::WebContentsObserver
;
40 namespace extensions
{
42 namespace identity_private
= api::identity_private
;
44 WebAuthFlow::WebAuthFlow(
47 const GURL
& provider_url
,
49 : delegate_(delegate
),
51 provider_url_(provider_url
),
53 embedded_window_created_(false) {
56 WebAuthFlow::~WebAuthFlow() {
57 DCHECK(delegate_
== NULL
);
59 // Stop listening to notifications first since some of the code
60 // below may generate notifications.
61 registrar_
.RemoveAll();
62 WebContentsObserver::Observe(NULL
);
64 if (!shell_window_key_
.empty()) {
65 apps::ShellWindowRegistry::Get(profile_
)->RemoveObserver(this);
67 if (shell_window_
&& shell_window_
->web_contents())
68 shell_window_
->web_contents()->Close();
72 void WebAuthFlow::Start() {
73 apps::ShellWindowRegistry::Get(profile_
)->AddObserver(this);
75 // Attach a random ID string to the window so we can recoginize it
76 // in OnShellWindowAdded.
77 std::string random_bytes
;
78 crypto::RandBytes(WriteInto(&random_bytes
, 33), 32);
79 bool success
= base::Base64Encode(random_bytes
, &shell_window_key_
);
82 // identityPrivate.onWebFlowRequest(shell_window_key, provider_url_, mode_)
83 scoped_ptr
<base::ListValue
> args(new base::ListValue());
84 args
->AppendString(shell_window_key_
);
85 args
->AppendString(provider_url_
.spec());
86 if (mode_
== WebAuthFlow::INTERACTIVE
)
87 args
->AppendString("interactive");
89 args
->AppendString("silent");
91 scoped_ptr
<Event
> event(
92 new Event(identity_private::OnWebFlowRequest::kEventName
, args
.Pass()));
93 event
->restrict_to_browser_context
= profile_
;
94 ExtensionSystem
* system
= ExtensionSystem::Get(profile_
);
96 extensions::ComponentLoader
* component_loader
=
97 system
->extension_service()->component_loader();
98 if (!component_loader
->Exists(extension_misc::kIdentityApiUiAppId
)) {
99 component_loader
->Add(
100 IDR_IDENTITY_API_SCOPE_APPROVAL_MANIFEST
,
101 base::FilePath(FILE_PATH_LITERAL("identity_scope_approval_dialog")));
104 system
->event_router()->DispatchEventWithLazyListener(
105 extension_misc::kIdentityApiUiAppId
, event
.Pass());
108 void WebAuthFlow::DetachDelegateAndDelete() {
110 base::MessageLoop::current()->DeleteSoon(FROM_HERE
, this);
113 void WebAuthFlow::OnShellWindowAdded(ShellWindow
* shell_window
) {
114 if (shell_window
->window_key() == shell_window_key_
&&
115 shell_window
->extension()->id() == extension_misc::kIdentityApiUiAppId
) {
116 shell_window_
= shell_window
;
117 WebContentsObserver::Observe(shell_window
->web_contents());
121 content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED
,
122 content::NotificationService::AllBrowserContextsAndSources());
126 void WebAuthFlow::OnShellWindowIconChanged(ShellWindow
* shell_window
) {}
128 void WebAuthFlow::OnShellWindowRemoved(ShellWindow
* shell_window
) {
129 if (shell_window
->window_key() == shell_window_key_
&&
130 shell_window
->extension()->id() == extension_misc::kIdentityApiUiAppId
) {
131 shell_window_
= NULL
;
132 registrar_
.RemoveAll();
135 delegate_
->OnAuthFlowFailure(WebAuthFlow::WINDOW_CLOSED
);
139 void WebAuthFlow::BeforeUrlLoaded(const GURL
& url
) {
140 if (delegate_
&& embedded_window_created_
)
141 delegate_
->OnAuthFlowURLChange(url
);
144 void WebAuthFlow::AfterUrlLoaded() {
145 if (delegate_
&& embedded_window_created_
&& mode_
== WebAuthFlow::SILENT
)
146 delegate_
->OnAuthFlowFailure(WebAuthFlow::INTERACTION_REQUIRED
);
149 void WebAuthFlow::Observe(int type
,
150 const content::NotificationSource
& source
,
151 const content::NotificationDetails
& details
) {
152 DCHECK(shell_window_
);
157 if (!embedded_window_created_
) {
158 DCHECK(type
== content::NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED
);
160 RenderViewHost
* render_view(
161 content::Details
<RenderViewHost
>(details
).ptr());
162 WebContents
* web_contents
= WebContents::FromRenderViewHost(render_view
);
165 (web_contents
->GetEmbedderWebContents() ==
166 WebContentsObserver::web_contents())) {
167 // Switch from watching the shell window to the guest inside it.
168 embedded_window_created_
= true;
169 WebContentsObserver::Observe(web_contents
);
171 registrar_
.RemoveAll();
173 content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT
,
174 content::Source
<WebContents
>(web_contents
));
176 content::NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED
,
177 content::Source
<WebContents
>(web_contents
));
180 // embedded_window_created_
182 case content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT
: {
183 ResourceRedirectDetails
* redirect_details
=
184 content::Details
<ResourceRedirectDetails
>(details
).ptr();
185 if (redirect_details
!= NULL
)
186 BeforeUrlLoaded(redirect_details
->new_url
);
189 case content::NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED
: {
190 std::pair
<content::NavigationEntry
*, bool>* title
=
191 content::Details
<std::pair
<content::NavigationEntry
*, bool> >(
195 delegate_
->OnAuthFlowTitleChange(
196 UTF16ToUTF8(title
->first
->GetTitle()));
202 << "Got a notification that we did not register for: " << type
;
208 void WebAuthFlow::RenderProcessGone(base::TerminationStatus status
) {
210 delegate_
->OnAuthFlowFailure(WebAuthFlow::WINDOW_CLOSED
);
213 void WebAuthFlow::DidStartProvisionalLoadForFrame(
215 int64 parent_frame_id
,
217 const GURL
& validated_url
,
219 bool is_iframe_srcdoc
,
220 RenderViewHost
* render_view_host
) {
222 BeforeUrlLoaded(validated_url
);
225 void WebAuthFlow::DidFailProvisionalLoad(
227 const base::string16
& frame_unique_name
,
229 const GURL
& validated_url
,
231 const base::string16
& error_description
,
232 RenderViewHost
* render_view_host
) {
234 delegate_
->OnAuthFlowFailure(LOAD_FAILED
);
237 void WebAuthFlow::DidStopLoading(RenderViewHost
* render_view_host
) {
241 void WebAuthFlow::DidNavigateMainFrame(
242 const content::LoadCommittedDetails
& details
,
243 const content::FrameNavigateParams
& params
) {
244 if (delegate_
&& details
.http_status_code
>= 400)
245 delegate_
->OnAuthFlowFailure(LOAD_FAILED
);
248 } // namespace extensions