mirror of
https://github.com/arun11299/cpp-jwt.git
synced 2025-05-14 16:58:34 +00:00
Added checks and tests for iat/jti/sub
This commit is contained in:
parent
babdd4f0a6
commit
7ddf7ec3e9
9 changed files with 284 additions and 3 deletions
|
@ -79,9 +79,16 @@ enum class VerificationErrc
|
||||||
InvalidAlgorithm = 1,
|
InvalidAlgorithm = 1,
|
||||||
TokenExpired,
|
TokenExpired,
|
||||||
InvalidIssuer,
|
InvalidIssuer,
|
||||||
|
InvalidSubject,
|
||||||
|
InvalidIAT,
|
||||||
|
//Checks for the existence of JTI
|
||||||
|
//if validate_jti is passed in decode
|
||||||
|
InvalidJTI,
|
||||||
InvalidAudience,
|
InvalidAudience,
|
||||||
ImmatureSignature,
|
ImmatureSignature,
|
||||||
InvalidSignature,
|
InvalidSignature,
|
||||||
|
// Invalid value type used for known claims
|
||||||
|
TypeConversionError,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -194,6 +194,54 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Derived from VerificationError.
|
||||||
|
* Thrown when the subject claim does not match
|
||||||
|
* with the one provided as part of decode argument.
|
||||||
|
*/
|
||||||
|
class InvalidSubjectError final: public VerificationError
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
InvalidSubjectError(std::string msg)
|
||||||
|
: VerificationError(std::move(msg))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Derived from VerificationError.
|
||||||
|
* Thrown when verify_iat parameter is passed to
|
||||||
|
* decode and IAT is not present.
|
||||||
|
*/
|
||||||
|
class InvalidIATError final: public VerificationError
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
InvalidIATError(std::string msg)
|
||||||
|
: VerificationError(std::move(msg))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Derived from VerificationError.
|
||||||
|
* Thrown when validate_jti is asked for
|
||||||
|
* in decode and jti claim is not present.
|
||||||
|
*/
|
||||||
|
class InvalidJTIError final: public VerificationError
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
InvalidJTIError(std::string msg)
|
||||||
|
: VerificationError(std::move(msg))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Derived from VerificationError.
|
* Derived from VerificationError.
|
||||||
* Thrown when the token is decoded at a time before
|
* Thrown when the token is decoded at a time before
|
||||||
|
@ -225,6 +273,22 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Derived from VerificationError.
|
||||||
|
* Thrown when there type expectation mismatch
|
||||||
|
* while verifying the values of registered claim names.
|
||||||
|
*/
|
||||||
|
class TypeConversionError final: public VerificationError
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
TypeConversionError(std::string msg)
|
||||||
|
: VerificationError(std::move(msg))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
} // END namespace jwt
|
} // END namespace jwt
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -110,12 +110,20 @@ struct VerificationErrorCategory: std::error_category
|
||||||
return "token expired";
|
return "token expired";
|
||||||
case VerificationErrc::InvalidIssuer:
|
case VerificationErrc::InvalidIssuer:
|
||||||
return "invalid issuer";
|
return "invalid issuer";
|
||||||
|
case VerificationErrc::InvalidSubject:
|
||||||
|
return "invalid subject";
|
||||||
case VerificationErrc::InvalidAudience:
|
case VerificationErrc::InvalidAudience:
|
||||||
return "invalid audience";
|
return "invalid audience";
|
||||||
|
case VerificationErrc::InvalidIAT:
|
||||||
|
return "invalid iat";
|
||||||
|
case VerificationErrc::InvalidJTI:
|
||||||
|
return "invalid jti";
|
||||||
case VerificationErrc::ImmatureSignature:
|
case VerificationErrc::ImmatureSignature:
|
||||||
return "immature signature";
|
return "immature signature";
|
||||||
case VerificationErrc::InvalidSignature:
|
case VerificationErrc::InvalidSignature:
|
||||||
return "invalid signature";
|
return "invalid signature";
|
||||||
|
case VerificationErrc::TypeConversionError:
|
||||||
|
return "type conversion error";
|
||||||
};
|
};
|
||||||
|
|
||||||
assert (0 && "Code not reached");
|
assert (0 && "Code not reached");
|
||||||
|
|
|
@ -455,6 +455,23 @@ std::error_code jwt_object::verify(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Check the subject
|
||||||
|
if (dparams.has_sub)
|
||||||
|
{
|
||||||
|
if (has_claim(registered_claims::subject))
|
||||||
|
{
|
||||||
|
jwt::string_view p_sub = payload()
|
||||||
|
.get_claim_value<std::string>(registered_claims::subject);
|
||||||
|
if (p_sub.data() != dparams.sub) {
|
||||||
|
ec = VerificationErrc::InvalidSubject;
|
||||||
|
return ec;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ec = VerificationErrc::InvalidSubject;
|
||||||
|
return ec;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//Check for NBF
|
//Check for NBF
|
||||||
if (has_claim(registered_claims::not_before))
|
if (has_claim(registered_claims::not_before))
|
||||||
{
|
{
|
||||||
|
@ -471,6 +488,27 @@ std::error_code jwt_object::verify(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Check IAT validation
|
||||||
|
if (dparams.validate_iat) {
|
||||||
|
if (!has_claim(registered_claims::issued_at)) {
|
||||||
|
ec = VerificationErrc::InvalidIAT;
|
||||||
|
return ec;
|
||||||
|
} else {
|
||||||
|
// Will throw type conversion error
|
||||||
|
auto val = payload()
|
||||||
|
.get_claim_value<uint64_t>(registered_claims::issued_at);
|
||||||
|
(void)val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Check JTI validation
|
||||||
|
if (dparams.validate_jti) {
|
||||||
|
if (!has_claim("jti")) {
|
||||||
|
ec = VerificationErrc::InvalidJTI;
|
||||||
|
return ec;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return ec;
|
return ec;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -534,6 +572,28 @@ void jwt_object::set_decode_params(DecodeParams& dparams, params::detail::audien
|
||||||
jwt_object::set_decode_params(dparams, std::forward<Rest>(args)...);
|
jwt_object::set_decode_params(dparams, std::forward<Rest>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename DecodeParams, typename... Rest>
|
||||||
|
void jwt_object::set_decode_params(DecodeParams& dparams, params::detail::subject_param s, Rest&&... args)
|
||||||
|
{
|
||||||
|
dparams.sub = std::move(s).get();
|
||||||
|
dparams.has_sub = true;
|
||||||
|
jwt_object::set_decode_params(dparams, std::forward<Rest>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename DecodeParams, typename... Rest>
|
||||||
|
void jwt_object::set_decode_params(DecodeParams& dparams, params::detail::validate_iat_param v, Rest&&... args)
|
||||||
|
{
|
||||||
|
dparams.validate_iat = v.get();
|
||||||
|
jwt_object::set_decode_params(dparams, std::forward<Rest>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename DecodeParams, typename... Rest>
|
||||||
|
void jwt_object::set_decode_params(DecodeParams& dparams, params::detail::validate_jti_param v, Rest&&... args)
|
||||||
|
{
|
||||||
|
dparams.validate_jti = v.get();
|
||||||
|
jwt_object::set_decode_params(dparams, std::forward<Rest>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename DecodeParams>
|
template <typename DecodeParams>
|
||||||
void jwt_object::set_decode_params(DecodeParams& dparams)
|
void jwt_object::set_decode_params(DecodeParams& dparams)
|
||||||
{
|
{
|
||||||
|
@ -577,6 +637,17 @@ jwt_object decode(const jwt::string_view enc_str,
|
||||||
//TODO: optional type
|
//TODO: optional type
|
||||||
bool has_aud = false;
|
bool has_aud = false;
|
||||||
std::string aud;
|
std::string aud;
|
||||||
|
|
||||||
|
//The subject
|
||||||
|
//TODO: optional type
|
||||||
|
bool has_sub = false;
|
||||||
|
std::string sub;
|
||||||
|
|
||||||
|
//Validate IAT
|
||||||
|
bool validate_iat = false;
|
||||||
|
|
||||||
|
//Validate JTI
|
||||||
|
bool validate_jti = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
decode_params dparams{};
|
decode_params dparams{};
|
||||||
|
@ -624,7 +695,11 @@ jwt_object decode(const jwt::string_view enc_str,
|
||||||
obj.payload(std::move(payload));
|
obj.payload(std::move(payload));
|
||||||
|
|
||||||
if (dparams.verify) {
|
if (dparams.verify) {
|
||||||
ec = obj.verify(dparams, algos);
|
try {
|
||||||
|
ec = obj.verify(dparams, algos);
|
||||||
|
} catch (const json_ns::detail::type_error& e) {
|
||||||
|
ec = VerificationErrc::TypeConversionError;
|
||||||
|
}
|
||||||
|
|
||||||
if (ec) return obj;
|
if (ec) return obj;
|
||||||
}
|
}
|
||||||
|
@ -705,6 +780,18 @@ void jwt_throw_exception(const std::error_code& ec)
|
||||||
{
|
{
|
||||||
throw InvalidAudienceError(ec.message());
|
throw InvalidAudienceError(ec.message());
|
||||||
}
|
}
|
||||||
|
case VerificationErrc::InvalidSubject:
|
||||||
|
{
|
||||||
|
throw InvalidSubjectError(ec.message());
|
||||||
|
}
|
||||||
|
case VerificationErrc::InvalidIAT:
|
||||||
|
{
|
||||||
|
throw InvalidIATError(ec.message());
|
||||||
|
}
|
||||||
|
case VerificationErrc::InvalidJTI:
|
||||||
|
{
|
||||||
|
throw InvalidJTIError(ec.message());
|
||||||
|
}
|
||||||
case VerificationErrc::ImmatureSignature:
|
case VerificationErrc::ImmatureSignature:
|
||||||
{
|
{
|
||||||
throw ImmatureSignatureError(ec.message());
|
throw ImmatureSignatureError(ec.message());
|
||||||
|
@ -713,6 +800,10 @@ void jwt_throw_exception(const std::error_code& ec)
|
||||||
{
|
{
|
||||||
throw InvalidSignatureError(ec.message());
|
throw InvalidSignatureError(ec.message());
|
||||||
}
|
}
|
||||||
|
case VerificationErrc::TypeConversionError:
|
||||||
|
{
|
||||||
|
throw TypeConversionError(ec.message());
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
assert (0 && "Unknown error code");
|
assert (0 && "Unknown error code");
|
||||||
};
|
};
|
||||||
|
|
|
@ -41,6 +41,7 @@ SOFTWARE.
|
||||||
// For convenience
|
// For convenience
|
||||||
using json_t = nlohmann::json;
|
using json_t = nlohmann::json;
|
||||||
using system_time_t = std::chrono::time_point<std::chrono::system_clock>;
|
using system_time_t = std::chrono::time_point<std::chrono::system_clock>;
|
||||||
|
namespace json_ns = nlohmann;
|
||||||
|
|
||||||
namespace jwt {
|
namespace jwt {
|
||||||
|
|
||||||
|
@ -1024,6 +1025,15 @@ public: //TODO: Not good
|
||||||
template <typename DecodeParams, typename... Rest>
|
template <typename DecodeParams, typename... Rest>
|
||||||
static void set_decode_params(DecodeParams& dparams, params::detail::audience_param a, Rest&&... args);
|
static void set_decode_params(DecodeParams& dparams, params::detail::audience_param a, Rest&&... args);
|
||||||
|
|
||||||
|
template <typename DecodeParams, typename... Rest>
|
||||||
|
static void set_decode_params(DecodeParams& dparams, params::detail::subject_param a, Rest&&... args);
|
||||||
|
|
||||||
|
template <typename DecodeParams, typename... Rest>
|
||||||
|
static void set_decode_params(DecodeParams& dparams, params::detail::validate_iat_param v, Rest&&... args);
|
||||||
|
|
||||||
|
template <typename DecodeParams, typename... Rest>
|
||||||
|
static void set_decode_params(DecodeParams& dparams, params::detail::validate_jti_param v, Rest&&... args);
|
||||||
|
|
||||||
template <typename DecodeParams>
|
template <typename DecodeParams>
|
||||||
static void set_decode_params(DecodeParams& dparams);
|
static void set_decode_params(DecodeParams& dparams);
|
||||||
|
|
||||||
|
@ -1043,6 +1053,9 @@ private: // Data Members
|
||||||
* Decode the JWT signature to create the `jwt_object`.
|
* Decode the JWT signature to create the `jwt_object`.
|
||||||
* This version reports error back using `std::error_code`.
|
* This version reports error back using `std::error_code`.
|
||||||
*
|
*
|
||||||
|
* If any of the registered claims are found in wrong format
|
||||||
|
* then sets TypeConversion error in the error_code.
|
||||||
|
*
|
||||||
* NOTE: Memory allocation exceptions are not caught.
|
* NOTE: Memory allocation exceptions are not caught.
|
||||||
*
|
*
|
||||||
* Optional parameters that can be passed:
|
* Optional parameters that can be passed:
|
||||||
|
@ -1062,6 +1075,14 @@ private: // Data Members
|
||||||
*
|
*
|
||||||
* 5. issuer: The issuer the client expects to be in the claim.
|
* 5. issuer: The issuer the client expects to be in the claim.
|
||||||
* NOTE: It is case-sensitive
|
* NOTE: It is case-sensitive
|
||||||
|
*
|
||||||
|
* 6. sub: The subject the client expects to be in the claim.
|
||||||
|
*
|
||||||
|
* 7. validate_iat: Checks if IAT claim is present or not
|
||||||
|
* and the type is uint64_t or not. If claim is not present
|
||||||
|
* then set InvalidIAT error.
|
||||||
|
*
|
||||||
|
* 8. validate_jti: Checks if jti claim is present or not.
|
||||||
*/
|
*/
|
||||||
template <typename SequenceT, typename... Args>
|
template <typename SequenceT, typename... Args>
|
||||||
jwt_object decode(const jwt::string_view enc_str,
|
jwt_object decode(const jwt::string_view enc_str,
|
||||||
|
|
|
@ -198,6 +198,46 @@ struct issuer_param
|
||||||
std::string iss_;
|
std::string iss_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
struct subject_param
|
||||||
|
{
|
||||||
|
subject_param(std::string sub)
|
||||||
|
: sub_(std::move(sub))
|
||||||
|
{}
|
||||||
|
|
||||||
|
const std::string& get() const& noexcept { return sub_; }
|
||||||
|
std::string get() && noexcept { return sub_; }
|
||||||
|
|
||||||
|
std::string sub_;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
struct validate_iat_param
|
||||||
|
{
|
||||||
|
validate_iat_param(bool v)
|
||||||
|
: iat_(v)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool get() const noexcept { return iat_; }
|
||||||
|
|
||||||
|
bool iat_;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
struct validate_jti_param
|
||||||
|
{
|
||||||
|
validate_jti_param(bool v)
|
||||||
|
: jti_(v)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool get() const noexcept { return jti_; }
|
||||||
|
|
||||||
|
bool jti_;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
struct nbf_param
|
struct nbf_param
|
||||||
|
@ -336,7 +376,7 @@ algorithms(SequenceConcept&& sc)
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
detail::audience_param
|
detail::audience_param
|
||||||
aud(const string_view aud)
|
aud(const jwt::string_view aud)
|
||||||
{
|
{
|
||||||
return { aud.data() };
|
return { aud.data() };
|
||||||
}
|
}
|
||||||
|
@ -344,11 +384,35 @@ aud(const string_view aud)
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
detail::issuer_param
|
detail::issuer_param
|
||||||
issuer(const string_view iss)
|
issuer(const jwt::string_view iss)
|
||||||
{
|
{
|
||||||
return { iss.data() };
|
return { iss.data() };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
detail::subject_param
|
||||||
|
sub(const jwt::string_view subj)
|
||||||
|
{
|
||||||
|
return { subj.data() };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
detail::validate_iat_param
|
||||||
|
validate_iat(bool v)
|
||||||
|
{
|
||||||
|
return { v };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
detail::validate_jti_param
|
||||||
|
validate_jti(bool v)
|
||||||
|
{
|
||||||
|
return { v };
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
detail::nbf_param
|
detail::nbf_param
|
||||||
|
|
|
@ -167,6 +167,20 @@ TEST (DecodeVerify, InvalidAudienceTest)
|
||||||
EXPECT_EQ (ec.value(), static_cast<int>(jwt::VerificationErrc::InvalidAudience));
|
EXPECT_EQ (ec.value(), static_cast<int>(jwt::VerificationErrc::InvalidAudience));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST (DecodeVerify, InvalidIATTest)
|
||||||
|
{
|
||||||
|
using namespace jwt::params;
|
||||||
|
|
||||||
|
jwt::jwt_object obj{algorithm("hs256"), secret("secret"), payload({{"sub", "test"}, {"aud", "www"}})};
|
||||||
|
|
||||||
|
obj.add_claim("iat", "what?");
|
||||||
|
auto enc_str = obj.signature();
|
||||||
|
|
||||||
|
std::error_code ec;
|
||||||
|
auto dec_obj = jwt::decode(enc_str, algorithms({"hs256"}), ec, secret("secret"), validate_iat(true));
|
||||||
|
EXPECT_EQ (ec.value(), static_cast<int>(jwt::VerificationErrc::TypeConversionError));
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
::testing::InitGoogleTest(&argc, argv);
|
::testing::InitGoogleTest(&argc, argv);
|
||||||
return RUN_ALL_TESTS();
|
return RUN_ALL_TESTS();
|
||||||
|
|
|
@ -164,6 +164,18 @@ TEST (DecodeVerifyExp, KeyNotPresentTest)
|
||||||
jwt::KeyNotPresentError);
|
jwt::KeyNotPresentError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST (DecodeVerifyExp, InvalidSubjectTest)
|
||||||
|
{
|
||||||
|
using namespace jwt::params;
|
||||||
|
|
||||||
|
jwt::jwt_object obj{algorithm("hs256"), secret("secret"), payload({{"sub", "test"}, {"aud", "www"}})};
|
||||||
|
|
||||||
|
auto enc_str = obj.signature();
|
||||||
|
|
||||||
|
EXPECT_THROW (jwt::decode(enc_str, algorithms({"hs256"}), secret("secret"), sub("TEST")),
|
||||||
|
jwt::InvalidSubjectError);
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
::testing::InitGoogleTest(&argc, argv);
|
::testing::InitGoogleTest(&argc, argv);
|
||||||
return RUN_ALL_TESTS();
|
return RUN_ALL_TESTS();
|
||||||
|
|
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue