Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion api/envoy/extensions/filters/http/jwt_authn/v3/config.proto
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE;
// cache_duration:
// seconds: 300
//
// [#next-free-field: 10]
// [#next-free-field: 11]
message JwtProvider {
option (udpa.annotations.versioning).previous_message_type =
"envoy.config.filter.http.jwt_authn.v2alpha.JwtProvider";
Expand Down Expand Up @@ -191,6 +191,10 @@ message JwtProvider {
// exp: 1501281058
//
string payload_in_metadata = 9;

// Specify the clock skew in seconds when verifying JWT time constraint,
// such as `exp`, and `nbf`. If not specified, default is 60 seconds.
uint32 clock_skew_seconds = 10;
}

// This message specifies how to fetch JWKS from remote and how to cache it.
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions bazel/repository_locations.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -408,13 +408,13 @@ REPOSITORY_LOCATIONS_SPEC = dict(
project_name = "jwt_verify_lib",
project_desc = "JWT verification library for C++",
project_url = "https://github.com/google/jwt_verify_lib",
version = "7276a339af8426724b744216f619c99152f8c141",
sha256 = "f1fde4f3ebb3b2d841332c7a02a4b50e0529a19709934c63bc6208d1bbe28fb1",
version = "28efec2e4df1072db0ed03597591360ec9f80aac",
sha256 = "7a5c35b7cbf633398503ae12cad8c2833e92b3a796eed68b6256d22d51ace5e1",
strip_prefix = "jwt_verify_lib-{version}",
urls = ["https://github.com/google/jwt_verify_lib/archive/{version}.tar.gz"],
use_category = ["dataplane_ext"],
extensions = ["envoy.filters.http.jwt_authn"],
release_date = "2020-07-10",
release_date = "2020-11-04",
cpe = "N/A",
),
com_github_nodejs_http_parser = dict(
Expand Down
1 change: 1 addition & 0 deletions docs/root/version_history/current.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Minor Behavior Changes
* ext_authz filter: disable `envoy.reloadable_features.ext_authz_measure_timeout_on_check_created` by default.
* ext_authz filter: the deprecated field :ref:`use_alpha <envoy_api_field_config.filter.http.ext_authz.v2.ExtAuthz.use_alpha>` is no longer supported and cannot be set anymore.
* grpc_web filter: if a `grpc-accept-encoding` header is present it's passed as-is to the upstream and if it isn't `grpc-accept-encoding:identity` is sent instead. The header was always overwriten with `grpc-accept-encoding:identity,deflate,gzip` before.
* jwt_authn filter: added support of Jwt time constraint verification with a clock skew (default to 60 seconds) and added a filter config field :ref:`clock_skew_seconds <envoy_v3_api_field_extensions.filters.http.jwt_authn.v3.JwtProvider.clock_skew_seconds>` to configure it.
* memory: enable new tcmalloc with restartable sequences for aarch64 builds.
* tls: removed RSA key transport and SHA-1 cipher suites from the client-side defaults.
* watchdog: the watchdog action :ref:`abort_action <envoy_v3_api_msg_watchdog.v3alpha.AbortActionConfig>` is now the default action to terminate the process if watchdog kill / multikill is enabled.
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

37 changes: 14 additions & 23 deletions source/extensions/filters/http/jwt_authn/authenticator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ void AuthenticatorImpl::startVerify() {

jwt_ = std::make_unique<::google::jwt_verify::Jwt>();
ENVOY_LOG(debug, "{}: Parse Jwt {}", name(), curr_token_->token());
const Status status = jwt_->parseFromString(curr_token_->token());
Status status = jwt_->parseFromString(curr_token_->token());
if (status != Status::Ok) {
doneWithStatus(status);
return;
Expand All @@ -163,33 +163,23 @@ void AuthenticatorImpl::startVerify() {
}
}

// TODO(qiwzhang): Cross-platform-wise the below unix_timestamp code is wrong as the
// epoch is not guaranteed to be defined as the unix epoch. We should use
// the abseil time functionality instead or use the jwt_verify_lib to check
// the validity of a JWT.
// Check "exp" claim.
const uint64_t unix_timestamp =
std::chrono::duration_cast<std::chrono::seconds>(timeSource().systemTime().time_since_epoch())
.count();
// If the nbf claim does *not* appear in the JWT, then the nbf field is defaulted
// to 0.
if (jwt_->nbf_ > unix_timestamp) {
doneWithStatus(Status::JwtNotYetValid);
return;
}
// If the exp claim does *not* appear in the JWT then the exp field is defaulted
// to 0.
if (jwt_->exp_ > 0 && jwt_->exp_ < unix_timestamp) {
doneWithStatus(Status::JwtExpired);
return;
}

// Check the issuer is configured or not.
jwks_data_ = provider_ ? jwks_cache_.findByProvider(provider_.value())
: jwks_cache_.findByIssuer(jwt_->iss_);
// isIssuerSpecified() check already make sure the issuer is in the cache.
ASSERT(jwks_data_ != nullptr);

// Default is 60 seconds
uint64_t clock_skew_seconds = ::google::jwt_verify::kClockSkewInSecond;
if (jwks_data_->getJwtProvider().clock_skew_seconds() > 0) {
clock_skew_seconds = jwks_data_->getJwtProvider().clock_skew_seconds();
}
status = jwt_->verifyTimeConstraint(absl::ToUnixSeconds(absl::Now()), clock_skew_seconds);
if (status != Status::Ok) {
doneWithStatus(status);
return;
}

// Check if audience is allowed
bool is_allowed = check_audience_ ? check_audience_->areAudiencesAllowed(jwt_->audiences_)
: jwks_data_->areAudiencesAllowed(jwt_->audiences_);
Expand Down Expand Up @@ -247,7 +237,8 @@ void AuthenticatorImpl::onDestroy() {

// Verify with a specific public key.
void AuthenticatorImpl::verifyKey() {
const Status status = ::google::jwt_verify::verifyJwt(*jwt_, *jwks_data_->getJwksObj());
const Status status =
::google::jwt_verify::verifyJwtWithoutTimeChecking(*jwt_, *jwks_data_->getJwksObj());
if (status != Status::Ok) {
doneWithStatus(status);
return;
Expand Down
17 changes: 17 additions & 0 deletions test/extensions/filters/http/jwt_authn/authenticator_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,23 @@ TEST_F(AuthenticatorTest, TestExpiredJWT) {
expectVerifyStatus(Status::JwtExpired, headers);
}

// This test verifies when a JWT is expired but with a big clock skew.
TEST_F(AuthenticatorTest, TestExpiredJWTWithABigClockSkew) {
auto& provider = (*proto_config_.mutable_providers())[std::string(ProviderName)];
// Token is expired at 1205005587, but add clock skew at another 1205005587.
provider.set_clock_skew_seconds(1205005587);
createAuthenticator();

EXPECT_CALL(*raw_fetcher_, fetch(_, _, _))
.WillOnce(Invoke([this](const envoy::config::core::v3::HttpUri&, Tracing::Span&,
JwksFetcher::JwksReceiver& receiver) {
receiver.onJwksSuccess(std::move(jwks_));
}));

Http::TestRequestHeaderMapImpl headers{{"Authorization", "Bearer " + std::string(ExpiredToken)}};
expectVerifyStatus(Status::Ok, headers);
}

// This test verifies when a JWT is not yet valid, JwtNotYetValid status is returned.
TEST_F(AuthenticatorTest, TestNotYetValidJWT) {
EXPECT_CALL(*raw_fetcher_, fetch(_, _, _)).Times(0);
Expand Down