Skip to content

Commit c155afb

Browse files
authored
Improve load_jwks metadata verification
1 parent b438aa6 commit c155afb

File tree

3 files changed

+202
-49
lines changed

3 files changed

+202
-49
lines changed

src/scitokens.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,8 @@ int keycache_set_background_refresh(int enabled, char **err_msg);
310310
int keycache_stop_background_refresh(char **err_msg);
311311

312312
/**
313-
* Load the JWKS from the keycache for a given issuer, refreshing only if needed.
313+
* Load the JWKS from the keycache for a given issuer, refreshing only if
314+
* needed.
314315
* - Returns 0 if successful, nonzero on failure.
315316
* - If the existing JWKS has not expired, this will return the cached JWKS
316317
* without triggering a download.
@@ -326,7 +327,6 @@ int keycache_load_jwks(const char *issuer, char **jwks, char **err_msg);
326327
* - `metadata` is an output variable set to a JSON string containing:
327328
* - "expires": expiration time (Unix epoch seconds)
328329
* - "next_update": next update time (Unix epoch seconds)
329-
* - "extra": additional metadata (currently an empty JSON object)
330330
* - If the issuer does not exist in the cache, returns an error.
331331
*/
332332
int keycache_get_jwks_metadata(const char *issuer, char **metadata,

src/scitokens_cache.cpp

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,7 @@ std::string scitokens::Validator::load_jwks(const std::string &issuer) {
433433
auto now = std::time(NULL);
434434
picojson::value jwks;
435435
int64_t next_update;
436-
436+
437437
try {
438438
// Try to get from cache
439439
if (get_public_keys_from_db(issuer, now, jwks, next_update)) {
@@ -448,12 +448,12 @@ std::string scitokens::Validator::load_jwks(const std::string &issuer) {
448448
// Negative cache hit - return empty keys
449449
return std::string("{\"keys\": []}");
450450
}
451-
451+
452452
// Either not in cache or past next_update - refresh
453453
if (!refresh_jwks(issuer)) {
454454
throw CurlException("Failed to load JWKS for issuer: " + issuer);
455455
}
456-
456+
457457
// Get the newly refreshed JWKS
458458
return get_jwks(issuer);
459459
}
@@ -462,57 +462,57 @@ std::string scitokens::Validator::get_jwks_metadata(const std::string &issuer) {
462462
auto now = std::time(NULL);
463463
int64_t next_update = -1;
464464
int64_t expires = -1;
465-
465+
466466
// Get the metadata from database without expiry check
467467
auto cache_fname = get_cache_file();
468468
if (cache_fname.size() == 0) {
469469
throw std::runtime_error("Unable to access cache file");
470470
}
471-
471+
472472
sqlite3 *db;
473473
int rc = sqlite3_open(cache_fname.c_str(), &db);
474474
if (rc) {
475475
sqlite3_close(db);
476476
throw std::runtime_error("Failed to open cache database");
477477
}
478478
sqlite3_busy_timeout(db, SQLITE_BUSY_TIMEOUT_MS);
479-
479+
480480
sqlite3_stmt *stmt;
481481
rc = sqlite3_prepare_v2(db, "SELECT keys from keycache where issuer = ?",
482482
-1, &stmt, NULL);
483483
if (rc != SQLITE_OK) {
484484
sqlite3_close(db);
485485
throw std::runtime_error("Failed to prepare database query");
486486
}
487-
487+
488488
if (sqlite3_bind_text(stmt, 1, issuer.c_str(), issuer.size(),
489489
SQLITE_STATIC) != SQLITE_OK) {
490490
sqlite3_finalize(stmt);
491491
sqlite3_close(db);
492492
throw std::runtime_error("Failed to bind issuer to query");
493493
}
494-
494+
495495
rc = sqlite3_step(stmt);
496496
if (rc == SQLITE_ROW) {
497497
const unsigned char *data = sqlite3_column_text(stmt, 0);
498498
std::string metadata(reinterpret_cast<const char *>(data));
499499
sqlite3_finalize(stmt);
500500
sqlite3_close(db);
501-
501+
502502
picojson::value json_obj;
503503
auto err = picojson::parse(json_obj, metadata);
504504
if (!err.empty() || !json_obj.is<picojson::object>()) {
505505
throw JsonException("Invalid JSON in cache entry");
506506
}
507-
507+
508508
auto top_obj = json_obj.get<picojson::object>();
509-
509+
510510
// Extract expires
511511
auto iter = top_obj.find("expires");
512512
if (iter != top_obj.end() && iter->second.is<int64_t>()) {
513513
expires = iter->second.get<int64_t>();
514514
}
515-
515+
516516
// Extract next_update
517517
iter = top_obj.find("next_update");
518518
if (iter != top_obj.end() && iter->second.is<int64_t>()) {
@@ -521,13 +521,16 @@ std::string scitokens::Validator::get_jwks_metadata(const std::string &issuer) {
521521
// Default next_update to 4 hours before expiry
522522
next_update = expires - DEFAULT_NEXT_UPDATE_OFFSET_S;
523523
}
524-
525-
// Build metadata JSON
524+
525+
// Build metadata JSON (add future keys at top level if needed)
526526
picojson::object metadata_obj;
527-
metadata_obj["expires"] = picojson::value(expires);
528-
metadata_obj["next_update"] = picojson::value(next_update);
529-
metadata_obj["extra"] = picojson::value(picojson::object());
530-
527+
if (expires != -1) {
528+
metadata_obj["expires"] = picojson::value(expires);
529+
}
530+
if (next_update != -1) {
531+
metadata_obj["next_update"] = picojson::value(next_update);
532+
}
533+
531534
return picojson::value(metadata_obj).serialize();
532535
} else {
533536
sqlite3_finalize(stmt);
@@ -541,22 +544,22 @@ bool scitokens::Validator::delete_jwks(const std::string &issuer) {
541544
if (cache_fname.size() == 0) {
542545
return false;
543546
}
544-
547+
545548
sqlite3 *db;
546549
int rc = sqlite3_open(cache_fname.c_str(), &db);
547550
if (rc) {
548551
sqlite3_close(db);
549552
return false;
550553
}
551554
sqlite3_busy_timeout(db, SQLITE_BUSY_TIMEOUT_MS);
552-
555+
553556
// Use the existing remove_issuer_entry function
554557
// Note: remove_issuer_entry closes the database on error
555558
if (remove_issuer_entry(db, issuer, true) != 0) {
556559
// Database already closed by remove_issuer_entry
557560
return false;
558561
}
559-
562+
560563
sqlite3_close(db);
561564
return true;
562565
}

0 commit comments

Comments
 (0)