web_auth_flow.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
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/bind.h" 8#include "base/command_line.h" 9#include "base/location.h" 10#include "base/message_loop.h" 11#include "base/string_util.h" 12#include "base/stringprintf.h" 13#include "chrome/browser/profiles/profile.h" 14#include "chrome/browser/ui/browser.h" 15#include "chrome/browser/ui/browser_navigator.h" 16#include "chrome/common/chrome_switches.h" 17#include "content/public/browser/load_notification_details.h" 18#include "content/public/browser/navigation_controller.h" 19#include "content/public/browser/notification_details.h" 20#include "content/public/browser/notification_source.h" 21#include "content/public/browser/notification_types.h" 22#include "content/public/browser/render_view_host.h" 23#include "content/public/browser/resource_request_details.h" 24#include "content/public/browser/web_contents.h" 25#include "content/public/common/page_transition_types.h" 26#include "googleurl/src/gurl.h" 27#include "ipc/ipc_message.h" 28#include "ui/base/window_open_disposition.h" 29 30using content::LoadNotificationDetails; 31using content::NavigationController; 32using content::RenderViewHost; 33using content::ResourceRedirectDetails; 34using content::WebContents; 35using content::WebContentsObserver; 36 37namespace { 38 39static const char kChromeExtensionSchemeUrlPattern[] = 40 "chrome-extension://%s/"; 41static const char kChromiumDomainRedirectUrlPattern[] = 42 "https://%s.chromiumapp.org/"; 43 44} // namespace 45 46namespace extensions { 47 48WebAuthFlow::WebAuthFlow( 49 Delegate* delegate, 50 Profile* profile, 51 const std::string& extension_id, 52 const GURL& provider_url, 53 Mode mode, 54 const gfx::Rect& initial_bounds, 55 chrome::HostDesktopType host_desktop_type) 56 : delegate_(delegate), 57 profile_(profile), 58 provider_url_(provider_url), 59 mode_(mode), 60 initial_bounds_(initial_bounds), 61 host_desktop_type_(host_desktop_type), 62 popup_shown_(false), 63 contents_(NULL) { 64 InitValidRedirectUrlPrefixes(extension_id); 65} 66 67WebAuthFlow::~WebAuthFlow() { 68 // Set the delegate to NULL to avoid reporting results twice. 69 delegate_ = NULL; 70 71 // Stop listening to notifications first since some of the code 72 // below may generate notifications. 73 registrar_.RemoveAll(); 74 75 if (contents_) { 76 if (popup_shown_) { 77 contents_->Close(); 78 } else { 79 contents_->Stop(); 80 // Tell message loop to delete contents_ instead of deleting it 81 // directly since destructor can run in response to a callback from 82 // contents_. 83 MessageLoop::current()->DeleteSoon(FROM_HERE, contents_); 84 } 85 } 86} 87 88void WebAuthFlow::Start() { 89 contents_ = CreateWebContents(); 90 WebContentsObserver::Observe(contents_); 91 92 NavigationController* controller = &(contents_->GetController()); 93 94 // Register for appropriate notifications to intercept navigation to the 95 // redirect URLs. 96 registrar_.Add( 97 this, 98 content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT, 99 content::Source<WebContents>(contents_)); 100 101 controller->LoadURL( 102 provider_url_, 103 content::Referrer(), 104 content::PAGE_TRANSITION_AUTO_TOPLEVEL, 105 std::string()); 106} 107 108WebContents* WebAuthFlow::CreateWebContents() { 109 return WebContents::Create(WebContents::CreateParams(profile_)); 110} 111 112void WebAuthFlow::ShowAuthFlowPopup() { 113 Browser::CreateParams browser_params(Browser::TYPE_POPUP, profile_, 114 host_desktop_type_); 115 browser_params.initial_bounds = initial_bounds_; 116 Browser* browser = new Browser(browser_params); 117 chrome::NavigateParams params(browser, contents_); 118 params.disposition = CURRENT_TAB; 119 params.window_action = chrome::NavigateParams::SHOW_WINDOW; 120 chrome::Navigate(¶ms); 121 // Observe method and WebContentsObserver::* methods will be called 122 // for varous navigation events. That is where we check for redirect 123 // to the right URL. 124 popup_shown_ = true; 125} 126 127bool WebAuthFlow::BeforeUrlLoaded(const GURL& url) { 128 if (IsValidRedirectUrl(url)) { 129 ReportResult(url); 130 return true; 131 } 132 return false; 133} 134 135void WebAuthFlow::AfterUrlLoaded() { 136 // Do nothing if a popup is already created. 137 if (popup_shown_) 138 return; 139 140 // Report results directly if not in interactive mode. 141 if (mode_ != WebAuthFlow::INTERACTIVE) { 142 ReportResult(GURL()); 143 return; 144 } 145 146 // We are in interactive mode and window is not shown yet; show the window. 147 ShowAuthFlowPopup(); 148} 149 150void WebAuthFlow::Observe(int type, 151 const content::NotificationSource& source, 152 const content::NotificationDetails& details) { 153 switch (type) { 154 case content::NOTIFICATION_RESOURCE_RECEIVED_REDIRECT: { 155 ResourceRedirectDetails* redirect_details = 156 content::Details<ResourceRedirectDetails>(details).ptr(); 157 if (redirect_details != NULL) 158 BeforeUrlLoaded(redirect_details->new_url); 159 } 160 break; 161 default: 162 NOTREACHED() << "Got a notification that we did not register for: " 163 << type; 164 break; 165 } 166} 167 168void WebAuthFlow::ProvisionalChangeToMainFrameUrl( 169 const GURL& url, 170 RenderViewHost* render_view_host) { 171 BeforeUrlLoaded(url); 172} 173 174void WebAuthFlow::DidStopLoading(RenderViewHost* render_view_host) { 175 AfterUrlLoaded(); 176} 177 178void WebAuthFlow::WebContentsDestroyed(WebContents* web_contents) { 179 contents_ = NULL; 180 ReportResult(GURL()); 181} 182 183void WebAuthFlow::ReportResult(const GURL& url) { 184 if (!delegate_) 185 return; 186 187 if (url.is_empty()) { 188 delegate_->OnAuthFlowFailure(); 189 } else { 190 // TODO(munjal): Consider adding code to parse out access token 191 // from some common places (e.g. URL fragment) so the apps don't 192 // have to do that work. 193 delegate_->OnAuthFlowSuccess(url.spec()); 194 } 195 196 // IMPORTANT: Do not access any members after calling the delegate 197 // since the delegate can destroy |this| in the callback and hence 198 // all data members are invalid after that. 199} 200 201bool WebAuthFlow::IsValidRedirectUrl(const GURL& url) const { 202 std::vector<std::string>::const_iterator iter; 203 for (iter = valid_prefixes_.begin(); iter != valid_prefixes_.end(); ++iter) { 204 if (StartsWithASCII(url.spec(), *iter, false)) { 205 return true; 206 } 207 } 208 return false; 209} 210 211void WebAuthFlow::InitValidRedirectUrlPrefixes( 212 const std::string& extension_id) { 213 valid_prefixes_.push_back(base::StringPrintf( 214 kChromeExtensionSchemeUrlPattern, extension_id.c_str())); 215 valid_prefixes_.push_back(base::StringPrintf( 216 kChromiumDomainRedirectUrlPattern, extension_id.c_str())); 217} 218 219} // namespace extensions 220