Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / api / identity / gaia_web_auth_flow.cc
1 // Copyright (c) 2013 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/gaia_web_auth_flow.h"
6
7 #include "base/strings/string_number_conversions.h"
8 #include "base/strings/string_split.h"
9 #include "base/strings/string_util.h"
10 #include "base/strings/stringprintf.h"
11 #include "chrome/browser/profiles/profile.h"
12 #include "chrome/browser/signin/profile_oauth2_token_service.h"
13 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
14 #include "chrome/browser/signin/signin_manager.h"
15 #include "chrome/browser/signin/signin_manager_factory.h"
16 #include "google_apis/gaia/gaia_urls.h"
17 #include "net/base/escape.h"
18
19 namespace extensions {
20
21 GaiaWebAuthFlow::GaiaWebAuthFlow(Delegate* delegate,
22                                  Profile* profile,
23                                  const std::string& extension_id,
24                                  const OAuth2Info& oauth2_info,
25                                  const std::string& locale)
26     : delegate_(delegate),
27       profile_(profile) {
28   const char kOAuth2RedirectPathFormat[] = "/%s#";
29   const char kOAuth2AuthorizeFormat[] =
30       "?response_type=token&approval_prompt=force&authuser=0&"
31       "client_id=%s&"
32       "scope=%s&"
33       "origin=chrome-extension://%s/&"
34       "redirect_uri=%s:/%s&"
35       "hl=%s";
36
37   std::vector<std::string> client_id_parts;
38   base::SplitString(oauth2_info.client_id, '.', &client_id_parts);
39   std::reverse(client_id_parts.begin(), client_id_parts.end());
40   redirect_scheme_ = JoinString(client_id_parts, '.');
41
42   redirect_path_prefix_ =
43       base::StringPrintf(kOAuth2RedirectPathFormat, extension_id.c_str());
44
45   auth_url_ = GaiaUrls::GetInstance()->oauth2_auth_url().Resolve(
46       base::StringPrintf(kOAuth2AuthorizeFormat,
47                          oauth2_info.client_id.c_str(),
48                          net::EscapeUrlEncodedData(
49                              JoinString(oauth2_info.scopes, ' '), true).c_str(),
50                          extension_id.c_str(),
51                          redirect_scheme_.c_str(),
52                          extension_id.c_str(),
53                          locale.c_str()));
54 }
55
56 GaiaWebAuthFlow::~GaiaWebAuthFlow() {
57   if (web_flow_)
58     web_flow_.release()->DetachDelegateAndDelete();
59 }
60
61 void GaiaWebAuthFlow::Start() {
62   ProfileOAuth2TokenService* token_service =
63       ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
64   ubertoken_fetcher_.reset(new UbertokenFetcher(token_service,
65                                                 this,
66                                                 profile_->GetRequestContext()));
67   SigninManagerBase* signin_manager =
68       SigninManagerFactory::GetForProfile(profile_);
69   ubertoken_fetcher_->StartFetchingToken(
70       signin_manager->GetAuthenticatedAccountId());
71 }
72
73 void GaiaWebAuthFlow::OnUbertokenSuccess(const std::string& token) {
74   const char kMergeSessionQueryFormat[] = "?uberauth=%s&"
75                                           "continue=%s&"
76                                           "source=appsv2";
77
78   std::string merge_query = base::StringPrintf(
79       kMergeSessionQueryFormat,
80       net::EscapeUrlEncodedData(token, true).c_str(),
81       net::EscapeUrlEncodedData(auth_url_.spec(), true).c_str());
82   GURL merge_url(
83       GaiaUrls::GetInstance()->merge_session_url().Resolve(merge_query));
84
85   web_flow_ = CreateWebAuthFlow(merge_url);
86   web_flow_->Start();
87 }
88
89 void GaiaWebAuthFlow::OnUbertokenFailure(const GoogleServiceAuthError& error) {
90   delegate_->OnGaiaFlowFailure(
91       GaiaWebAuthFlow::SERVICE_AUTH_ERROR, error, std::string());
92 }
93
94 void GaiaWebAuthFlow::OnAuthFlowFailure(WebAuthFlow::Failure failure) {
95   GaiaWebAuthFlow::Failure gaia_failure;
96
97   switch (failure) {
98     case WebAuthFlow::WINDOW_CLOSED:
99       gaia_failure = GaiaWebAuthFlow::WINDOW_CLOSED;
100       break;
101     case WebAuthFlow::LOAD_FAILED:
102       gaia_failure = GaiaWebAuthFlow::LOAD_FAILED;
103       break;
104     default:
105       NOTREACHED() << "Unexpected error from web auth flow: " << failure;
106       gaia_failure = GaiaWebAuthFlow::LOAD_FAILED;
107       break;
108   }
109
110   delegate_->OnGaiaFlowFailure(
111       gaia_failure,
112       GoogleServiceAuthError(GoogleServiceAuthError::NONE),
113       std::string());
114 }
115
116 void GaiaWebAuthFlow::OnAuthFlowURLChange(const GURL& url) {
117   const char kOAuth2RedirectAccessTokenKey[] = "access_token";
118   const char kOAuth2RedirectErrorKey[] = "error";
119   const char kOAuth2ExpiresInKey[] = "expires_in";
120
121   // The format of the target URL is:
122   //     reversed.oauth.client.id:/extensionid#access_token=TOKEN
123   //
124   // Because there is no double slash, everything after the scheme is
125   // interpreted as a path, including the fragment.
126
127   if (url.scheme() == redirect_scheme_ && !url.has_host() && !url.has_port() &&
128       StartsWithASCII(url.GetContent(), redirect_path_prefix_, true)) {
129     web_flow_.release()->DetachDelegateAndDelete();
130
131     std::string fragment = url.GetContent().substr(
132         redirect_path_prefix_.length(), std::string::npos);
133     std::vector<std::pair<std::string, std::string> > pairs;
134     base::SplitStringIntoKeyValuePairs(fragment, '=', '&', &pairs);
135     std::string access_token;
136     std::string error;
137     std::string expiration;
138
139     for (std::vector<std::pair<std::string, std::string> >::iterator
140              it = pairs.begin();
141          it != pairs.end();
142          ++it) {
143       if (it->first == kOAuth2RedirectAccessTokenKey)
144         access_token = it->second;
145       else if (it->first == kOAuth2RedirectErrorKey)
146         error = it->second;
147       else if (it->first == kOAuth2ExpiresInKey)
148         expiration = it->second;
149     }
150
151     if (access_token.empty() && error.empty()) {
152       delegate_->OnGaiaFlowFailure(
153           GaiaWebAuthFlow::INVALID_REDIRECT,
154           GoogleServiceAuthError(GoogleServiceAuthError::NONE),
155           std::string());
156     } else if (!error.empty()) {
157       delegate_->OnGaiaFlowFailure(
158           GaiaWebAuthFlow::OAUTH_ERROR,
159           GoogleServiceAuthError(GoogleServiceAuthError::NONE),
160           error);
161     } else {
162       delegate_->OnGaiaFlowCompleted(access_token, expiration);
163     }
164   }
165 }
166
167 void GaiaWebAuthFlow::OnAuthFlowTitleChange(const std::string& title) {
168   // On the final page the title will be "Loading <redirect-url>".
169   // Treat it as though we'd really been redirected to <redirect-url>.
170   const char kRedirectPrefix[] = "Loading ";
171   std::string prefix(kRedirectPrefix);
172
173   if (StartsWithASCII(title, prefix, true)) {
174     GURL url(title.substr(prefix.length(), std::string::npos));
175     if (url.is_valid())
176       OnAuthFlowURLChange(url);
177   }
178 }
179
180 scoped_ptr<WebAuthFlow> GaiaWebAuthFlow::CreateWebAuthFlow(GURL url) {
181   return scoped_ptr<WebAuthFlow>(new WebAuthFlow(this,
182                                                  profile_,
183                                                  url,
184                                                  WebAuthFlow::INTERACTIVE));
185 }
186
187 }  // namespace extensions