@@ -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
151153static char * oauth_copy_response (http_t * http );
152154static 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 );
153156static char * oauth_load_value (const char * auth_uri , const char * secondary_uri , _cups_otype_t otype );
154157static char * oauth_make_path (char * buffer , size_t bufsize , const char * auth_uri , const char * secondary_uri , _cups_otype_t otype );
155158static 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