@@ -111,6 +111,14 @@ class SciToken {
111111friend class scitokens ::Validator;
112112
113113public:
114+
115+ enum class Profile {
116+ COMPAT = 0 ,
117+ SCITOKENS_1_0,
118+ SCITOKENS_2_0,
119+ WLCG_1_0
120+ };
121+
114122 SciToken (SciTokenKey &signing_algorithm)
115123 : m_key(signing_algorithm)
116124 {}
@@ -121,6 +129,11 @@ friend class scitokens::Validator;
121129 if (key == " iss" ) {m_issuer_set = true ;}
122130 }
123131
132+ void
133+ set_serialize_mode (Profile profile) {
134+ m_serialize_profile = profile;
135+ }
136+
124137 const jwt::claim
125138 get_claim (const std::string &key) {
126139 return m_claims[key];
@@ -162,6 +175,20 @@ friend class scitokens::Validator;
162175 uuid_unparse_lower (uuid, uuid_str);
163176 m_claims[" jti" ] = std::string (uuid_str);
164177
178+ if (m_serialize_profile == Profile::SCITOKENS_2_0) {
179+ m_claims[" ver" ] = std::string (" scitokens:2.0" );
180+ auto iter = m_claims.find (" aud" );
181+ if (iter == m_claims.end ()) {
182+ m_claims[" aud" ] = std::string (" ANY" );
183+ }
184+ } else if (m_serialize_profile == Profile::WLCG_1_0) {
185+ m_claims[" wlcg_ver" ] = std::string (" 1.0" );
186+ auto iter = m_claims.find (" aud" );
187+ if (iter == m_claims.end ()) {
188+ m_claims[" aud" ] = std::string (" https://wlcg.cern.ch/jwt/v1/any" );
189+ }
190+ }
191+
165192 // Set all the payload claims
166193 for (auto it : m_claims) {
167194 builder.set_payload_claim (it.first , it.second );
@@ -176,6 +203,8 @@ friend class scitokens::Validator;
176203private:
177204 bool m_issuer_set{false };
178205 int m_lifetime{600 };
206+ Profile m_profile{Profile::SCITOKENS_1_0};
207+ Profile m_serialize_profile{Profile::COMPAT};
179208 std::unordered_map<std::string, jwt::claim> m_claims;
180209 std::unique_ptr<jwt::decoded_jwt> m_decoded;
181210 SciTokenKey &m_key;
@@ -252,19 +281,62 @@ class Validator {
252281 throw jwt::token_verification_exception (" 'ver' claim value must be a string (if present)" );
253282 }
254283 std::string ver_string = claim.as_string ();
255- if (ver_string == " scitoken :2.0" ) {
284+ if (ver_string == " scitokens :2.0" ) {
256285 must_verify_everything = false ;
286+ if ((m_validate_profile != SciToken::Profile::COMPAT) &&
287+ (m_validate_profile != SciToken::Profile::SCITOKENS_2_0))
288+ {
289+ throw jwt::token_verification_exception (" Invalidate token type; not expecting a SciToken 2.0." );
290+ }
291+ m_profile = SciToken::Profile::SCITOKENS_2_0;
257292 if (!jwt.has_payload_claim (" aud" )) {
258293 throw jwt::token_verification_exception (" 'aud' claim required for SciTokens 2.0 profile" );
259294 }
260295 }
261- else if (ver_string == " scitokens:1.0" ) must_verify_everything = m_validate_all_claims;
262- else {
296+ else if (ver_string == " scitokens:1.0" ) {
297+ must_verify_everything = m_validate_all_claims;
298+ if ((m_validate_profile != SciToken::Profile::COMPAT) &&
299+ (m_validate_profile != SciToken::Profile::SCITOKENS_1_0))
300+ {
301+ throw jwt::token_verification_exception (" Invalidate token type; not expecting a SciToken 1.0." );
302+ }
303+ m_profile = SciToken::Profile::SCITOKENS_1_0;
304+ } else {
263305 std::stringstream ss;
264306 ss << " Unknown profile version in token: " << ver_string;
265307 throw jwt::token_verification_exception (ss.str ());
266308 }
309+ // Handle WLCG common JWT profile.
310+ } else if (jwt.has_payload_claim (" wlcg.ver" )) {
311+ if ((m_validate_profile != SciToken::Profile::COMPAT) &&
312+ (m_validate_profile != SciToken::Profile::WLCG_1_0))
313+ {
314+ throw jwt::token_verification_exception (" Invalidate token type; not expecting a WLCG 1.0." );
315+ }
316+
317+ m_profile = SciToken::Profile::WLCG_1_0;
318+ must_verify_everything = false ;
319+ const jwt::claim &claim = jwt.get_payload_claim (" wlcg.ver" );
320+ if (claim.get_type () != jwt::claim::type::string) {
321+ throw jwt::token_verification_exception (" 'ver' claim value must be a string (if present)" );
322+ }
323+ std::string ver_string = claim.as_string ();
324+ if (ver_string != " 1.0" ) {
325+ std::stringstream ss;
326+ ss << " Unknown WLCG profile version in token: " << ver_string;
327+ throw jwt::token_verification_exception (ss.str ());
328+ }
329+ if (!jwt.has_payload_claim (" aud" )) {
330+ throw jwt::token_verification_exception (" Malformed token: 'aud' claim required for WLCG profile" );
331+ }
267332 } else {
333+ if ((m_validate_profile != SciToken::Profile::COMPAT) &&
334+ (m_validate_profile != SciToken::Profile::SCITOKENS_1_0))
335+ {
336+ throw jwt::token_verification_exception (" Invalidate token type; not expecting a SciToken 1.0." );
337+ }
338+
339+ m_profile = SciToken::Profile::SCITOKENS_1_0;
268340 must_verify_everything = m_validate_all_claims;
269341 }
270342
@@ -339,13 +411,38 @@ class Validator {
339411 m_validate_all_claims = new_val;
340412 }
341413
414+ /* *
415+ * Get the profile of the last validated token.
416+ *
417+ * If there has been no validation - or the validation failed,
418+ * then the return value is unspecified.
419+ *
420+ * Will not return Profile::COMPAT.
421+ */
422+ SciToken::Profile get_profile () const {
423+ if (m_profile == SciToken::Profile::COMPAT) {
424+ throw jwt::token_verification_exception (" Token profile has not been set." );
425+ }
426+ return m_profile;
427+ }
428+
429+ /* *
430+ * Set the profile that will be used for validation; COMPAT indicates any supported profile
431+ * is allowable.
432+ */
433+ void set_validate_profile (SciToken::Profile profile) {
434+ m_validate_profile = profile;
435+ }
436+
342437private:
343438 void get_public_key_pem (const std::string &issuer, const std::string &kid, std::string &public_pem, std::string &algorithm);
344439 void get_public_keys_from_web (const std::string &issuer, picojson::value &keys, int64_t &next_update, int64_t &expires);
345440 bool get_public_keys_from_db (const std::string issuer, int64_t now, picojson::value &keys, int64_t &next_update);
346441 bool store_public_keys (const std::string &issuer, const picojson::value &keys, int64_t next_update, int64_t expires);
347442
348443 bool m_validate_all_claims{true };
444+ SciToken::Profile m_profile{SciToken::Profile::COMPAT};
445+ SciToken::Profile m_validate_profile{SciToken::Profile::COMPAT};
349446 ClaimStringValidatorMap m_validators;
350447 ClaimValidatorMap m_claim_validators;
351448
@@ -377,6 +474,10 @@ class Enforcer {
377474 m_validator.add_critical_claims (critical_claims);
378475 }
379476
477+ void set_validate_profile (SciToken::Profile profile) {
478+ m_validate_profile = profile;
479+ }
480+
380481 bool test (const SciToken &scitoken, const std::string &authz, const std::string &path) {
381482 reset_state ();
382483 m_test_path = path;
@@ -407,21 +508,26 @@ class Enforcer {
407508
408509 static bool aud_validator (const jwt::claim &claim, void *myself) {
409510 auto me = reinterpret_cast <scitokens::Enforcer*>(myself);
511+ std::vector<std::string> jwt_audiences;
410512 if (claim.get_type () == jwt::claim::type::string) {
411513 const std::string &audience = claim.as_string ();
412- if ((audience == " ANY" ) && !me->m_audiences .empty ()) {return true ;}
413- for (const auto &aud : me->m_audiences ) {
414- if (aud == audience) {return true ;}
415- }
514+ jwt_audiences.push_back (audience);
416515 } else if (claim.get_type () == jwt::claim::type::array) {
417516 const picojson::array &audiences = claim.as_array ();
418517 for (const auto &aud_value : audiences) {
419- if (!aud_value.is <std::string>()) {continue ;}
420- std::string audience = aud_value.get <std::string>();
421- if ((audience == " ANY" ) && !me->m_audiences .empty ()) {return true ;}
422- for (const auto &aud : me->m_audiences ) {
423- if (aud == audience) {return true ;}
424- }
518+ const std::string &audience = aud_value.get <std::string>();
519+ jwt_audiences.push_back (audience);
520+ }
521+ }
522+ for (const auto &aud_value : jwt_audiences) {
523+ if (((me->m_validator .get_profile () == SciToken::Profile::SCITOKENS_2_0) && (aud_value == " ANY" )) ||
524+ ((me->m_validator .get_profile () == SciToken::Profile::WLCG_1_0) && (aud_value == " https://wlcg.cern.ch/jwt/v1/any" ))
525+ )
526+ {
527+ return true ;
528+ }
529+ for (const auto &aud : me->m_audiences ) {
530+ if (aud == aud_value) {return true ;}
425531 }
426532 }
427533 return false ;
@@ -431,8 +537,11 @@ class Enforcer {
431537 m_test_path = " " ;
432538 m_test_authz = " " ;
433539 m_gen_acls.clear ();
540+ m_validator.set_validate_profile (m_validate_profile);
434541 }
435542
543+ SciToken::Profile m_validate_profile{SciToken::Profile::COMPAT};
544+
436545 std::string m_test_path;
437546 std::string m_test_authz;
438547 AclsList m_gen_acls;
0 commit comments