blob: 57e0e93e656fdeb9a7cad30efca04dfc018daeb4 [file] [log] [blame]
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////
#include "tink/jwt/verified_jwt.h"
#include "gtest/gtest.h"
#include "absl/strings/escaping.h"
#include "tink/jwt/internal/jwt_mac_impl.h"
#include "tink/jwt/jwt_mac.h"
#include "tink/jwt/jwt_validator.h"
#include "tink/jwt/raw_jwt.h"
#include "tink/subtle/hmac_boringssl.h"
#include "tink/util/constants.h"
#include "tink/util/enums.h"
#include "tink/util/errors.h"
#include "tink/util/protobuf_helper.h"
#include "tink/util/secret_data.h"
#include "tink/util/test_matchers.h"
#include "tink/util/test_util.h"
using ::crypto::tink::test::IsOk;
using ::crypto::tink::test::IsOkAndHolds;
namespace crypto {
namespace tink {
namespace {
util::StatusOr<VerifiedJwt> CreateVerifiedJwt(const RawJwt& raw_jwt) {
// Creating a VerifiedJwt is a bit complicated since it can only be created
// JWT primitives.
std::string key_value;
if (!absl::WebSafeBase64Unescape(
"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1"
"qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow",
&key_value)) {
return util::Status(util::error::INVALID_ARGUMENT, "failed to parse key");
}
crypto::tink::util::StatusOr<std::unique_ptr<Mac>> mac_or =
subtle::HmacBoringSsl::New(
util::Enums::ProtoToSubtle(google::crypto::tink::HashType::SHA256),
32, util::SecretDataFromStringView(key_value));
if (!mac_or.ok()) {
return mac_or.status();
}
std::unique_ptr<JwtMac> jwt_mac = absl::make_unique<jwt_internal::JwtMacImpl>(
std::move(mac_or.ValueOrDie()), "HS256");
auto compact_or = jwt_mac->ComputeMacAndEncode(raw_jwt);
if (!compact_or.ok()) {
return compact_or.status();
}
auto validator_builder = JwtValidatorBuilder();
auto issued_at_or = raw_jwt.GetIssuedAt();
if (issued_at_or.ok()) {
validator_builder.SetFixedNow(issued_at_or.ValueOrDie());
}
auto audience_or = raw_jwt.GetAudiences();
if (audience_or.ok()) {
validator_builder.SetAudience(audience_or.ValueOrDie()[0]);
}
return jwt_mac->VerifyMacAndDecode(compact_or.ValueOrDie(),
validator_builder.Build());
}
TEST(VerifiedJwt, GetTypeIssuerSubjectJwtIdOK) {
auto raw_jwt_or = RawJwtBuilder()
.SetTypeHeader("typeHeader")
.SetIssuer("issuer")
.SetSubject("subject")
.SetJwtId("jwt_id")
.Build();
ASSERT_THAT(raw_jwt_or.status(), IsOk());
auto verified_jwt_or = CreateVerifiedJwt(raw_jwt_or.ValueOrDie());
ASSERT_THAT(verified_jwt_or.status(), IsOk());
VerifiedJwt jwt = verified_jwt_or.ValueOrDie();
EXPECT_TRUE(jwt.HasTypeHeader());
EXPECT_THAT(jwt.GetTypeHeader(), IsOkAndHolds("typeHeader"));
EXPECT_TRUE(jwt.HasIssuer());
EXPECT_THAT(jwt.GetIssuer(), IsOkAndHolds("issuer"));
EXPECT_TRUE(jwt.HasSubject());
EXPECT_THAT(jwt.GetSubject(), IsOkAndHolds("subject"));
EXPECT_TRUE(jwt.HasJwtId());
EXPECT_THAT(jwt.GetJwtId(), IsOkAndHolds("jwt_id"));
}
TEST(VerifiedJwt, TimestampsOK) {
absl::Time now = absl::Now();
auto builder = RawJwtBuilder().SetIssuer("issuer");
ASSERT_THAT(builder.SetNotBefore(now - absl::Seconds(300)), IsOk());
ASSERT_THAT(builder.SetIssuedAt(now), IsOk());
ASSERT_THAT(builder.SetExpiration(now + absl::Seconds(300)), IsOk());
auto raw_jwt_or = builder.Build();
ASSERT_THAT(raw_jwt_or.status(), IsOk());
auto verified_jwt_or = CreateVerifiedJwt(raw_jwt_or.ValueOrDie());
ASSERT_THAT(verified_jwt_or.status(), IsOk());
VerifiedJwt jwt = verified_jwt_or.ValueOrDie();
EXPECT_TRUE(jwt.HasNotBefore());
auto nbf_or = jwt.GetNotBefore();
ASSERT_THAT(nbf_or.status(), IsOk());
auto nbf = nbf_or.ValueOrDie();
EXPECT_LT(nbf, now - absl::Seconds(299));
EXPECT_GT(nbf, now - absl::Seconds(301));
EXPECT_TRUE(jwt.HasIssuedAt());
auto iat_or = jwt.GetIssuedAt();
ASSERT_THAT(iat_or.status(), IsOk());
auto iat = iat_or.ValueOrDie();
EXPECT_LT(iat, now + absl::Seconds(1));
EXPECT_GT(iat, now - absl::Seconds(1));
EXPECT_TRUE(jwt.HasExpiration());
auto exp_or = jwt.GetExpiration();
ASSERT_THAT(exp_or.status(), IsOk());
auto exp = exp_or.ValueOrDie();
EXPECT_LT(exp, now + absl::Seconds(301));
EXPECT_GT(exp, now + absl::Seconds(299));
}
TEST(VerifiedJwt, GetAudiencesOK) {
auto raw_jwt_or =
RawJwtBuilder().AddAudience("audience1").AddAudience("audience2").Build();
ASSERT_THAT(raw_jwt_or.status(), IsOk());
auto verified_jwt_or = CreateVerifiedJwt(raw_jwt_or.ValueOrDie());
ASSERT_THAT(verified_jwt_or.status(), IsOk());
VerifiedJwt jwt = verified_jwt_or.ValueOrDie();
std::vector<std::string> expected = {"audience1", "audience2"};
EXPECT_TRUE(jwt.HasAudiences());
EXPECT_THAT(jwt.GetAudiences(), IsOkAndHolds(expected));
}
TEST(VerifiedJwt, GetCustomClaimOK) {
auto builder = RawJwtBuilder();
ASSERT_THAT(builder.AddNullClaim("null_claim"), IsOk());
ASSERT_THAT(builder.AddBooleanClaim("boolean_claim", true), IsOk());
ASSERT_THAT(builder.AddNumberClaim("number_claim", 123.456), IsOk());
ASSERT_THAT(builder.AddStringClaim("string_claim", "a string"), IsOk());
ASSERT_THAT(
builder.AddJsonObjectClaim("object_claim", R"({ "number": 123.456})"),
IsOk());
ASSERT_THAT(
builder.AddJsonArrayClaim("array_claim", R"([1, "one", 1.2, true])"),
IsOk());
auto raw_jwt_or = builder.Build();
ASSERT_THAT(raw_jwt_or.status(), IsOk());
auto verified_jwt_or = CreateVerifiedJwt(raw_jwt_or.ValueOrDie());
ASSERT_THAT(verified_jwt_or.status(), IsOk());
VerifiedJwt jwt = verified_jwt_or.ValueOrDie();
EXPECT_TRUE(jwt.IsNullClaim("null_claim"));
EXPECT_TRUE(jwt.HasBooleanClaim("boolean_claim"));
EXPECT_THAT(jwt.GetBooleanClaim("boolean_claim"), IsOkAndHolds(true));
EXPECT_TRUE(jwt.HasNumberClaim("number_claim"));
EXPECT_THAT(jwt.GetNumberClaim("number_claim"), IsOkAndHolds(123.456));
EXPECT_TRUE(jwt.HasStringClaim("string_claim"));
EXPECT_THAT(jwt.GetStringClaim("string_claim"), IsOkAndHolds("a string"));
EXPECT_TRUE(jwt.HasJsonObjectClaim("object_claim"));
EXPECT_THAT(jwt.GetJsonObjectClaim("object_claim"),
IsOkAndHolds(R"({"number":123.456})"));
EXPECT_TRUE(jwt.HasJsonArrayClaim("array_claim"));
EXPECT_THAT(jwt.GetJsonArrayClaim("array_claim"),
IsOkAndHolds(R"([1,"one",1.2,true])"));
std::vector<std::string> expected_claim_names = {
"object_claim", "number_claim", "boolean_claim",
"array_claim", "null_claim", "string_claim"};
EXPECT_THAT(jwt.CustomClaimNames(),
testing::UnorderedElementsAreArray(expected_claim_names));
}
TEST(VerifiedJwt, HasCustomClaimIsFalseForWrongType) {
auto builder = RawJwtBuilder();
ASSERT_THAT(builder.AddNullClaim("null_claim"), IsOk());
ASSERT_THAT(builder.AddBooleanClaim("boolean_claim", true), IsOk());
ASSERT_THAT(builder.AddNumberClaim("number_claim", 123.456), IsOk());
ASSERT_THAT(builder.AddStringClaim("string_claim", "a string"), IsOk());
auto raw_jwt_or = builder.Build();
ASSERT_THAT(raw_jwt_or.status(), IsOk());
auto verified_jwt_or = CreateVerifiedJwt(raw_jwt_or.ValueOrDie());
ASSERT_THAT(verified_jwt_or.status(), IsOk());
VerifiedJwt jwt = verified_jwt_or.ValueOrDie();
EXPECT_FALSE(jwt.IsNullClaim("boolean_claim"));
EXPECT_FALSE(jwt.HasBooleanClaim("number_claim"));
EXPECT_FALSE(jwt.HasNumberClaim("string_claim"));
EXPECT_FALSE(jwt.HasStringClaim("null_claim"));
}
TEST(VerifiedJwt, HasAlwaysReturnsFalseForRegisteredClaims) {
absl::Time now = absl::Now();
auto builder = RawJwtBuilder()
.SetIssuer("issuer")
.SetSubject("subject")
.SetJwtId("jwt_id");
ASSERT_THAT(builder.SetNotBefore(now - absl::Seconds(300)), IsOk());
ASSERT_THAT(builder.SetIssuedAt(now), IsOk());
ASSERT_THAT(builder.SetExpiration(now + absl::Seconds(300)), IsOk());
auto raw_jwt_or = builder.Build();
ASSERT_THAT(raw_jwt_or.status(), IsOk());
auto verified_jwt_or = CreateVerifiedJwt(raw_jwt_or.ValueOrDie());
ASSERT_THAT(verified_jwt_or.status(), IsOk());
VerifiedJwt jwt = verified_jwt_or.ValueOrDie();
EXPECT_FALSE(jwt.HasStringClaim("iss"));
EXPECT_FALSE(jwt.HasStringClaim("sub"));
EXPECT_FALSE(jwt.HasStringClaim("jti"));
EXPECT_FALSE(jwt.HasNumberClaim("nbf"));
EXPECT_FALSE(jwt.HasNumberClaim("iat"));
EXPECT_FALSE(jwt.HasNumberClaim("exp"));
EXPECT_THAT(jwt.CustomClaimNames(), testing::IsEmpty());
}
TEST(VerifiedJwt, GetRegisteredCustomClaimNotOK) {
absl::Time now = absl::Now();
auto builder =
RawJwtBuilder().SetIssuer("issuer").SetSubject("subject").SetJwtId(
"jwt_id");
ASSERT_THAT(builder.SetNotBefore(now - absl::Seconds(300)), IsOk());
ASSERT_THAT(builder.SetIssuedAt(now), IsOk());
ASSERT_THAT(builder.SetExpiration(now + absl::Seconds(300)), IsOk());
auto raw_jwt_or = builder.Build();
ASSERT_THAT(raw_jwt_or.status(), IsOk());
auto verified_jwt_or = CreateVerifiedJwt(raw_jwt_or.ValueOrDie());
ASSERT_THAT(verified_jwt_or.status(), IsOk());
VerifiedJwt jwt = verified_jwt_or.ValueOrDie();
EXPECT_FALSE(jwt.GetStringClaim("iss").ok());
EXPECT_FALSE(jwt.GetStringClaim("sub").ok());
EXPECT_FALSE(jwt.GetStringClaim("jti").ok());
EXPECT_FALSE(jwt.GetNumberClaim("nbf").ok());
EXPECT_FALSE(jwt.GetNumberClaim("iat").ok());
EXPECT_FALSE(jwt.GetNumberClaim("exp").ok());
}
TEST(VerifiedJwt, EmptyTokenHasAndIsReturnsFalse) {
auto raw_jwt_or = RawJwtBuilder().Build();
ASSERT_THAT(raw_jwt_or.status(), IsOk());
auto verified_jwt_or = CreateVerifiedJwt(raw_jwt_or.ValueOrDie());
ASSERT_THAT(verified_jwt_or.status(), IsOk());
VerifiedJwt jwt = verified_jwt_or.ValueOrDie();
EXPECT_FALSE(jwt.HasTypeHeader());
EXPECT_FALSE(jwt.HasIssuer());
EXPECT_FALSE(jwt.HasSubject());
EXPECT_FALSE(jwt.HasAudiences());
EXPECT_FALSE(jwt.HasJwtId());
EXPECT_FALSE(jwt.HasExpiration());
EXPECT_FALSE(jwt.HasNotBefore());
EXPECT_FALSE(jwt.HasIssuedAt());
EXPECT_FALSE(jwt.IsNullClaim("null_claim"));
EXPECT_FALSE(jwt.HasBooleanClaim("boolean_claim"));
EXPECT_FALSE(jwt.HasNumberClaim("number_claim"));
EXPECT_FALSE(jwt.HasStringClaim("string_claim"));
EXPECT_FALSE(jwt.HasJsonObjectClaim("object_claim"));
EXPECT_FALSE(jwt.HasJsonArrayClaim("array_claim"));
}
TEST(VerifiedJwt, EmptyTokenGetReturnsNotOK) {
auto raw_jwt_or = RawJwtBuilder().Build();
ASSERT_THAT(raw_jwt_or.status(), IsOk());
auto verified_jwt_or = CreateVerifiedJwt(raw_jwt_or.ValueOrDie());
ASSERT_THAT(verified_jwt_or.status(), IsOk());
VerifiedJwt jwt = verified_jwt_or.ValueOrDie();
EXPECT_FALSE(jwt.GetTypeHeader().ok());
EXPECT_FALSE(jwt.GetIssuer().ok());
EXPECT_FALSE(jwt.GetSubject().ok());
EXPECT_FALSE(jwt.GetAudiences().ok());
EXPECT_FALSE(jwt.GetJwtId().ok());
EXPECT_FALSE(jwt.GetExpiration().ok());
EXPECT_FALSE(jwt.GetNotBefore().ok());
EXPECT_FALSE(jwt.GetIssuedAt().ok());
EXPECT_FALSE(jwt.IsNullClaim("null_claim"));
EXPECT_FALSE(jwt.GetBooleanClaim("boolean_claim").ok());
EXPECT_FALSE(jwt.GetNumberClaim("number_claim").ok());
EXPECT_FALSE(jwt.GetStringClaim("string_claim").ok());
EXPECT_FALSE(jwt.GetJsonObjectClaim("object_claim").ok());
EXPECT_FALSE(jwt.GetJsonArrayClaim("array_claim").ok());
}
TEST(VerifiedJwt, GetJsonPayload) {
auto raw_jwt_or = RawJwtBuilder().SetIssuer("issuer").Build();
ASSERT_THAT(raw_jwt_or.status(), IsOk());
auto verified_jwt_or = CreateVerifiedJwt(raw_jwt_or.ValueOrDie());
ASSERT_THAT(verified_jwt_or.status(), IsOk());
VerifiedJwt jwt = verified_jwt_or.ValueOrDie();
EXPECT_THAT(jwt.GetJsonPayload(), IsOkAndHolds(R"({"iss":"issuer"})"));
}
TEST(VerifiedJwt, MoveMakesCopy) {
auto raw_jwt_or = RawJwtBuilder().SetIssuer("issuer").Build();
ASSERT_THAT(raw_jwt_or.status(), IsOk());
auto verified_jwt_or = CreateVerifiedJwt(raw_jwt_or.ValueOrDie());
ASSERT_THAT(verified_jwt_or.status(), IsOk());
VerifiedJwt jwt = verified_jwt_or.ValueOrDie();
VerifiedJwt jwt2 = std::move(jwt);
EXPECT_TRUE(jwt.HasIssuer());
EXPECT_THAT(jwt.GetIssuer(), IsOkAndHolds("issuer"));
EXPECT_TRUE(jwt2.HasIssuer());
EXPECT_THAT(jwt2.GetIssuer(), IsOkAndHolds("issuer"));
}
} // namespace
} // namespace tink
} // namespace crypto