blob: e586765a4993f6f607ccce41e995ff7770a99aab [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "services/authentication/google_authentication_impl.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/message_loop/message_loop.h"
#include "base/trace_event/trace_event.h"
#include "base/values.h"
#include "mojo/common/binding_set.h"
#include "mojo/data_pipe_utils/data_pipe_drainer.h"
#include "mojo/data_pipe_utils/data_pipe_utils.h"
#include "mojo/public/c/system/main.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
#include "mojo/public/cpp/system/macros.h"
#include "mojo/services/network/interfaces/url_loader.mojom.h"
#include "services/authentication/credentials_impl_db.mojom.h"
#include "services/authentication/google_authentication_utils.h"
using namespace authentication::util;
namespace authentication {
GoogleAuthenticationServiceImpl::GoogleAuthenticationServiceImpl(
mojo::InterfaceRequest<AuthenticationService> request,
const mojo::String app_url,
mojo::NetworkServicePtr& network_service,
mojo::files::DirectoryPtr& directory)
: binding_(this, request.Pass()),
app_url_(app_url),
network_service_(network_service) {
accounts_db_manager_ = new AccountsDbManager(directory.Pass());
}
GoogleAuthenticationServiceImpl::~GoogleAuthenticationServiceImpl() {
delete accounts_db_manager_;
}
void GoogleAuthenticationServiceImpl::GetOAuth2Token(
const mojo::String& username,
mojo::Array<mojo::String> scopes,
const GetOAuth2TokenCallback& callback) {
if (!accounts_db_manager_->isValid()) {
callback.Run(nullptr, "Accounts db validation failed.");
return;
}
authentication::CredentialsPtr creds =
accounts_db_manager_->GetCredentials(username);
if (!creds->token) {
callback.Run(nullptr, "User grant not found");
return;
}
// TODO: Scopes are not used with the scoped refresh tokens. When we start
// supporting full login scoped tokens, then the scopes here gets used for
// Sidescoping.
mojo::Map<mojo::String, mojo::String> params;
params[kOAuth2ClientIdParamName] = kMojoShellOAuth2ClientId;
params[kOAuth2ClientSecretParamName] = kMojoShellOAuth2ClientSecret;
params[kOAuth2GrantTypeParamName] = kOAuth2RefreshTokenGrantType;
params[kOAuth2RefreshTokenParamName] = creds->token;
Request("https://www.googleapis.com/oauth2/v3/token", "POST",
BuildUrlQuery(params.Pass()),
base::Bind(&GoogleAuthenticationServiceImpl::OnGetOAuth2Token,
base::Unretained(this), callback));
}
void GoogleAuthenticationServiceImpl::SelectAccount(
bool returnLastSelected,
const SelectAccountCallback& callback) {
if (!accounts_db_manager_->isValid()) {
callback.Run(nullptr, "Accounts db validation failed.");
return;
}
mojo::String username;
if (returnLastSelected) {
username = accounts_db_manager_->GetAuthorizedUserForApp(app_url_);
if (!username.is_null()) {
callback.Run(username, nullptr);
return;
}
}
// TODO(ukode): Select one among the list of accounts using an AccountPicker
// UI instead of the first account always.
mojo::Array<mojo::String> users = accounts_db_manager_->GetAllUsers();
if (!users.size()) {
callback.Run(nullptr, "No user accounts found.");
return;
}
username = users[0];
accounts_db_manager_->UpdateAuthorization(app_url_, username);
callback.Run(username, nullptr);
}
void GoogleAuthenticationServiceImpl::ClearOAuth2Token(
const mojo::String& token) {}
void GoogleAuthenticationServiceImpl::OnGetOAuth2Token(
const GetOAuth2TokenCallback& callback,
const std::string& response,
const std::string& error) {
if (response.empty()) {
callback.Run(nullptr, "Error from server:" + error);
return;
}
scoped_ptr<base::DictionaryValue> dict(ParseOAuth2Response(response.c_str()));
if (!dict.get() || dict->HasKey("error")) {
callback.Run(nullptr, "Error in parsing response:" + response);
return;
}
std::string access_token;
dict->GetString("access_token", &access_token);
callback.Run(access_token, nullptr);
}
void GoogleAuthenticationServiceImpl::Request(
const std::string& url,
const std::string& method,
const std::string& message,
const mojo::Callback<void(std::string, std::string)>& callback) {
mojo::URLRequestPtr request(mojo::URLRequest::New());
request->url = url;
request->method = method;
request->auto_follow_redirects = true;
// Add headers
auto content_type_header = mojo::HttpHeader::New();
content_type_header->name = "Content-Type";
content_type_header->value = "application/x-www-form-urlencoded";
request->headers.push_back(content_type_header.Pass());
if (!message.empty()) {
request->body.push_back(
mojo::common::WriteStringToConsumerHandle(message).Pass());
}
mojo::URLLoaderPtr url_loader;
network_service_->CreateURLLoader(GetProxy(&url_loader));
url_loader->Start(
request.Pass(),
base::Bind(&GoogleAuthenticationServiceImpl::HandleServerResponse,
base::Unretained(this), callback));
url_loader.WaitForIncomingResponse();
}
void GoogleAuthenticationServiceImpl::HandleServerResponse(
const mojo::Callback<void(std::string, std::string)>& callback,
mojo::URLResponsePtr response) {
if (response.is_null()) {
LOG(WARNING) << "Something went horribly wrong...exiting!!";
callback.Run("", "Empty response");
return;
}
if (response->error) {
LOG(ERROR) << "Got error (" << response->error->code
<< "), reason: " << response->error->description.get().c_str();
callback.Run("", response->error->description.get().c_str());
return;
}
std::string response_body;
mojo::common::BlockingCopyToString(response->body.Pass(), &response_body);
callback.Run(response_body, "");
}
} // authentication namespace