blob: fb769425adcc2dc38c1cf4083035748717f9ab1f [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 "google_apis/gaia/oauth2_id_token_decoder.h"
#include <base/base64url.h>
#include <base/json/json_reader.h>
#include <base/strings/string_split.h>
#include <base/values.h>
#include <memory>
namespace {
// The name of the service flag that defines the account is Unicorn.
const char kChildAccountServiceFlag[] = "uca";
// The name of the service flag that defines the account is in advanced
// protection program.
const char kAdvancedProtectionAccountServiceFlag[] = "tia";
// The key indexing service flags in the ID token JSON.
const char kServicesKey[] = "services";
// Decodes the JWT ID token to a dictionary. Returns whether the decoding was
// successful.
std::unique_ptr<base::Value> DecodeIdToken(const std::string id_token) {
const std::vector<base::StringPiece> token_pieces =
base::SplitStringPiece(base::StringPiece(id_token), ".",
base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
if (token_pieces.size() != 3) {
VLOG(1) << "Invalid id_token: not in JWT format";
return nullptr;
}
// Only the payload is used. The header is ignored, and signature
// verification is not needed since the token was obtained directly from LSO.
std::string payload;
if (!base::Base64UrlDecode(token_pieces[1],
base::Base64UrlDecodePolicy::IGNORE_PADDING,
&payload)) {
VLOG(1) << "Invalid id_token: not in Base64Url encoding";
return nullptr;
}
std::unique_ptr<base::Value> decoded_payload =
base::JSONReader::ReadDeprecated(payload);
if (!decoded_payload.get() ||
decoded_payload->type() != base::Value::Type::DICTIONARY) {
VLOG(1) << "Invalid id_token: paylod is not a well-formed JSON";
return nullptr;
}
return decoded_payload;
}
// Obtains a vector of service flags from the encoded JWT ID token. Returns
// whether decoding the ID token and obtaining the list of service flags from it
// was successful.
bool GetServiceFlags(const std::string id_token,
std::vector<std::string>* out_service_flags) {
DCHECK(out_service_flags->empty());
std::unique_ptr<base::Value> decoded_payload = DecodeIdToken(id_token);
if (decoded_payload == nullptr) {
VLOG(1) << "Failed to decode the id_token";
return false;
}
base::Value* service_flags_value_raw =
decoded_payload->FindKeyOfType(kServicesKey, base::Value::Type::LIST);
if (service_flags_value_raw == nullptr) {
VLOG(1) << "Missing service flags in the id_token";
return false;
}
base::Value::ListStorage& service_flags_value =
service_flags_value_raw->GetList();
for (size_t i = 0; i < service_flags_value.size(); ++i) {
const std::string& flag = service_flags_value[i].GetString();
if (flag.size())
out_service_flags->push_back(flag);
}
return true;
}
} // namespace
namespace gaia {
TokenServiceFlags ParseServiceFlags(const std::string& id_token) {
TokenServiceFlags token_service_flags;
std::vector<std::string> service_flags;
if (!GetServiceFlags(id_token, &service_flags)) {
// If service flags can’t be obtained, then assume these service flags
// are not set.
VLOG(1) << "Assuming the account doesn't have any service flag set "
<< "due to decoding failure";
return token_service_flags;
}
token_service_flags.is_child_account =
std::find(service_flags.begin(), service_flags.end(),
kChildAccountServiceFlag) != service_flags.end();
token_service_flags.is_under_advanced_protection =
std::find(service_flags.begin(), service_flags.end(),
kAdvancedProtectionAccountServiceFlag) != service_flags.end();
return token_service_flags;
}
} // namespace gaia