1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
|
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#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;
// static
std::optional<InstanceIdentityToken> InstanceIdentityToken::Create(
std::string_view jwt) {
// ID Tokens are comprised of three parts separated by periods:
// 1) Base64 encoded JSON header
// 2) Base64 encoded JSON payload
// 3) Signature bytes
auto parts =
base::SplitString(jwt, ".", base::WhitespaceHandling::KEEP_WHITESPACE,
base::SplitResult::SPLIT_WANT_NONEMPTY);
// Return early if the token is malformed or missing the signature.
if (parts.size() != 3) {
LOG(WARNING) << "Invalid instance identity token: " << jwt;
return std::nullopt;
}
// Parse and validate the header.
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);
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;
}
// Parse and validate the payload.
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);
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;
}
} // namespace remoting
|