11
22#include < memory>
3+ #include < sstream>
34
45#include < jwt-cpp/jwt.h>
56
@@ -133,7 +134,7 @@ class SciToken {
133134 }
134135
135136 void
136- deserialize (const std::string &data);
137+ deserialize (const std::string &data, std::vector<std::string> allowed_issuers={} );
137138
138139private:
139140 bool m_issuer_set{false };
@@ -145,6 +146,9 @@ class SciToken {
145146
146147class Validator {
147148
149+ typedef int (*ValidatorFunction)(const char *value, char **err_msg);
150+ typedef std::map<std::string, std::vector<ValidatorFunction>> ClaimValidatorMap;
151+
148152public:
149153 void verify (jwt::decoded_jwt &jwt) {
150154 if (!jwt.has_payload_claim (" iat" )) {
@@ -162,6 +166,27 @@ class Validator {
162166 if (!jwt.has_header_claim (" kid" )) {
163167 throw jwt::token_verification_exception (" 'kid' claim is mandatory" );
164168 }
169+ if (!m_allowed_issuers.empty ()) {
170+ std::string issuer = jwt.get_issuer ();
171+ bool permitted = false ;
172+ for (const auto &allowed_issuer : m_allowed_issuers) {
173+ if (issuer == allowed_issuer) {
174+ permitted = true ;
175+ break ;
176+ }
177+ }
178+ if (!permitted) {
179+ throw jwt::token_verification_exception (" Token issuer is not in list of allowed issuers." );
180+ }
181+ }
182+
183+ for (const auto &claim : m_critical_claims) {
184+ if (!jwt.has_payload_claim (claim)) {
185+ std::stringstream ss;
186+ ss << " '" << claim << " ' claim is mandatory" ;
187+ throw jwt::token_verification_exception (ss.str ());
188+ }
189+ }
165190
166191 std::string public_pem;
167192 std::string algorithm;
@@ -172,13 +197,90 @@ class Validator {
172197 .allow_algorithm (key);
173198
174199 verifier.verify (jwt);
200+
201+ bool must_verify_everything = true ;
202+ if (jwt.has_payload_claim (" ver" )) {
203+ const jwt::claim &claim = jwt.get_payload_claim (" ver" );
204+ if (claim.get_type () != jwt::claim::type::string) {
205+ throw jwt::token_verification_exception (" 'ver' claim value must be a string (if present)" );
206+ }
207+ std::string ver_string = claim.as_string ();
208+ if (ver_string == " scitoken:2.0" ) must_verify_everything = false ;
209+ else if (ver_string == " scitokens:1.0" ) must_verify_everything = m_validate_all_claims;
210+ else {
211+ std::stringstream ss;
212+ ss << " Unknown profile version in token: " << ver_string;
213+ throw jwt::token_verification_exception (ss.str ());
214+ }
215+ } else {
216+ must_verify_everything = m_validate_all_claims;
217+ }
218+
219+ for (const auto &claim_pair : jwt.get_payload_claims ()) {
220+ if (claim_pair.first == " iat" || claim_pair.first == " nbf" || claim_pair.first == " exp" ) {
221+ continue ;
222+ }
223+ auto iter = m_validators.find (claim_pair.first );
224+ if (iter == m_validators.end () || iter->second .empty ()) {
225+ bool is_issuer = claim_pair.first == " iss" ;
226+ if (is_issuer && !m_allowed_issuers.empty ()) {
227+ // skip; we verified it above
228+ } else if (must_verify_everything) {
229+ std::stringstream ss;
230+ ss << " '" << claim_pair.first << " ' claim verification is mandatory" ;
231+ throw jwt::token_verification_exception (ss.str ());
232+ }
233+ }
234+ for (const auto verification_func : iter->second ) {
235+ const jwt::claim &claim = jwt.get_payload_claim (claim_pair.first );
236+ if (claim.get_type () != jwt::claim::type::string) {
237+ std::stringstream ss;
238+ ss << " '" << claim_pair.first << " ' claim value must be a string to verify." ;
239+ throw jwt::token_verification_exception (ss.str ());
240+ }
241+ std::string value = claim.as_string ();
242+ char * err_msg = nullptr ;
243+ if (verification_func (value.c_str (), &err_msg)) {
244+ if (err_msg) {
245+ throw jwt::token_verification_exception (err_msg);
246+ } else {
247+ std::stringstream ss;
248+ ss << " '" << claim_pair.first << " ' claim verification failed." ;
249+ throw jwt::token_verification_exception (ss.str ());
250+ }
251+ }
252+ }
253+ }
254+ }
255+
256+ void add_critical_claims (const std::vector<std::string> &claims) {
257+ std::copy (claims.begin (), claims.end (), std::back_inserter (m_critical_claims));
258+ }
259+
260+ void add_allowed_issuers (const std::vector<std::string> &allowed_issuers) {
261+ std::copy (allowed_issuers.begin (), allowed_issuers.end (), std::back_inserter (m_allowed_issuers));
262+ }
263+
264+ void add_string_validator (const std::string &claim, ValidatorFunction func) {
265+ auto result = m_validators.insert ({claim, std::vector<ValidatorFunction>()});
266+ result.first ->second .push_back (func);
267+ }
268+
269+ void set_validate_all_claims_scitokens_1 (bool new_val) {
270+ m_validate_all_claims = new_val;
175271 }
176272
177273private:
178274 void get_public_key_pem (const std::string &issuer, const std::string &kid, std::string &public_pem, std::string &algorithm);
179275 void get_public_keys_from_web (const std::string &issuer, picojson::value &keys, int64_t &next_update, int64_t &expires);
180276 bool get_public_keys_from_db (const std::string issuer, int64_t now, picojson::value &keys, int64_t &next_update);
181277 bool store_public_keys (const std::string &issuer, const picojson::value &keys, int64_t next_update, int64_t expires);
278+
279+ bool m_validate_all_claims{true };
280+ ClaimValidatorMap m_validators;
281+
282+ std::vector<std::string> m_critical_claims;
283+ std::vector<std::string> m_allowed_issuers;
182284};
183285
184286}
0 commit comments