From 42934fba65f0821a7f0997a46eb738447555b813 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Conte=20Mac=20Donell?= Date: Tue, 24 Oct 2023 10:43:45 -0700 Subject: [PATCH] jwt_authn: Allow to extract keys from jwt struct claims (#30377) Added a new field `list_claim_keys` on :ref:`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"). Signed-off-by: Martin Conte Mac Donell --- changelogs/current.yaml | 4 +++ .../http/http_filters/jwt_authn_filter.rst | 5 ++- .../filters/http/jwt_authn/authenticator.cc | 34 ++++++++++++++----- .../http/jwt_authn/authenticator_test.cc | 8 +++++ .../filters/http/jwt_authn/test_common.h | 2 ++ 5 files changed, 43 insertions(+), 10 deletions(-) diff --git a/changelogs/current.yaml b/changelogs/current.yaml index 6563980fc8a3..8ddf455f3da8 100644 --- a/changelogs/current.yaml +++ b/changelogs/current.yaml @@ -36,5 +36,9 @@ removed_config_or_runtime: runtime flag and legacy code path. new_features: +- area: jwt + change: | + The jwt filter can now serialize non-primitive custom claims when maping claims to headers. + These claims will be serialized as JSON and encoded as Base64. deprecated: diff --git a/docs/root/configuration/http/http_filters/jwt_authn_filter.rst b/docs/root/configuration/http/http_filters/jwt_authn_filter.rst index e84a23a31400..f8dd7e3a66cb 100644 --- a/docs/root/configuration/http/http_filters/jwt_authn_filter.rst +++ b/docs/root/configuration/http/http_filters/jwt_authn_filter.rst @@ -274,10 +274,13 @@ The field :ref:`claim_to_headers x-jwt-claim-nested-key: + x-jwt-tenants: diff --git a/source/extensions/filters/http/jwt_authn/authenticator.cc b/source/extensions/filters/http/jwt_authn/authenticator.cc index d302e066f382..cb380b905eb0 100644 --- a/source/extensions/filters/http/jwt_authn/authenticator.cc +++ b/source/extensions/filters/http/jwt_authn/authenticator.cc @@ -306,21 +306,37 @@ void AuthenticatorImpl::addJWTClaimToHeader(const std::string& claim_name, const auto status = payload_getter.GetValue(claim_name, claim_value); std::string str_claim_value; if (status == StructUtils::OK) { - 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: { + 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: + ENVOY_LOG(debug, "[jwt_auth] claim : {} is of an unknown type '{}'", claim_name, + claim_value->kind_case()); + 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); } } diff --git a/test/extensions/filters/http/jwt_authn/authenticator_test.cc b/test/extensions/filters/http/jwt_authn/authenticator_test.cc index 4ebe0f9a3feb..70b123c3807f 100644 --- a/test/extensions/filters/http/jwt_authn/authenticator_test.cc +++ b/test/extensions/filters/http/jwt_authn/authenticator_test.cc @@ -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" @@ -153,6 +154,13 @@ TEST_F(AuthenticatorTest, TestClaimToHeader) { EXPECT_EQ(headers.get_("x-jwt-claim-nested"), "value1"); 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 non-primitive type are + // 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())); } // This test verifies when wrong claim is passed in claim_to_headers diff --git a/test/extensions/filters/http/jwt_authn/test_common.h b/test/extensions/filters/http/jwt_authn/test_common.h index 95199667d03d..eb696a7a2509 100644 --- a/test/extensions/filters/http/jwt_authn/test_common.h +++ b/test/extensions/filters/http/jwt_authn/test_common.h @@ -97,6 +97,8 @@ 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-object-key" + claim_name: "nested.nested-2.key-5" rules: - match: path: "/"