#include "remoting/base/instance_identity_token.h"
#include <optional>
#include <ostream>
#include <string>
#include <string_view>
#include "base/base64.h"
#include "base/json/json_reader.h"
#include "base/logging.h"
#include "base/strings/string_split.h"
#include "base/values.h"
namespace remoting {
InstanceIdentityToken::InstanceIdentityToken(base::Value::Dict header,
base::Value::Dict payload)
: header_(std::move(header)), payload_(std::move(payload)) {}
InstanceIdentityToken::~InstanceIdentityToken() = default;
std::optional<InstanceIdentityToken> InstanceIdentityToken::Create(
std::string_view jwt) {
auto parts =
base::SplitString(jwt, ".", base::WhitespaceHandling::KEEP_WHITESPACE,
base::SplitResult::SPLIT_WANT_NONEMPTY);
if (parts.size() != 3) {
LOG(WARNING) << "Invalid instance identity token: " << jwt;
return std::nullopt;
}
auto encoded_header = parts[0];
std::string decoded_header;
if (!base::Base64Decode(encoded_header, &decoded_header,
base::Base64DecodePolicy::kForgiving)) {
LOG(WARNING) << "Failed to decode instance identity token header: "
<< encoded_header;
return std::nullopt;
}
auto header = base::JSONReader::ReadDict(
decoded_header, base::JSON_PARSE_CHROMIUM_EXTENSIONS);
if (!header.has_value()) {
LOG(WARNING) << "Failed to parse instance identity token header: "
<< decoded_header;
return std::nullopt;
}
if (!header->contains("kid") || !header->contains("alg") ||
!header->contains("typ")) {
LOG(WARNING) << "Invalid instance identity token header:\n" << *header;
return std::nullopt;
}
auto encoded_payload = parts[1];
std::string decoded_payload;
if (!base::Base64Decode(encoded_payload, &decoded_payload,
base::Base64DecodePolicy::kForgiving)) {
LOG(WARNING) << "Failed to decode instance identity token payload: "
<< encoded_payload;
return std::nullopt;
}
auto payload = base::JSONReader::ReadDict(
decoded_payload, base::JSON_PARSE_CHROMIUM_EXTENSIONS);
if (!payload.has_value()) {
LOG(WARNING) << "Failed to parse instance identity token payload: "
<< decoded_payload;
return std::nullopt;
}
if (!payload->contains("iss") || !payload->contains("aud") ||
!payload->contains("iat") || !payload->contains("exp") ||
!payload->contains("azp") || !payload->contains("google")) {
LOG(WARNING) << "Invalid instance identity token payload:\n" << *payload;
return std::nullopt;
}
auto* google_payload = payload->FindDict("google");
auto* compute_engine_payload =
google_payload ? google_payload->FindDict("compute_engine") : nullptr;
if (compute_engine_payload == nullptr ||
!compute_engine_payload->contains("instance_id") ||
!compute_engine_payload->contains("instance_name") ||
!compute_engine_payload->contains("project_id") ||
!compute_engine_payload->contains("project_number") ||
!compute_engine_payload->contains("zone")) {
LOG(WARNING) << "Invalid instance identity token payload:\n" << *payload;
return std::nullopt;
}
return InstanceIdentityToken(std::move(*header), std::move(*payload));
}
std::ostream& operator<<(std::ostream& out,
const InstanceIdentityToken& token) {
out << token.header() << token.payload();
return out;
}
}