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,
|
||||
TokenExpired,
|
||||
InvalidIssuer,
|
||||
InvalidSubject,
|
||||
InvalidIAT,
|
||||
//Checks for the existence of JTI
|
||||
//if validate_jti is passed in decode
|
||||
InvalidJTI,
|
||||
InvalidAudience,
|
||||
ImmatureSignature,
|
||||
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.
|
||||
* 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
|
||||
|
||||
#endif
|
||||
|
|
|
@ -110,12 +110,20 @@ struct VerificationErrorCategory: std::error_category
|
|||
return "token expired";
|
||||
case VerificationErrc::InvalidIssuer:
|
||||
return "invalid issuer";
|
||||
case VerificationErrc::InvalidSubject:
|
||||
return "invalid subject";
|
||||
case VerificationErrc::InvalidAudience:
|
||||
return "invalid audience";
|
||||
case VerificationErrc::InvalidIAT:
|
||||
return "invalid iat";
|
||||
case VerificationErrc::InvalidJTI:
|
||||
return "invalid jti";
|
||||
case VerificationErrc::ImmatureSignature:
|
||||
return "immature signature";
|
||||
case VerificationErrc::InvalidSignature:
|
||||
return "invalid signature";
|
||||
case VerificationErrc::TypeConversionError:
|
||||
return "type conversion error";
|
||||
};
|
||||
|
||||
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
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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)...);
|
||||
}
|
||||
|
||||
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>
|
||||
void jwt_object::set_decode_params(DecodeParams& dparams)
|
||||
{
|
||||
|
@ -577,6 +637,17 @@ jwt_object decode(const jwt::string_view enc_str,
|
|||
//TODO: optional type
|
||||
bool has_aud = false;
|
||||
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{};
|
||||
|
@ -624,7 +695,11 @@ jwt_object decode(const jwt::string_view enc_str,
|
|||
obj.payload(std::move(payload));
|
||||
|
||||
if (dparams.verify) {
|
||||
try {
|
||||
ec = obj.verify(dparams, algos);
|
||||
} catch (const json_ns::detail::type_error& e) {
|
||||
ec = VerificationErrc::TypeConversionError;
|
||||
}
|
||||
|
||||
if (ec) return obj;
|
||||
}
|
||||
|
@ -705,6 +780,18 @@ void jwt_throw_exception(const std::error_code& ec)
|
|||
{
|
||||
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:
|
||||
{
|
||||
throw ImmatureSignatureError(ec.message());
|
||||
|
@ -713,6 +800,10 @@ void jwt_throw_exception(const std::error_code& ec)
|
|||
{
|
||||
throw InvalidSignatureError(ec.message());
|
||||
}
|
||||
case VerificationErrc::TypeConversionError:
|
||||
{
|
||||
throw TypeConversionError(ec.message());
|
||||
}
|
||||
default:
|
||||
assert (0 && "Unknown error code");
|
||||
};
|
||||
|
|
|
@ -41,6 +41,7 @@ SOFTWARE.
|
|||
// For convenience
|
||||
using json_t = nlohmann::json;
|
||||
using system_time_t = std::chrono::time_point<std::chrono::system_clock>;
|
||||
namespace json_ns = nlohmann;
|
||||
|
||||
namespace jwt {
|
||||
|
||||
|
@ -1024,6 +1025,15 @@ public: //TODO: Not good
|
|||
template <typename DecodeParams, typename... Rest>
|
||||
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>
|
||||
static void set_decode_params(DecodeParams& dparams);
|
||||
|
||||
|
@ -1043,6 +1053,9 @@ private: // Data Members
|
|||
* Decode the JWT signature to create the `jwt_object`.
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
* 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>
|
||||
jwt_object decode(const jwt::string_view enc_str,
|
||||
|
|
|
@ -198,6 +198,46 @@ struct issuer_param
|
|||
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
|
||||
|
@ -336,7 +376,7 @@ algorithms(SequenceConcept&& sc)
|
|||
/**
|
||||
*/
|
||||
detail::audience_param
|
||||
aud(const string_view aud)
|
||||
aud(const jwt::string_view aud)
|
||||
{
|
||||
return { aud.data() };
|
||||
}
|
||||
|
@ -344,11 +384,35 @@ aud(const string_view aud)
|
|||
/**
|
||||
*/
|
||||
detail::issuer_param
|
||||
issuer(const string_view iss)
|
||||
issuer(const jwt::string_view iss)
|
||||
{
|
||||
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
|
||||
|
|
|
@ -167,6 +167,20 @@ TEST (DecodeVerify, InvalidAudienceTest)
|
|||
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[]) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
|
|
|
@ -164,6 +164,18 @@ TEST (DecodeVerifyExp, KeyNotPresentTest)
|
|||
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[]) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
|
|
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue