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+
917namespace scitokens {
1018
1119class 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
1933class CurlException : public std ::runtime_error {
2034public:
@@ -47,7 +61,6 @@ class JsonException : public std::runtime_error {
4761 {}
4862};
4963
50-
5164class SciTokenKey {
5265
5366public:
@@ -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
255269public:
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