#include "google_apis/gaia/gaia_auth_util.h"
#include <stddef.h>
#include <memory>
#include <string_view>
#include "base/base64.h"
#include "base/base64url.h"
#include "base/containers/contains.h"
#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/notreached.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/supports_user_data.h"
#include "base/values.h"
#include "google_apis/gaia/bound_oauth_token.pb.h"
#include "google_apis/gaia/gaia_id.h"
#include "google_apis/gaia/gaia_urls.h"
#include "google_apis/gaia/list_accounts_response.pb.h"
#include "google_apis/gaia/oauth2_mint_token_consent_result.pb.h"
#include "url/gurl.h"
#include "url/origin.h"
#include "url/scheme_host_port.h"
namespace gaia {
namespace {
const char kGmailDomain[] = "gmail.com";
const char kGoogleDomain[] = "google.com";
const char kGooglemailDomain[] = "googlemail.com";
std::string CanonicalizeEmailImpl(std::string_view email_address,
bool change_googlemail_to_gmail) {
std::string lower_case_email = base::ToLowerASCII(email_address);
std::vector<std::string> parts = base::SplitString(
lower_case_email, "@", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
if (parts.size() != 2U)
return lower_case_email;
if (change_googlemail_to_gmail && parts[1] == kGooglemailDomain)
parts[1] = kGmailDomain;
if (parts[1] == kGmailDomain)
base::RemoveChars(parts[0], ".", &parts[0]);
std::string new_email = base::JoinString(parts, "@");
VLOG(1) << "Canonicalized " << email_address << " to " << new_email;
return new_email;
}
}
ListedAccount::ListedAccount() = default;
ListedAccount::ListedAccount(const ListedAccount&) = default;
ListedAccount& ListedAccount::operator=(const ListedAccount&) = default;
ListedAccount::~ListedAccount() = default;
MultiloginAccountAuthCredentials::MultiloginAccountAuthCredentials(
GaiaId gaia_id,
std::string token,
std::string token_binding_assertion)
: gaia_id(std::move(gaia_id)),
token(std::move(token)),
token_binding_assertion(std::move(token_binding_assertion)) {}
std::string CanonicalizeEmail(std::string_view email_address) {
return CanonicalizeEmailImpl(email_address, false);
}
std::string CanonicalizeDomain(std::string_view domain) {
return base::ToLowerASCII(domain);
}
std::string SanitizeEmail(std::string_view email_address) {
std::string sanitized(email_address);
if (!base::Contains(sanitized, '@')) {
sanitized += '@';
sanitized += kGmailDomain;
}
return sanitized;
}
bool AreEmailsSame(std::string_view email1, std::string_view email2) {
return CanonicalizeEmailImpl(gaia::SanitizeEmail(email1), true) ==
CanonicalizeEmailImpl(gaia::SanitizeEmail(email2), true);
}
std::string ExtractDomainName(std::string_view email_address) {
if (email_address == "") {
DUMP_WILL_BE_NOTREACHED();
return std::string();
}
std::string email = CanonicalizeEmail(email_address);
size_t separator_pos = email.find('@');
if (separator_pos != std::string::npos &&
separator_pos < email.length() - 1) {
return email.substr(separator_pos + 1);
}
DUMP_WILL_BE_NOTREACHED() << "Not a proper email address: " << email;
return std::string();
}
bool IsGoogleInternalAccountEmail(std::string_view email) {
return ExtractDomainName(SanitizeEmail(email)) == kGoogleDomain;
}
bool IsGoogleRobotAccountEmail(std::string_view email) {
std::string domain_name = gaia::ExtractDomainName(SanitizeEmail(email));
return base::EndsWith(domain_name, "gserviceaccount.com") ||
base::EndsWith(domain_name, "googleusercontent.com");
}
bool HasGaiaSchemeHostPort(const GURL& url) {
const url::Origin& gaia_origin = GaiaUrls::GetInstance()->gaia_origin();
CHECK(!gaia_origin.opaque());
CHECK(gaia_origin.GetURL().SchemeIsHTTPOrHTTPS());
const url::SchemeHostPort& gaia_scheme_host_port =
gaia_origin.GetTupleOrPrecursorTupleIfOpaque();
return url::SchemeHostPort(url) == gaia_scheme_host_port;
}
bool ParseBinaryListAccountsData(const std::string& data,
std::vector<ListedAccount>* accounts) {
if (accounts) {
accounts->clear();
}
std::string decoded_data;
if (!base::Base64Decode(data, &decoded_data,
base::Base64DecodePolicy::kForgiving)) {
VLOG(1) << "Failed to decode ListAccounts data as a Base64 String";
return false;
}
ListAccountsResponse parsed_result;
if (!parsed_result.ParseFromString(decoded_data)) {
VLOG(1) << "malformed ListAccountsResponse";
return false;
}
for (const auto& account : parsed_result.account()) {
if (account.display_email().empty() || account.obfuscated_id().empty()) {
continue;
}
ListedAccount listed_account;
listed_account.email = CanonicalizeEmail(account.display_email());
listed_account.gaia_id = GaiaId(account.obfuscated_id());
listed_account.valid =
!account.has_valid_session() || account.valid_session();
listed_account.signed_out =
account.has_signed_out() && account.signed_out();
listed_account.verified =
!account.has_is_verified() || account.is_verified();
listed_account.raw_email = account.display_email();
if (accounts) {
accounts->push_back(std::move(listed_account));
}
}
return true;
}
bool ParseOAuth2MintTokenConsentResult(std::string_view consent_result,
bool* approved,
GaiaId* gaia_id) {
DCHECK(approved);
DCHECK(gaia_id);
std::string decoded_result;
if (!base::Base64UrlDecode(consent_result,
base::Base64UrlDecodePolicy::DISALLOW_PADDING,
&decoded_result)) {
VLOG(1) << "Base64UrlDecode() failed to decode the consent result";
return false;
}
OAuth2MintTokenConsentResult parsed_result;
if (!parsed_result.ParseFromString(decoded_result)) {
VLOG(1) << "Failed to parse the consent result protobuf message";
return false;
}
*approved = parsed_result.approved();
*gaia_id = GaiaId(parsed_result.obfuscated_id());
return true;
}
std::string CreateBoundOAuthToken(const GaiaId& gaia_id,
const std::string& refresh_token,
const std::string& binding_key_assertion) {
BoundOAuthToken bound_oauth_token;
bound_oauth_token.set_gaia_id(gaia_id.ToString());
bound_oauth_token.set_token(refresh_token);
bound_oauth_token.set_token_binding_assertion(binding_key_assertion);
std::string serialized = bound_oauth_token.SerializeAsString();
if (serialized.empty()) {
VLOG(1) << "Failed to serialize bound OAuth token to protobuf message";
return std::string();
}
std::string base64_encoded;
base::Base64UrlEncode(serialized, base::Base64UrlEncodePolicy::OMIT_PADDING,
&base64_encoded);
return base64_encoded;
}
std::string CreateMultiOAuthHeader(
const std::vector<MultiloginAccountAuthCredentials>& accounts) {
gaia::MultiOAuthHeader header;
for (const MultiloginAccountAuthCredentials& account : accounts) {
gaia::MultiOAuthHeader::AccountRequest request;
request.set_gaia_id(account.gaia_id.ToString());
request.set_token(account.token);
if (!account.token_binding_assertion.empty()) {
request.set_token_binding_assertion(account.token_binding_assertion);
}
header.mutable_account_requests()->Add(std::move(request));
}
std::string base64_encoded_header;
base::Base64UrlEncode(header.SerializeAsString(),
base::Base64UrlEncodePolicy::OMIT_PADDING,
&base64_encoded_header);
return base64_encoded_header;
}
}