Skip to content

Commit a3dd83f

Browse files
authored
Add client assertion restrictions, and code tidy up (#423)
* Add jwt client assertion restrictions and tests * Replace `system_clock` with `steady_clock` when calculating the response time * Tidy up
1 parent b831b5d commit a3dd83f

5 files changed

+91
-6
lines changed

Development/cmake/NmosCppTest.cmake

+1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ set(NMOS_CPP_TEST_NMOS_TEST_SOURCES
4747
nmos/test/did_sdid_test.cpp
4848
nmos/test/event_type_test.cpp
4949
nmos/test/json_validator_test.cpp
50+
nmos/test/jwt_generator_test.cpp
5051
nmos/test/jwt_validation_test.cpp
5152
nmos/test/paging_utils_test.cpp
5253
nmos/test/query_api_test.cpp

Development/nmos/authorization_behaviour.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,8 @@ namespace nmos
162162
{
163163
return false;
164164
}
165-
auto now = std::chrono::system_clock::now();
166-
auto exp = std::chrono::system_clock::from_time_t(expires_at);
165+
const auto now = std::chrono::system_clock::now();
166+
const auto exp = std::chrono::system_clock::from_time_t(expires_at);
167167
return (now > exp);
168168
};
169169

Development/nmos/client_utils.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -296,10 +296,10 @@ namespace nmos
296296
{
297297
slog::log<slog::severities::too_much_info>(gate, SLOG_FLF) << "Sending request";
298298
// see https://developer.mozilla.org/en-US/docs/Web/API/Resource_Timing_API#Resource_loading_timestamps
299-
const auto start_time = std::chrono::system_clock::now();
299+
const auto start_time = std::chrono::steady_clock::now();
300300
return client.request(request, token).then([start_time, &gate](web::http::http_response res)
301301
{
302-
const auto response_start = std::chrono::system_clock::now();
302+
const auto response_start = std::chrono::steady_clock::now();
303303
const auto request_dur = std::chrono::duration_cast<std::chrono::microseconds>(response_start - start_time).count() / 1000.0;
304304

305305
// see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Server-Timing

Development/nmos/jwt_generator_impl.cpp

+17-2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ namespace nmos
1717
static utility::string_t create_client_assertion(const utility::string_t& issuer, const utility::string_t& subject, const web::uri& audience, const std::chrono::seconds& token_lifetime, const utility::string_t& public_key, const utility::string_t& private_key, const utility::string_t& keyid)
1818
{
1919
using namespace jwt::traits;
20+
21+
const auto now = std::chrono::system_clock::now();
2022

2123
// use server private key to create client_assertion (JWT)
2224
// where client_assertion MUST including iss, sub, aud, exp, and may including jti
@@ -26,8 +28,8 @@ namespace nmos
2628
.set_issuer(utility::us2s(issuer))
2729
.set_subject(utility::us2s(subject))
2830
.set_audience(utility::us2s(audience.to_string()))
29-
.set_issued_at(std::chrono::system_clock::now())
30-
.set_expires_at(std::chrono::system_clock::now() + token_lifetime)
31+
.set_issued_at(now)
32+
.set_expires_at(now + token_lifetime)
3133
.set_id(utility::us2s(nmos::make_id()))
3234
.set_key_id(utility::us2s(keyid))
3335
.set_type("JWT")
@@ -36,6 +38,19 @@ namespace nmos
3638

3739
static utility::string_t create_client_assertion(const utility::string_t& issuer, const utility::string_t& subject, const web::uri& audience, const std::chrono::seconds& token_lifetime, const utility::string_t& private_key, const utility::string_t& keyid)
3840
{
41+
// see https://tools.ietf.org/html/rfc7523#section-3
42+
if (issuer.empty())
43+
{
44+
throw jwk_exception("empty issuer");
45+
}
46+
if (subject.empty())
47+
{
48+
throw jwk_exception("empty subject");
49+
}
50+
if (audience.is_empty())
51+
{
52+
throw jwk_exception("empty audience");
53+
}
3954
return create_client_assertion(issuer, subject, audience, token_lifetime, rsa_public_key(private_key), private_key, keyid);
4055
}
4156
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// The first "test" is of course whether the header compiles standalone
2+
#include "nmos/jwt_generator.h"
3+
4+
#include "bst/test/test.h"
5+
#include "cpprest/basic_utils.h"
6+
#include "nmos/jwk_utils.h" // for nmos::experimental::jwk_exception
7+
8+
namespace
9+
{
10+
// this is the private key (rsa.api.testsuite.nmos.tv.key.pem) from the nmos-testing
11+
// https://https://github.com/AMWA-TV/nmos-testing/blob/master/test_data/BCP00301/ca/intermediate/private/rsa.api.testsuite.nmos.tv.key.pem
12+
const auto test_private_key = utility::s2us(R"(-----BEGIN RSA PRIVATE KEY-----
13+
MIIEogIBAAKCAQEAyXHgphlqcINx+ZKkBefDo5X5rHUuTpom9OcRKpWQHt7oYUr1
14+
UhoKJ+8SxbsSvtlrvvGa6kiSk/m6i7haU9dGKSDJzndYJSi+Qbc2jfSPfoHtHvsy
15+
PIworhKniDA7YNE+olr23KGYSqdWidp3nzQLdaHuvOqjjjb3Jm2hvdt4Rfyk8r90
16+
5FY1kdZ/rINtvUDNHZnno9xPw9Hk+xc/cfOJyLUxBndy5wSp7Dhl8Wg1tLuK0rIG
17+
JuFBFrZWykGySGP8s3KzeSeugojYa4JWoXFix6+hlTOfyyu5VXtDkTIZotXcAOBl
18+
EEFLNtSko0yzWuSDo1HF0IwwCvgmwFnewdgFGQIDAQABAoIBAGnZ2ebNsh1/JHO0
19+
91VXDHk4BGL3jCanX9MOW/nZb0qZbNg68B99KVsEiAO4okgArVo/UFzNV6BD+B8U
20+
9vnZQ7e2z/QayAl2mEqlwBflq0UZdoTyD9q692FI0hmA5qKgMN5VGCSlEQYhWhrD
21+
3lmcmmzscyt3zAudnE7oCrZdzZxQD1r2dgjuLFiSaueUHPS/7FQavMx5sGGowcmN
22+
ex+nEHEBeRn5Ws7NWKtX6UoFa8btJIapsqkLHgtXgEsrbFDfLXbEgdFgXg36e8Ak
23+
lCuzUsehaM56eesVNyT/4FVN9ilJu0I1OlJs7sNXOIR4v//PqWoEnEGV/Fwb0YPT
24+
YWPr81ECgYEA7jIIQD9TKYdBdR2A5b8AmRAuVpYsC36Cw6n/3Mq/Le1Bf0nj1cVf
25+
JBkmLOYO/41h6Z+kITJfUZMniy6a/D7LXMSd/jwJM7WKLBfM/AIQHsSpIo/bkW/Q
26+
zVa2inDLYnICWAuR2KNWR46CWy6wnjlWdag76YYJxlk91vpy6RqUCtUCgYEA2ICb
27+
fBp5DNayESM7zDjh8PvgMn49tj6BjuO+oJ7vycQzDlgp/NV2kwtRpNQUcT4wAvTJ
28+
W/GHOlUTxIDxhZqJu3Ix5ue/R0YprhlZofSzwXVoqh+NvZGi4UYiJkTr7zXhzoU1
29+
PV5vWb1YMqpiYJxA3BRj3K0YdVmPkdcoLsXgKzUCgYAuBh7QAyxXbtn3/h5kxfYg
30+
nR7G/jc+dVBg7B0TFV3BSwGHzcgnCv7qI63bqQwm1rOfh4gYHfqK8YsHepbZvGxg
31+
3WDFueXxRteO0355BxEEUO15TyCWxmsq8eFNeKPjvrGzP3EL0eue4etQIQJhYCTT
32+
kREaexqyZ5XqTvQbFFacjQKBgEhC1KKda12/ovtZWTIWokL+rpvryskzH6cDmLKf
33+
mcUsOSZGgu0iiksV8hAjwRby/K9f6H1JpirwDoL9zp8bL3Fi8gjxvMQbRPoY9/O4
34+
au7dMyvlEDf/je/Gqss/IchboZx+lYCALoYzTmbKu78nJ/bMz2/uTkWMuQCiYYUL
35+
AoEpAoGAYIG2aCsuLV+1bPHC0vYvDC0V+NMA8e9HrplHdrQ4IBxyZnmHqvrGZ+04
36+
huUpDxAX+hxYap0k0RPJ3HaFmIHz7DpStX/aIjcfucEnoev7OLj4/3j6s2tHfeWL
37+
JP+1+v/YSIKc7WXvz95YsmoJZ02Ikv8zBan9HIzczmkqDe0C1RQ=
38+
-----END RSA PRIVATE KEY-----
39+
)");
40+
41+
}
42+
43+
BST_TEST_CASE(testClientAssertion)
44+
{
45+
using namespace nmos::experimental;
46+
47+
const utility::string_t issuer{ U("api.testsuite.nmos.tv") };
48+
const utility::string_t subject{ U("api.testsuite.nmos.tv") };
49+
const web::uri audience{ U("https://mocks.testsuite.nmos.tv:5010/testtoken") };
50+
const std::chrono::seconds token_lifetime{ 100 };
51+
const utility::string_t private_key{ test_private_key };
52+
const utility::string_t keyid{ U("key_1") };
53+
54+
// bad cases
55+
const utility::string_t bad_issuer{};
56+
BST_REQUIRE_THROW(jwt_generator::create_client_assertion(bad_issuer, subject, audience, token_lifetime, private_key, keyid), nmos::experimental::jwk_exception);
57+
58+
const utility::string_t bad_subject{};
59+
BST_REQUIRE_THROW(jwt_generator::create_client_assertion(issuer, bad_subject, audience, token_lifetime, private_key, keyid), nmos::experimental::jwk_exception);
60+
61+
const utility::string_t bad_audience{};
62+
BST_REQUIRE_THROW(jwt_generator::create_client_assertion(issuer, subject, bad_audience, token_lifetime, private_key, keyid), nmos::experimental::jwk_exception);
63+
64+
const utility::string_t bad_private_key{};
65+
BST_REQUIRE_THROW(jwt_generator::create_client_assertion(issuer, subject, audience, token_lifetime, bad_private_key, keyid), nmos::experimental::jwk_exception);
66+
67+
// good case
68+
BST_REQUIRE_NO_THROW(jwt_generator::create_client_assertion(issuer, subject, audience, token_lifetime, private_key, keyid));
69+
}

0 commit comments

Comments
 (0)