Skip to content

Commit 5ed7fc2

Browse files
committed
Add JWKS to the list of cached data for OAuth, and use it to validate the JWT
id_token value.
1 parent 54b879f commit 5ed7fc2

1 file changed

Lines changed: 63 additions & 1 deletion

File tree

cups/oauth.c

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ typedef enum _cups_otype_e // OAuth data type
9898
_CUPS_OTYPE_CLIENT_SECRET, // Client secret
9999
_CUPS_OTYPE_CODE_VERIFIER, // Client code_verifier
100100
_CUPS_OTYPE_USER_ID, // (User) ID token
101+
_CUPS_OTYPE_JWKS, // Server key store
101102
_CUPS_OTYPE_METADATA, // Server metadata
102103
_CUPS_OTYPE_NONCE, // Client nonce
103104
_CUPS_OTYPE_REDIRECT_URI, // Redirect URI used
@@ -124,6 +125,7 @@ static const char * const cups_otypes[] =
124125
"_CUPS_OTYPE_CLIENT_SECRET", // Client secret
125126
"_CUPS_OTYPE_CODE_VERIFIER", // Client code_verifier
126127
"_CUPS_OTYPE_USER_ID", // (User) ID token
128+
"_CUPS_OTYPE_JWKS", // Server key store
127129
"_CUPS_OTYPE_METADATA", // Server metadata
128130
"_CUPS_OTYPE_NONCE", // Client nonce
129131
"_CUPS_OTYPE_REDIRECT_URI", // Redirect URI used
@@ -150,6 +152,7 @@ static const char *github_metadata = // Github.com OAuth metadata
150152

151153
static char *oauth_copy_response(http_t *http);
152154
static cups_json_t *oauth_do_post(const char *ep, const char *content_type, const char *data);
155+
static cups_json_t *oauth_get_jwks(const char *auth_uri, cups_json_t *metadata);
153156
static char *oauth_load_value(const char *auth_uri, const char *secondary_uri, _cups_otype_t otype);
154157
static char *oauth_make_path(char *buffer, size_t bufsize, const char *auth_uri, const char *secondary_uri, _cups_otype_t otype);
155158
static char *oauth_make_software_id(char *buffer, size_t bufsize);
@@ -1052,6 +1055,9 @@ cupsOAuthGetTokens(
10521055
if (id_value)
10531056
{
10541057
// Validate the JWT
1058+
cups_json_t *jwks; // JWT key set
1059+
bool valid; // Valid id_token?
1060+
10551061
jwt = cupsJWTImportString(id_value, CUPS_JWS_FORMAT_COMPACT);
10561062
jnonce = cupsJWTGetClaimString(jwt, "nonce");
10571063
nonce = oauth_load_value(auth_uri, resource_uri, _CUPS_OTYPE_NONCE);
@@ -1060,7 +1066,16 @@ cupsOAuthGetTokens(
10601066
if (!jwt || (jnonce && nonce && strcmp(jnonce, nonce)))
10611067
goto done;
10621068

1063-
// TODO: Validate id_token against Authorization Server's JWKS
1069+
// Validate id_token against the Authorization Server's JWKS
1070+
if ((jwks = oauth_get_jwks(auth_uri, metadata)) == NULL)
1071+
goto done;
1072+
1073+
valid = cupsJWTHasValidSignature(jwt, jwks);
1074+
DEBUG_printf("1cupsOAuthGetTokens: valid=%s", valid ? "true" : "false");
1075+
cupsJSONDelete(jwks);
1076+
if (!valid)
1077+
goto done;
1078+
10641079
// TODO: Validate at_hash claim string against access_token value
10651080
}
10661081

@@ -1414,6 +1429,52 @@ oauth_do_post(const char *ep, // I - Endpoint URI
14141429
}
14151430

14161431

1432+
//
1433+
// 'oauth_get_jwks()' - Get the JWT key set for an Authorization Server.
1434+
//
1435+
1436+
static cups_json_t * // O - JWKS or `NULL` on error
1437+
oauth_get_jwks(const char *auth_uri, // I - Authorization server URI
1438+
cups_json_t *metadata) // I - Server metadata
1439+
{
1440+
const char *jwks_uri; // URI of key set
1441+
cups_json_t *jwks; // JWT key set
1442+
char filename[1024]; // Local metadata filename
1443+
struct stat fileinfo; // Local metadata file info
1444+
1445+
1446+
DEBUG_printf("oauth_get_jwks(auth_uri=\"%s\", metadata=%p)", auth_uri, (void *)metadata);
1447+
1448+
// Get existing key set...
1449+
if (!oauth_make_path(filename, sizeof(filename), auth_uri, /*secondary_uri*/NULL, _CUPS_OTYPE_JWKS))
1450+
return (NULL);
1451+
1452+
if (stat(filename, &fileinfo))
1453+
memset(&fileinfo, 0, sizeof(fileinfo));
1454+
1455+
// Don't bother connecting if the key set was updated recently...
1456+
if ((time(NULL) - fileinfo.st_mtime) <= 60)
1457+
return (cupsJSONImportFile(filename));
1458+
1459+
// Try getting the key set...
1460+
if ((jwks_uri = cupsJSONGetString(cupsJSONFind(metadata, "jwks_uri"))) == NULL)
1461+
return (NULL);
1462+
1463+
if ((jwks = cupsJSONImportURL(jwks_uri, &fileinfo.st_mtime)) != NULL)
1464+
{
1465+
// Save the key set...
1466+
char *s = cupsJSONExportString(jwks);
1467+
// JSON string
1468+
1469+
oauth_save_value(auth_uri, /*secondary_uri*/NULL, _CUPS_OTYPE_JWKS, s);
1470+
free(s);
1471+
}
1472+
1473+
// Return what we got...
1474+
return (jwks);
1475+
}
1476+
1477+
14171478
//
14181479
// 'oauth_load_value()' - Load the contents of the specified value file.
14191480
//
@@ -1484,6 +1545,7 @@ oauth_make_path(
14841545
"csec", // Client secret
14851546
"cver", // Code verifier
14861547
"idtk", // ID token
1548+
"jwks", // Key store
14871549
"meta", // Metadata
14881550
"nonc", // Nonce
14891551
"ruri", // Redirect URI

0 commit comments

Comments
 (0)