blob: eac6a7c18b4a421ec39257c298eb9e9d6604f2ef [file] [log] [blame]
// Copyright 2017 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 "components/signin/core/browser/dice_header_helper.h"
#include <vector>
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "google_apis/gaia/gaia_auth_util.h"
#include "google_apis/gaia/gaia_urls.h"
namespace signin {
const char kDiceProtocolVersion[] = "1";
namespace {
// Request parameters.
const char kRequestSigninAll[] = "all_accounts";
const char kRequestSignoutNoConfirmation[] = "no_confirmation";
const char kRequestSignoutShowConfirmation[] = "show_confirmation";
// Signin response parameters.
const char kSigninActionAttrName[] = "action";
const char kSigninAuthUserAttrName[] = "authuser";
const char kSigninAuthorizationCodeAttrName[] = "authorization_code";
const char kSigninEmailAttrName[] = "email";
const char kSigninIdAttrName[] = "id";
// Signout response parameters.
const char kSignoutEmailAttrName[] = "email";
const char kSignoutSessionIndexAttrName[] = "sessionindex";
const char kSignoutObfuscatedIDAttrName[] = "obfuscatedid";
// Determines the Dice action that has been passed from Gaia in the header.
DiceAction GetDiceActionFromHeader(const std::string& value) {
if (value == "SIGNIN")
return DiceAction::SIGNIN;
else if (value == "SIGNOUT")
return DiceAction::SIGNOUT;
else if (value == "ENABLE_SYNC")
return DiceAction::ENABLE_SYNC;
else
return DiceAction::NONE;
}
} // namespace
DiceHeaderHelper::DiceHeaderHelper(AccountConsistencyMethod account_consistency)
: SigninHeaderHelper("Dice"), account_consistency_(account_consistency) {}
// static
DiceResponseParams DiceHeaderHelper::BuildDiceSigninResponseParams(
const std::string& header_value) {
DCHECK(!header_value.empty());
DiceResponseParams params;
ResponseHeaderDictionary header_dictionary =
ParseAccountConsistencyResponseHeader(header_value);
if (header_dictionary.count(kSigninActionAttrName) != 1u)
return params;
DiceResponseParams::AccountInfo* info = nullptr;
switch (GetDiceActionFromHeader(
header_dictionary.find(kSigninActionAttrName)->second)) {
case DiceAction::NONE:
case DiceAction::SIGNOUT:
DLOG(WARNING) << "Only SIGNIN and ENABLE_SYNC are supported through "
<< "X-Chrome-ID-Consistency-Response :" << header_value;
return params;
case DiceAction::SIGNIN:
params.user_intention = DiceAction::SIGNIN;
params.signin_info = std::make_unique<DiceResponseParams::SigninInfo>();
info = &params.signin_info->account_info;
break;
case DiceAction::ENABLE_SYNC:
params.user_intention = DiceAction::ENABLE_SYNC;
params.enable_sync_info =
std::make_unique<DiceResponseParams::EnableSyncInfo>();
info = &params.enable_sync_info->account_info;
break;
}
ResponseHeaderDictionary::const_iterator it = header_dictionary.begin();
for (; it != header_dictionary.end(); ++it) {
const std::string key_name(it->first);
const std::string value(it->second);
if (key_name == kSigninActionAttrName) {
// Do nothing, this was already parsed.
} else if (key_name == kSigninIdAttrName) {
info->gaia_id = value;
} else if (key_name == kSigninEmailAttrName) {
info->email = value;
} else if (key_name == kSigninAuthUserAttrName) {
bool parse_success = base::StringToInt(value, &info->session_index);
if (!parse_success)
info->session_index = -1;
} else if (key_name == kSigninAuthorizationCodeAttrName) {
if (params.signin_info)
params.signin_info->authorization_code = value;
else
DLOG(WARNING) << "Authorization code expected only with SIGNIN action";
} else {
DLOG(WARNING) << "Unexpected Gaia header attribute '" << key_name << "'.";
}
}
if (info->gaia_id.empty() || info->email.empty() ||
info->session_index == -1) {
DLOG(WARNING) << "Missing account info in Dice header: " << header_value;
return DiceResponseParams();
}
if (params.signin_info && params.signin_info->authorization_code.empty()) {
DLOG(WARNING) << "Missing authorization code in Dice SIGNIN header: "
<< header_value;
return DiceResponseParams();
}
return params;
}
// static
DiceResponseParams DiceHeaderHelper::BuildDiceSignoutResponseParams(
const std::string& header_value) {
// Google internal documentation of this header at:
// http://go/gaia-response-headers
DCHECK(!header_value.empty());
DiceResponseParams params;
params.user_intention = DiceAction::SIGNOUT;
std::vector<std::string> gaia_ids;
std::vector<std::string> emails;
std::vector<int> session_indices;
ResponseHeaderDictionary header_dictionary =
ParseAccountConsistencyResponseHeader(header_value);
ResponseHeaderDictionary::const_iterator it = header_dictionary.begin();
for (; it != header_dictionary.end(); ++it) {
const std::string key_name(it->first);
const std::string value(it->second);
if (key_name == kSignoutObfuscatedIDAttrName) {
gaia_ids.push_back(value);
// The Gaia ID is wrapped in quotes.
base::TrimString(value, "\"", &gaia_ids.back());
} else if (key_name == kSignoutEmailAttrName) {
// The email is wrapped in quotes.
emails.push_back(value);
base::TrimString(value, "\"", &emails.back());
} else if (key_name == kSignoutSessionIndexAttrName) {
int session_index = -1;
bool parse_success = base::StringToInt(value, &session_index);
if (parse_success)
session_indices.push_back(session_index);
} else {
DLOG(WARNING) << "Unexpected Gaia header attribute '" << key_name << "'.";
}
}
if ((gaia_ids.size() != emails.size()) ||
(gaia_ids.size() != session_indices.size())) {
DLOG(WARNING) << "Invalid parameter count for Dice SIGNOUT header: "
<< header_value;
return DiceResponseParams();
}
if (gaia_ids.empty()) {
DLOG(WARNING) << "No account specified in Dice SIGNOUT header";
return DiceResponseParams();
}
params.signout_info = std::make_unique<DiceResponseParams::SignoutInfo>();
for (size_t i = 0; i < gaia_ids.size(); ++i) {
params.signout_info->account_infos.emplace_back(gaia_ids[i], emails[i],
session_indices[i]);
}
return params;
}
bool DiceHeaderHelper::ShouldBuildRequestHeader(
const GURL& url,
const content_settings::CookieSettings* cookie_settings) {
return IsUrlEligibleForRequestHeader(url);
}
bool DiceHeaderHelper::IsUrlEligibleForRequestHeader(const GURL& url) {
if (account_consistency_ == AccountConsistencyMethod::kDisabled ||
account_consistency_ == AccountConsistencyMethod::kMirror) {
return false;
}
return gaia::IsGaiaSignonRealm(url.GetOrigin());
}
std::string DiceHeaderHelper::BuildRequestHeader(
const std::string& sync_account_id,
const std::string& device_id) {
std::vector<std::string> parts;
parts.push_back(base::StringPrintf("version=%s", kDiceProtocolVersion));
parts.push_back("client_id=" +
GaiaUrls::GetInstance()->oauth2_chrome_client_id());
if (!device_id.empty())
parts.push_back("device_id=" + device_id);
if (!sync_account_id.empty())
parts.push_back("sync_account_id=" + sync_account_id);
// Restrict Signin to Sync account only when fixing auth errors.
std::string signin_mode = kRequestSigninAll;
parts.push_back("signin_mode=" + signin_mode);
// Show the signout confirmation only when Dice is fully enabled.
const char* signout_mode_value =
(account_consistency_ == AccountConsistencyMethod::kDice)
? kRequestSignoutShowConfirmation
: kRequestSignoutNoConfirmation;
parts.push_back(base::StringPrintf("signout_mode=%s", signout_mode_value));
return base::JoinString(parts, ",");
}
} // namespace signin