Skip to content

Commit 1c99217

Browse files
committed
HTCONDOR-1290: Update Scitokens-cpp to openssl 3.0
-Updated to be compatable with JWT-CPP 0.6.0 -Added changes for traits namespace -Added error_code for sign & verify -Replaced jwt::token_verification_exception() due to change from passing string to error_code. I find the error_code method to be limited and confusing. So, I replaced with Scitoken local JWTVerificationException() to use custom error messages
1 parent 8727a1a commit 1c99217

File tree

2 files changed

+60
-46
lines changed

2 files changed

+60
-46
lines changed

src/scitokens_internal.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -444,7 +444,7 @@ normalize_absolute_path(const std::string &path) {
444444

445445
void
446446
SciToken::deserialize(const std::string &data, const std::vector<std::string> allowed_issuers) {
447-
m_decoded.reset(new jwt::decoded_jwt(data));
447+
m_decoded.reset(new jwt::decoded_jwt<jwt::traits::kazuho_picojson>(data));
448448

449449
scitokens::Validator val;
450450
val.add_allowed_issuers(allowed_issuers);
@@ -683,7 +683,7 @@ scitokens::Validator::store_public_ec_key(const std::string &issuer, const std::
683683
bool
684684
scitokens::Enforcer::scope_validator(const jwt::claim &claim, void *myself) {
685685
auto me = reinterpret_cast<scitokens::Enforcer*>(myself);
686-
if (claim.get_type() != jwt::claim::type::string) {
686+
if (claim.get_type() != jwt::json::type::string) {
687687
return false;
688688
}
689689
std::string scope = claim.as_string();

src/scitokens_internal.h

Lines changed: 58 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@
66
#include <jwt-cpp/jwt.h>
77
#include <uuid/uuid.h>
88

9+
namespace jwt {
10+
template<typename json_traits>
11+
class decoded_jwt;
12+
namespace traits {
13+
struct kazuho_picojson;
14+
}
15+
}
16+
917
namespace scitokens {
1018

1119
class UnsupportedKeyException : public std::runtime_error {
@@ -15,6 +23,12 @@ class UnsupportedKeyException : public std::runtime_error {
1523
{}
1624
};
1725

26+
class JWTVerificationException : public std::runtime_error {
27+
public:
28+
explicit JWTVerificationException(const std::string &msg)
29+
: std::runtime_error(msg)
30+
{}
31+
};
1832

1933
class CurlException : public std::runtime_error {
2034
public:
@@ -47,7 +61,6 @@ class JsonException : public std::runtime_error {
4761
{}
4862
};
4963

50-
5164
class SciTokenKey {
5265

5366
public:
@@ -64,17 +77,18 @@ class SciTokenKey {
6477
{}
6578

6679
std::string
67-
serialize(jwt::builder &builder) {
80+
serialize(jwt::builder<jwt::traits::kazuho_picojson> &builder) {
81+
std::error_code ec;
6882
builder.set_key_id(m_kid);
6983
return builder.sign(*this);
7084
}
7185

7286
std::string
73-
sign(const std::string &data) const {
87+
sign(const std::string &data, std::error_code &ec) const {
7488
if (m_name == "RS256") {
75-
return jwt::algorithm::rs256(m_public, m_private).sign(data);
89+
return jwt::algorithm::rs256(m_public, m_private).sign(data, ec);
7690
} else if (m_name == "ES256") {
77-
return jwt::algorithm::es256(m_public, m_private).sign(data);
91+
return jwt::algorithm::es256(m_public, m_private).sign(data, ec);
7892
}
7993
throw UnsupportedKeyException("Provided algorithm name is not supported");
8094
}
@@ -85,13 +99,13 @@ class SciTokenKey {
8599
}
86100

87101
void
88-
verify(const std::string &data, const std::string &signature) const {
102+
verify(const std::string &data, const std::string &signature, std::error_code &ec) const {
89103
if (m_name == "RS256") {
90-
jwt::algorithm::rs256(m_public, m_private).verify(data, signature);
104+
jwt::algorithm::rs256(m_public, m_private).verify(data, signature, ec);
91105
} else if (m_name == "ES256") {
92-
jwt::algorithm::es256(m_public, m_private).verify(data, signature);
106+
jwt::algorithm::es256(m_public, m_private).verify(data, signature, ec);
93107
} else {
94-
throw jwt::signature_verification_exception("Provided algorithm is not supported.");
108+
throw UnsupportedKeyException("Provided algorithm is not supported.");
95109
}
96110
}
97111

@@ -190,7 +204,7 @@ friend class scitokens::Validator;
190204

191205
std::string
192206
serialize() {
193-
jwt::builder builder(jwt::create());
207+
jwt::builder<jwt::traits::kazuho_picojson> builder(jwt::create());
194208

195209
if (!m_issuer_set) {
196210
throw MissingIssuerException();
@@ -241,7 +255,7 @@ friend class scitokens::Validator;
241255
Profile m_serialize_profile{Profile::COMPAT};
242256
Profile m_deserialize_profile{Profile::COMPAT};
243257
std::unordered_map<std::string, jwt::claim> m_claims;
244-
std::unique_ptr<jwt::decoded_jwt> m_decoded;
258+
std::unique_ptr<jwt::decoded_jwt<jwt::traits::kazuho_picojson>> m_decoded;
245259
SciTokenKey &m_key;
246260
};
247261

@@ -254,14 +268,14 @@ class Validator {
254268

255269
public:
256270
void verify(const SciToken &scitoken) {
257-
const jwt::decoded_jwt *jwt_decoded = scitoken.m_decoded.get();
271+
const jwt::decoded_jwt<jwt::traits::kazuho_picojson> *jwt_decoded = scitoken.m_decoded.get();
258272
if (!jwt_decoded) {
259-
throw jwt::token_verification_exception("Token is not deserialized from string.");
273+
throw JWTVerificationException("Token is not deserialized from string.");
260274
}
261275
verify(*jwt_decoded);
262276
}
263277

264-
void verify(const jwt::decoded_jwt &jwt) {
278+
void verify(const jwt::decoded_jwt<jwt::traits::kazuho_picojson> &jwt) {
265279
// If token has a typ header claim (RFC8725 Section 3.11), trust that in COMPAT mode.
266280
if (jwt.has_type()) {
267281
std::string t_type = jwt.get_type();
@@ -271,28 +285,28 @@ class Validator {
271285
}
272286
} else if (m_validate_profile == SciToken::Profile::AT_JWT) {
273287
if (t_type != "at+jwt" && t_type != "application/at+jwt") {
274-
throw jwt::token_verification_exception("'typ' header claim must be at+jwt");
288+
throw JWTVerificationException("'typ' header claim must be at+jwt");
275289
}
276290
m_profile = SciToken::Profile::AT_JWT;
277291
}
278292
} else {
279293
if (m_validate_profile == SciToken::Profile::AT_JWT) {
280-
throw jwt::token_verification_exception("'typ' header claim must be set for at+jwt tokens");
294+
throw JWTVerificationException("'typ' header claim must be set for at+jwt tokens");
281295
}
282296
}
283297
if (!jwt.has_payload_claim("iat")) {
284-
throw jwt::token_verification_exception("'iat' claim is mandatory");
298+
throw JWTVerificationException("'iat' claim is mandatory");
285299
}
286300
if (m_profile != SciToken::Profile::AT_JWT) {
287301
if (!jwt.has_payload_claim("nbf")) {
288-
throw jwt::token_verification_exception("'nbf' claim is mandatory");
302+
throw JWTVerificationException("'nbf' claim is mandatory");
289303
}
290304
}
291305
if (!jwt.has_payload_claim("exp")) {
292-
throw jwt::token_verification_exception("'exp' claim is mandatory");
306+
throw JWTVerificationException("'exp' claim is mandatory");
293307
}
294308
if (!jwt.has_payload_claim("iss")) {
295-
throw jwt::token_verification_exception("'iss' claim is mandatory");
309+
throw JWTVerificationException("'iss' claim is mandatory");
296310
}
297311
if (!m_allowed_issuers.empty()) {
298312
std::string issuer = jwt.get_issuer();
@@ -304,15 +318,15 @@ class Validator {
304318
}
305319
}
306320
if (!permitted) {
307-
throw jwt::token_verification_exception("Token issuer is not in list of allowed issuers.");
321+
throw JWTVerificationException("Token issuer is not in list of allowed issuers.");
308322
}
309323
}
310324

311325
for (const auto &claim : m_critical_claims) {
312326
if (!jwt.has_payload_claim(claim)) {
313327
std::stringstream ss;
314328
ss << "'" << claim << "' claim is mandatory";
315-
throw jwt::token_verification_exception(ss.str());
329+
throw JWTVerificationException(ss.str());
316330
}
317331
}
318332

@@ -337,57 +351,57 @@ class Validator {
337351
bool must_verify_everything = true;
338352
if (jwt.has_payload_claim("ver")) {
339353
const jwt::claim &claim = jwt.get_payload_claim("ver");
340-
if (claim.get_type() != jwt::claim::type::string) {
341-
throw jwt::token_verification_exception("'ver' claim value must be a string (if present)");
354+
if (claim.get_type() != jwt::json::type::string) {
355+
throw JWTVerificationException("'ver' claim value must be a string (if present)");
342356
}
343357
std::string ver_string = claim.as_string();
344358
if ((ver_string == "scitokens:2.0") || (ver_string == "scitoken:2.0")) {
345359
must_verify_everything = false;
346360
if ((m_validate_profile != SciToken::Profile::COMPAT) &&
347361
(m_validate_profile != SciToken::Profile::SCITOKENS_2_0))
348362
{
349-
throw jwt::token_verification_exception("Invalidate token type; not expecting a SciToken 2.0.");
363+
throw JWTVerificationException("Invalidate token type; not expecting a SciToken 2.0.");
350364
}
351365
m_profile = SciToken::Profile::SCITOKENS_2_0;
352366
if (!jwt.has_payload_claim("aud")) {
353-
throw jwt::token_verification_exception("'aud' claim required for SciTokens 2.0 profile");
367+
throw JWTVerificationException("'aud' claim required for SciTokens 2.0 profile");
354368
}
355369
}
356370
else if (ver_string == "scitokens:1.0") {
357371
must_verify_everything = m_validate_all_claims;
358372
if ((m_validate_profile != SciToken::Profile::COMPAT) &&
359373
(m_validate_profile != SciToken::Profile::SCITOKENS_1_0))
360374
{
361-
throw jwt::token_verification_exception("Invalidate token type; not expecting a SciToken 1.0.");
375+
throw JWTVerificationException("Invalidate token type; not expecting a SciToken 1.0.");
362376
}
363377
m_profile = SciToken::Profile::SCITOKENS_1_0;
364378
} else {
365379
std::stringstream ss;
366380
ss << "Unknown profile version in token: " << ver_string;
367-
throw jwt::token_verification_exception(ss.str());
381+
throw JWTVerificationException(ss.str());
368382
}
369383
// Handle WLCG common JWT profile.
370384
} else if (jwt.has_payload_claim("wlcg.ver")) {
371385
if ((m_validate_profile != SciToken::Profile::COMPAT) &&
372386
(m_validate_profile != SciToken::Profile::WLCG_1_0))
373387
{
374-
throw jwt::token_verification_exception("Invalidate token type; not expecting a WLCG 1.0.");
388+
throw JWTVerificationException("Invalidate token type; not expecting a WLCG 1.0.");
375389
}
376390

377391
m_profile = SciToken::Profile::WLCG_1_0;
378392
must_verify_everything = false;
379393
const jwt::claim &claim = jwt.get_payload_claim("wlcg.ver");
380-
if (claim.get_type() != jwt::claim::type::string) {
381-
throw jwt::token_verification_exception("'ver' claim value must be a string (if present)");
394+
if (claim.get_type() != jwt::json::type::string) {
395+
throw JWTVerificationException("'ver' claim value must be a string (if present)");
382396
}
383397
std::string ver_string = claim.as_string();
384398
if (ver_string != "1.0") {
385399
std::stringstream ss;
386400
ss << "Unknown WLCG profile version in token: " << ver_string;
387-
throw jwt::token_verification_exception(ss.str());
401+
throw JWTVerificationException(ss.str());
388402
}
389403
if (!jwt.has_payload_claim("aud")) {
390-
throw jwt::token_verification_exception("Malformed token: 'aud' claim required for WLCG profile");
404+
throw JWTVerificationException("Malformed token: 'aud' claim required for WLCG profile");
391405
}
392406
} else if (m_profile == SciToken::Profile::AT_JWT) {
393407
// detected early above from typ header claim.
@@ -396,7 +410,7 @@ class Validator {
396410
if ((m_validate_profile != SciToken::Profile::COMPAT) &&
397411
(m_validate_profile != SciToken::Profile::SCITOKENS_1_0))
398412
{
399-
throw jwt::token_verification_exception("Invalidate token type; not expecting a SciToken 1.0.");
413+
throw JWTVerificationException("Invalidate token type; not expecting a SciToken 1.0.");
400414
}
401415

402416
m_profile = SciToken::Profile::SCITOKENS_1_0;
@@ -418,26 +432,26 @@ class Validator {
418432
std::stringstream ss;
419433
ss << "'" << claim_pair.first << "' claim verification is mandatory";
420434
// std::cout << ss.str() << std::endl;
421-
throw jwt::token_verification_exception(ss.str());
435+
throw JWTVerificationException(ss.str());
422436
}
423437
}
424438
// std::cout << "Running claim " << claim_pair.first << " through validation." << std::endl;
425439
if (iter != m_validators.end()) for (const auto &verification_func : iter->second) {
426440
const jwt::claim &claim = jwt.get_payload_claim(claim_pair.first);
427-
if (claim.get_type() != jwt::claim::type::string) {
441+
if (claim.get_type() != jwt::json::type::string) {
428442
std::stringstream ss;
429443
ss << "'" << claim_pair.first << "' claim value must be a string to verify.";
430-
throw jwt::token_verification_exception(ss.str());
444+
throw JWTVerificationException(ss.str());
431445
}
432446
std::string value = claim.as_string();
433447
char *err_msg = nullptr;
434448
if (verification_func(value.c_str(), &err_msg)) {
435449
if (err_msg) {
436-
throw jwt::token_verification_exception(err_msg);
450+
throw JWTVerificationException(err_msg);
437451
} else {
438452
std::stringstream ss;
439453
ss << "'" << claim_pair.first << "' claim verification failed.";
440-
throw jwt::token_verification_exception(ss.str());
454+
throw JWTVerificationException(ss.str());
441455
}
442456
}
443457
}
@@ -446,7 +460,7 @@ class Validator {
446460
if (verification_pair.first(claim, verification_pair.second) == false) {
447461
std::stringstream ss;
448462
ss << "'" << claim_pair.first << "' claim verification failed.";
449-
throw jwt::token_verification_exception(ss.str());
463+
throw JWTVerificationException(ss.str());
450464
}
451465
}
452466
}
@@ -484,7 +498,7 @@ class Validator {
484498
*/
485499
SciToken::Profile get_profile() const {
486500
if (m_profile == SciToken::Profile::COMPAT) {
487-
throw jwt::token_verification_exception("Token profile has not been set.");
501+
throw JWTVerificationException("Token profile has not been set.");
488502
}
489503
return m_profile;
490504
}
@@ -569,18 +583,18 @@ class Enforcer {
569583
static bool all_validator(const jwt::claim &, void *) {return true;}
570584

571585
static bool str_validator(const jwt::claim &claim, void *) {
572-
return claim.get_type() == jwt::claim::type::string;
586+
return claim.get_type() == jwt::json::type::string;
573587
}
574588

575589
static bool scope_validator(const jwt::claim &claim, void *myself);
576590

577591
static bool aud_validator(const jwt::claim &claim, void *myself) {
578592
auto me = reinterpret_cast<scitokens::Enforcer*>(myself);
579593
std::vector<std::string> jwt_audiences;
580-
if (claim.get_type() == jwt::claim::type::string) {
594+
if (claim.get_type() == jwt::json::type::string) {
581595
const std::string &audience = claim.as_string();
582596
jwt_audiences.push_back(audience);
583-
} else if (claim.get_type() == jwt::claim::type::array) {
597+
} else if (claim.get_type() == jwt::json::type::array) {
584598
const picojson::array &audiences = claim.as_array();
585599
for (const auto &aud_value : audiences) {
586600
const std::string &audience = aud_value.get<std::string>();

0 commit comments

Comments
 (0)