Skip to content

Commit

Permalink
jwt_authn: Allow to extract keys from jwt struct claims (#30377)
Browse files Browse the repository at this point in the history
Added a new field `list_claim_keys` 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").

Signed-off-by: Martin Conte Mac Donell <reflejo@gmail.com>
  • Loading branch information
Reflejo authored Oct 24, 2023
1 parent ae0e6ce commit 42934fb
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 10 deletions.
4 changes: 4 additions & 0 deletions changelogs/current.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Original file line number Diff line number Diff line change
Expand Up @@ -274,10 +274,13 @@ The field :ref:`claim_to_headers <envoy_v3_api_field_extensions.filters.http.jwt
claim_name: sub
- header_name: x-jwt-claim-nested-key
claim_name: nested.claim.key
- header_name: x-jwt-tenants
claim_name: tenants
JWT claim ("sub" and "nested.claim.key") will be added to HTTP headers as following format:
In this example the `tenants` claim is an object, therefore the 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: <Base64 encoded JSON JWT Claim>
34 changes: 25 additions & 9 deletions source/extensions/filters/http/jwt_authn/authenticator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Expand Down
8 changes: 8 additions & 0 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 @@ -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
Expand Down
2 changes: 2 additions & 0 deletions test/extensions/filters/http/jwt_authn/test_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -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: "/"
Expand Down

0 comments on commit 42934fb

Please sign in to comment.