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