Added checks and tests for iat/jti/sub

This commit is contained in:
Arun M 2017-12-29 01:10:41 +05:30
parent babdd4f0a6
commit 7ddf7ec3e9
9 changed files with 284 additions and 3 deletions

View file

@ -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,
}; };
/** /**

View file

@ -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

View file

@ -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");

View file

@ -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) {
try {
ec = obj.verify(dparams, algos); 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");
}; };

View file

@ -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,

View file

@ -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

View file

@ -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();

View file

@ -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.