identity_api.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/identity_api.h" 6 7#include "base/lazy_instance.h" 8#include "base/values.h" 9#include "chrome/browser/extensions/extension_function_dispatcher.h" 10#include "chrome/browser/extensions/extension_install_prompt.h" 11#include "chrome/browser/extensions/extension_service.h" 12#include "chrome/browser/extensions/permissions_updater.h" 13#include "chrome/browser/profiles/profile.h" 14#include "chrome/browser/signin/token_service.h" 15#include "chrome/browser/signin/token_service_factory.h" 16#include "chrome/browser/ui/browser.h" 17#include "chrome/browser/ui/browser_navigator.h" 18#include "chrome/browser/ui/webui/signin/login_ui_service.h" 19#include "chrome/browser/ui/webui/signin/login_ui_service_factory.h" 20#include "chrome/browser/ui/webui/sync_promo/sync_promo_ui.h" 21#include "chrome/common/extensions/api/experimental_identity.h" 22#include "chrome/common/extensions/api/identity/oauth2_manifest_handler.h" 23#include "chrome/common/extensions/extension.h" 24#include "chrome/common/extensions/extension_manifest_constants.h" 25#include "chrome/common/extensions/manifest_handler.h" 26#include "chrome/common/url_constants.h" 27#include "content/public/common/page_transition_types.h" 28#include "googleurl/src/gurl.h" 29#include "ui/base/window_open_disposition.h" 30 31namespace extensions { 32 33namespace identity_constants { 34const char kInvalidClientId[] = "Invalid OAuth2 Client ID."; 35const char kInvalidScopes[] = "Invalid OAuth2 scopes."; 36const char kAuthFailure[] = "OAuth2 request failed: "; 37const char kNoGrant[] = "OAuth2 not granted or revoked."; 38const char kUserRejected[] = "The user did not approve access."; 39const char kUserNotSignedIn[] = "The user is not signed in."; 40const char kInvalidRedirect[] = "Did not redirect to the right URL."; 41} // namespace identity_constants 42 43namespace GetAuthToken = api::experimental_identity::GetAuthToken; 44namespace LaunchWebAuthFlow = api::experimental_identity::LaunchWebAuthFlow; 45namespace identity = api::experimental_identity; 46 47IdentityGetAuthTokenFunction::IdentityGetAuthTokenFunction() 48 : interactive_(false) {} 49IdentityGetAuthTokenFunction::~IdentityGetAuthTokenFunction() {} 50 51bool IdentityGetAuthTokenFunction::RunImpl() { 52 scoped_ptr<GetAuthToken::Params> params(GetAuthToken::Params::Create(*args_)); 53 EXTENSION_FUNCTION_VALIDATE(params.get()); 54 if (params->details.get() && params->details->interactive.get()) 55 interactive_ = *params->details->interactive; 56 57 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension()); 58 59 // Check that the necessary information is present in the manfist. 60 if (oauth2_info.client_id.empty()) { 61 error_ = identity_constants::kInvalidClientId; 62 return false; 63 } 64 65 if (oauth2_info.scopes.size() == 0) { 66 error_ = identity_constants::kInvalidScopes; 67 return false; 68 } 69 70 // Balanced in OnIssueAdviceSuccess|OnMintTokenSuccess|OnMintTokenFailure| 71 // InstallUIAbort|OnLoginUIClosed. 72 AddRef(); 73 74 if (!HasLoginToken()) { 75 if (StartLogin()) { 76 return true; 77 } else { 78 Release(); 79 return false; 80 } 81 } 82 83 if (StartFlow(OAuth2MintTokenFlow::MODE_MINT_TOKEN_NO_FORCE)) { 84 return true; 85 } else { 86 Release(); 87 return false; 88 } 89} 90 91void IdentityGetAuthTokenFunction::OnMintTokenSuccess( 92 const std::string& access_token) { 93 SetResult(Value::CreateStringValue(access_token)); 94 SendResponse(true); 95 Release(); // Balanced in RunImpl. 96} 97 98void IdentityGetAuthTokenFunction::OnMintTokenFailure( 99 const GoogleServiceAuthError& error) { 100 error_ = std::string(identity_constants::kAuthFailure) + error.ToString(); 101 SendResponse(false); 102 Release(); // Balanced in RunImpl. 103} 104 105void IdentityGetAuthTokenFunction::OnIssueAdviceSuccess( 106 const IssueAdviceInfo& issue_advice) { 107 // Existing grant was revoked and we used NO_FORCE, so we got info back 108 // instead. 109 if (interactive_) { 110 install_ui_.reset(new ExtensionInstallPrompt(GetAssociatedWebContents())); 111 ShowOAuthApprovalDialog(issue_advice); 112 } else { 113 error_ = identity_constants::kNoGrant; 114 SendResponse(false); 115 Release(); // Balanced in RunImpl. 116 } 117} 118 119void IdentityGetAuthTokenFunction::OnLoginUIClosed( 120 LoginUIService::LoginUI* ui) { 121 StopObservingLoginService(); 122 if (!StartFlow(OAuth2MintTokenFlow::MODE_MINT_TOKEN_NO_FORCE)) { 123 SendResponse(false); 124 Release(); 125 } 126} 127 128void IdentityGetAuthTokenFunction::InstallUIProceed() { 129 DCHECK(install_ui_->record_oauth2_grant()); 130 // The user has accepted the scopes, so we may now force (recording a grant 131 // and receiving a token). 132 bool success = StartFlow(OAuth2MintTokenFlow::MODE_MINT_TOKEN_FORCE); 133 DCHECK(success); 134} 135 136void IdentityGetAuthTokenFunction::InstallUIAbort(bool user_initiated) { 137 error_ = identity_constants::kUserRejected; 138 SendResponse(false); 139 Release(); // Balanced in RunImpl. 140} 141 142bool IdentityGetAuthTokenFunction::StartFlow(OAuth2MintTokenFlow::Mode mode) { 143 if (!HasLoginToken()) { 144 error_ = identity_constants::kUserNotSignedIn; 145 return false; 146 } 147 148 flow_.reset(CreateMintTokenFlow(mode)); 149 flow_->Start(); 150 return true; 151} 152 153bool IdentityGetAuthTokenFunction::StartLogin() { 154 if (!interactive_) { 155 error_ = identity_constants::kUserNotSignedIn; 156 return false; 157 } 158 159 ShowLoginPopup(); 160 return true; 161} 162 163void IdentityGetAuthTokenFunction::StartObservingLoginService() { 164 LoginUIService* login_ui_service = 165 LoginUIServiceFactory::GetForProfile(profile()); 166 login_ui_service->AddObserver(this); 167} 168 169void IdentityGetAuthTokenFunction::StopObservingLoginService() { 170 LoginUIService* login_ui_service = 171 LoginUIServiceFactory::GetForProfile(profile()); 172 login_ui_service->RemoveObserver(this); 173} 174 175void IdentityGetAuthTokenFunction::ShowLoginPopup() { 176 StartObservingLoginService(); 177 178 LoginUIService* login_ui_service = 179 LoginUIServiceFactory::GetForProfile(profile()); 180 login_ui_service->ShowLoginPopup(); 181} 182 183void IdentityGetAuthTokenFunction::ShowOAuthApprovalDialog( 184 const IssueAdviceInfo& issue_advice) { 185 install_ui_->ConfirmIssueAdvice(this, GetExtension(), issue_advice); 186} 187 188OAuth2MintTokenFlow* IdentityGetAuthTokenFunction::CreateMintTokenFlow( 189 OAuth2MintTokenFlow::Mode mode) { 190 const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(GetExtension()); 191 TokenService* token_service = TokenServiceFactory::GetForProfile(profile()); 192 return new OAuth2MintTokenFlow( 193 profile()->GetRequestContext(), 194 this, 195 OAuth2MintTokenFlow::Parameters( 196 token_service->GetOAuth2LoginRefreshToken(), 197 GetExtension()->id(), 198 oauth2_info.client_id, 199 oauth2_info.scopes, 200 mode)); 201} 202 203bool IdentityGetAuthTokenFunction::HasLoginToken() const { 204 TokenService* token_service = TokenServiceFactory::GetForProfile(profile()); 205 return token_service->HasOAuthLoginToken(); 206} 207 208IdentityLaunchWebAuthFlowFunction::IdentityLaunchWebAuthFlowFunction() {} 209IdentityLaunchWebAuthFlowFunction::~IdentityLaunchWebAuthFlowFunction() {} 210 211bool IdentityLaunchWebAuthFlowFunction::RunImpl() { 212 scoped_ptr<LaunchWebAuthFlow::Params> params( 213 LaunchWebAuthFlow::Params::Create(*args_)); 214 EXTENSION_FUNCTION_VALIDATE(params.get()); 215 const identity::WebAuthFlowDetails& details = params->details; 216 217 GURL auth_url(details.url); 218 WebAuthFlow::Mode mode = 219 details.interactive && *details.interactive ? 220 WebAuthFlow::INTERACTIVE : WebAuthFlow::SILENT; 221 222 // The bounds attributes are optional, but using 0 when they're not available 223 // does the right thing. 224 gfx::Rect initial_bounds; 225 if (details.width) 226 initial_bounds.set_width(*details.width); 227 if (details.height) 228 initial_bounds.set_height(*details.height); 229 if (details.left) 230 initial_bounds.set_x(*details.left); 231 if (details.top) 232 initial_bounds.set_y(*details.top); 233 234 AddRef(); // Balanced in OnAuthFlowSuccess/Failure. 235 236 Browser* current_browser = this->GetCurrentBrowser(); 237 chrome::HostDesktopType host_desktop_type = current_browser ? 238 current_browser->host_desktop_type() : chrome::GetActiveDesktop(); 239 auth_flow_.reset(new WebAuthFlow( 240 this, profile(), GetExtension()->id(), auth_url, mode, initial_bounds, 241 host_desktop_type)); 242 auth_flow_->Start(); 243 return true; 244} 245 246void IdentityLaunchWebAuthFlowFunction::OnAuthFlowSuccess( 247 const std::string& redirect_url) { 248 SetResult(Value::CreateStringValue(redirect_url)); 249 SendResponse(true); 250 Release(); // Balanced in RunImpl. 251} 252 253void IdentityLaunchWebAuthFlowFunction::OnAuthFlowFailure() { 254 error_ = identity_constants::kInvalidRedirect; 255 SendResponse(false); 256 Release(); // Balanced in RunImpl. 257} 258 259IdentityAPI::IdentityAPI(Profile* profile) { 260 (new OAuth2ManifestHandler)->Register(); 261} 262 263IdentityAPI::~IdentityAPI() { 264} 265 266static base::LazyInstance<ProfileKeyedAPIFactory<IdentityAPI> > 267 g_factory = LAZY_INSTANCE_INITIALIZER; 268 269// static 270ProfileKeyedAPIFactory<IdentityAPI>* IdentityAPI::GetFactoryInstance() { 271 return &g_factory.Get(); 272} 273 274} // namespace extensions 275