@@ -116,7 +116,8 @@ friend class scitokens::Validator;
116116 COMPAT = 0 ,
117117 SCITOKENS_1_0,
118118 SCITOKENS_2_0,
119- WLCG_1_0
119+ WLCG_1_0,
120+ AT_JWT
120121 };
121122
122123 SciToken (SciTokenKey &signing_algorithm)
@@ -198,6 +199,9 @@ friend class scitokens::Validator;
198199 builder.set_issued_at (time);
199200 builder.set_not_before (time);
200201 builder.set_expires_at (time + std::chrono::seconds (m_lifetime));
202+ if (m_serialize_profile == Profile::AT_JWT) {
203+ builder.set_type (" at+jwt" );
204+ }
201205
202206 uuid_t uuid;
203207 uuid_generate (uuid);
@@ -258,11 +262,31 @@ class Validator {
258262 }
259263
260264 void verify (const jwt::decoded_jwt &jwt) {
265+ // If token has a typ header claim (RFC8725 Section 3.11), trust that in COMPAT mode.
266+ if (jwt.has_type ()) {
267+ std::string t_type = jwt.get_type ();
268+ if (m_validate_profile == SciToken::Profile::COMPAT) {
269+ if (t_type == " at+jwt" || t_type == " application/at+jwt" ) {
270+ m_profile = SciToken::Profile::AT_JWT;
271+ }
272+ } else if (m_validate_profile == SciToken::Profile::AT_JWT) {
273+ if (t_type != " at+jwt" && t_type != " application/at+jwt" ) {
274+ throw jwt::token_verification_exception (" 'typ' header claim must be at+jwt" );
275+ }
276+ m_profile = SciToken::Profile::AT_JWT;
277+ }
278+ } else {
279+ if (m_validate_profile == SciToken::Profile::AT_JWT) {
280+ throw jwt::token_verification_exception (" 'typ' header claim must be set for at+jwt tokens" );
281+ }
282+ }
261283 if (!jwt.has_payload_claim (" iat" )) {
262284 throw jwt::token_verification_exception (" 'iat' claim is mandatory" );
263285 }
264- if (!jwt.has_payload_claim (" nbf" )) {
265- throw jwt::token_verification_exception (" 'nbf' claim is mandatory" );
286+ if (m_profile != SciToken::Profile::AT_JWT) {
287+ if (!jwt.has_payload_claim (" nbf" )) {
288+ throw jwt::token_verification_exception (" 'nbf' claim is mandatory" );
289+ }
266290 }
267291 if (!jwt.has_payload_claim (" exp" )) {
268292 throw jwt::token_verification_exception (" 'exp' claim is mandatory" );
@@ -360,6 +384,9 @@ class Validator {
360384 if (!jwt.has_payload_claim (" aud" )) {
361385 throw jwt::token_verification_exception (" Malformed token: 'aud' claim required for WLCG profile" );
362386 }
387+ } else if (m_profile == SciToken::Profile::AT_JWT) {
388+ // detected early above from typ header claim.
389+ must_verify_everything = false ;
363390 } else {
364391 if ((m_validate_profile != SciToken::Profile::COMPAT) &&
365392 (m_validate_profile != SciToken::Profile::SCITOKENS_1_0))
0 commit comments