Skip to content

Commit

Permalink
Serialize objects instead of enumerate keys
Browse files Browse the repository at this point in the history
Signed-off-by: Martin Conte Mac Donell <[email protected]>
  • Loading branch information
Reflejo committed Oct 24, 2023
1 parent a6d6713 commit 30a66fd
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 39 deletions.
8 changes: 4 additions & 4 deletions api/envoy/extensions/filters/http/jwt_authn/v3/config.proto
Original file line number Diff line number Diff line change
Expand Up @@ -763,8 +763,8 @@ message JwtClaimToHeader {
// the JSON name path.
string claim_name = 2 [(validate.rules).string = {min_len: 1}];

// If the claim name refers to a struct type (e.g. tenants: {"key": {}}), this field can be used to
// return a comma separated list of keys from the struct. This is useful for cases where the struct
// represents a list of elements such as tenants: {"key": {}, "key2": {}}.
bool list_claim_keys = 3;
// If the claim name refers to an object type (e.g. tenants: {"key": {}}), this field can be used to
// return the value serialized as JSON (base64 encoded). Note that if the claim refers to a field of
// any primitive type, this flag will be ignored.
bool allow_serialize_object = 3;
}
7 changes: 3 additions & 4 deletions changelogs/current.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,9 @@ removed_config_or_runtime:
new_features:
- area: jwt
change: |
Added a new field ``list_claim_keys`` on
Added a new field ``allow_serialize_object`` on
:ref:`claim_to_headers <envoy_v3_api_field_extensions.filters.http.jwt_authn.v3.JwtProvider.claim_to_headers>`
to extract keys from JWT token claims. This field enables the retrieval of keys from custom JWT
token claims, such as ``{"tenants": {"bitdrift": {}}`` (in this case a claim_name of ``tenants``
would extract the key ``bitdrift``).
to serialize custom claim objects from JWT tokens. When set to true, the filter will serialize the
claim as JSON and encode it to Base64.
deprecated:
Original file line number Diff line number Diff line change
Expand Up @@ -276,12 +276,12 @@ The field :ref:`claim_to_headers <envoy_v3_api_field_extensions.filters.http.jwt
claim_name: nested.claim.key
- header_name: x-jwt-tenants
claim_name: tenants
list_claim_keys: true
allow_serialize_object: true
JWT claim ("sub", "nested.claim.key" and "tenants") will be added to HTTP headers as following format:

.. code-block::
x-jwt-claim-sub: <JWT Claim>
x-jwt-claim-nested-key: <JWT Claim>
x-jwt-tenants: <Comma-separated JWT Claim>
x-jwt-tenants: <Base64 encoded JSON JWT Claim>
52 changes: 33 additions & 19 deletions source/extensions/filters/http/jwt_authn/authenticator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ class AuthenticatorImpl : public Logger::Loggable<Logger::Id::jwt>,

// Copy the JWT Claim to HTTP Header
void addJWTClaimToHeader(const std::string& claim_name, const std::string& header_name,
const bool list_claim_keys = false);
const bool allow_serialize_object = false);

// The jwks cache object.
JwksCache& jwks_cache_;
Expand Down Expand Up @@ -302,34 +302,48 @@ void AuthenticatorImpl::verifyKey() {

void AuthenticatorImpl::addJWTClaimToHeader(const std::string& claim_name,
const std::string& header_name,
const bool list_claim_keys) {
const bool allow_serialize_object) {
StructUtils payload_getter(jwt_->payload_pb_);
const ProtobufWkt::Value* claim_value;
const auto status = payload_getter.GetValue(claim_name, claim_value);
std::string str_claim_value;
if (status == StructUtils::OK) {
if (list_claim_keys) {
if (claim_value->has_struct_value()) {
const auto& fields = claim_value->struct_value().fields();
str_claim_value = absl::StrJoin(fields, ",", [](std::string* out, const auto& field) {
absl::StrAppend(out, field.first);
});
}
} else if (claim_value->kind_case() == Envoy::ProtobufWkt::Value::kStringValue) {
switch (claim_value->kind_case()) {
case Envoy::ProtobufWkt::Value::kStringValue:
str_claim_value = claim_value->string_value();
} else if (claim_value->kind_case() == Envoy::ProtobufWkt::Value::kNumberValue) {
break;
case Envoy::ProtobufWkt::Value::kNumberValue:
str_claim_value = convertClaimDoubleToString(claim_value->number_value());
} else if (claim_value->kind_case() == Envoy::ProtobufWkt::Value::kBoolValue) {
break;
case Envoy::ProtobufWkt::Value::kBoolValue:
str_claim_value = claim_value->bool_value() ? "true" : "false";
} else {
ENVOY_LOG(
debug,
"--------claim : {} is not a primitive type of int, double, string, or bool -----------",
claim_name);
break;
case Envoy::ProtobufWkt::Value::kStructValue:
ABSL_FALLTHROUGH_INTENDED;
case Envoy::ProtobufWkt::Value::kListValue: {
if (!allow_serialize_object) {
ENVOY_LOG(debug,
"[jwt_auth] claim : {} is an object type but allow_serialize_object is not set",
claim_name);
break;
}

std::string output;
auto status = claim_value->has_struct_value()
? ProtobufUtil::MessageToJsonString(claim_value->struct_value(), &output)
: ProtobufUtil::MessageToJsonString(claim_value->list_value(), &output);
if (status.ok()) {
str_claim_value = Envoy::Base64::encode(output.data(), output.size());
}
break;
}
default:
break;
}

if (!str_claim_value.empty()) {
headers_->addCopy(Http::LowerCaseString(header_name), str_claim_value);
ENVOY_LOG(debug, "--------claim : {} with value : {} is added to the header : {} -----------",
ENVOY_LOG(debug, "[jwt_auth] claim : {} with value : {} is added to the header : {}",
claim_name, str_claim_value, header_name);
}
}
Expand All @@ -354,7 +368,7 @@ void AuthenticatorImpl::handleGoodJwt(bool cache_hit) {
// Copy JWT claim to header
for (const auto& header_and_claim : provider.claim_to_headers()) {
addJWTClaimToHeader(header_and_claim.claim_name(), header_and_claim.header_name(),
header_and_claim.list_claim_keys());
header_and_claim.allow_serialize_object());
}

if (!provider.forward()) {
Expand Down
13 changes: 8 additions & 5 deletions test/extensions/filters/http/jwt_authn/authenticator_test.cc
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "envoy/config/core/v3/http_uri.pb.h"
#include "envoy/extensions/filters/http/jwt_authn/v3/config.pb.h"

#include "source/common/common/base64.h"
#include "source/common/http/message_impl.h"
#include "source/common/protobuf/utility.h"
#include "source/extensions/filters/http/common/jwks_fetcher.h"
Expand Down Expand Up @@ -154,10 +155,13 @@ TEST_F(AuthenticatorTest, TestClaimToHeader) {
EXPECT_EQ(headers.get_("x-jwt-bool-claim"), "true");
EXPECT_EQ(headers.get_("x-jwt-int-claim"), "9999");

// This check verifies whether the claim with the list_claim_keys value set is
// successfully added to header.
ASSERT_THAT(absl::StrSplit(headers.get_("x-jwt-claim-list-key"), ','),
testing::UnorderedElementsAre("key-2", "key-3", "key-4", "key-5"));
// This check verifies whether the claim with the allow_serialize_object value set is
// successfully serialized and added to headers.
std::string expected_json = "[\"str1\",\"str2\"]";

ASSERT_EQ(headers.get_("x-jwt-claim-object-key"),
Envoy::Base64::encode(expected_json.data(), expected_json.size()));
EXPECT_EQ(headers.get_("x-jwt-claim-primitive-key"), "9999");
}

// This test verifies when wrong claim is passed in claim_to_headers
Expand All @@ -177,7 +181,6 @@ TEST_F(AuthenticatorTest, TestClaimToHeaderWithHeaderReplace) {
EXPECT_EQ(headers.get_("x-jwt-claim-nested"), "value1");
EXPECT_FALSE(headers.has("x-jwt-claim-nested-wrong"));
EXPECT_FALSE(headers.has("x-jwt-unsupported-type-claim"));
EXPECT_FALSE(headers.has("x-jwt-claim-list-key-wrong"));
}

// This test verifies the Jwt is forwarded if "forward" flag is set.
Expand Down
10 changes: 5 additions & 5 deletions test/extensions/filters/http/jwt_authn/test_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,12 @@ const char ExampleConfig[] = R"(
claim_name: "nested.nested-2.key-3"
- header_name: "x-jwt-int-claim"
claim_name: "nested.nested-2.key-4"
- header_name: "x-jwt-claim-list-key"
claim_name: "nested.nested-2"
list_claim_keys: true
- header_name: "x-jwt-claim-list-key-wrong"
- header_name: "x-jwt-claim-object-key"
claim_name: "nested.nested-2.key-5"
allow_serialize_object: true
- header_name: "x-jwt-claim-primitive-key"
claim_name: "nested.nested-2.key-4"
list_claim_keys: true
allow_serialize_object: true
rules:
- match:
path: "/"
Expand Down

0 comments on commit 30a66fd

Please sign in to comment.