aboutsummaryrefslogtreecommitdiff
path: root/src/reclaim/plugin_rest_openid_connect.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/reclaim/plugin_rest_openid_connect.c')
-rw-r--r--src/reclaim/plugin_rest_openid_connect.c256
1 files changed, 233 insertions, 23 deletions
diff --git a/src/reclaim/plugin_rest_openid_connect.c b/src/reclaim/plugin_rest_openid_connect.c
index 1c00abcbd..bb8e1cd1e 100644
--- a/src/reclaim/plugin_rest_openid_connect.c
+++ b/src/reclaim/plugin_rest_openid_connect.c
@@ -20,6 +20,7 @@
20/** 20/**
21 * @author Martin Schanzenbach 21 * @author Martin Schanzenbach
22 * @author Philippe Buschmann 22 * @author Philippe Buschmann
23 * @author Tristan Schwieren
23 * @file identity/plugin_rest_openid_connect.c 24 * @file identity/plugin_rest_openid_connect.c
24 * @brief GNUnet Namestore REST plugin 25 * @brief GNUnet Namestore REST plugin
25 * 26 *
@@ -27,6 +28,7 @@
27#include "platform.h" 28#include "platform.h"
28#include <inttypes.h> 29#include <inttypes.h>
29#include <jansson.h> 30#include <jansson.h>
31#include <jose/jose.h>
30 32
31#include "gnunet_buffer_lib.h" 33#include "gnunet_buffer_lib.h"
32#include "gnunet_strings_lib.h" 34#include "gnunet_strings_lib.h"
@@ -63,6 +65,11 @@
63#define GNUNET_REST_API_NS_TOKEN "/openid/token" 65#define GNUNET_REST_API_NS_TOKEN "/openid/token"
64 66
65/** 67/**
68 * JSON Web Keys endpoint
69 */
70#define GNUNET_REST_API_JWKS "/jwks.json"
71
72/**
66 * UserInfo endpoint 73 * UserInfo endpoint
67 */ 74 */
68#define GNUNET_REST_API_NS_USERINFO "/openid/userinfo" 75#define GNUNET_REST_API_NS_USERINFO "/openid/userinfo"
@@ -228,6 +235,11 @@
228#define OIDC_ERROR_KEY_ACCESS_DENIED "access_denied" 235#define OIDC_ERROR_KEY_ACCESS_DENIED "access_denied"
229 236
230/** 237/**
238 * OIDC key store file name
239 */
240#define OIDC_JWK_RSA_FILENAME "jwk_rsa.json"
241
242/**
231 * How long to wait for a consume in userinfo endpoint 243 * How long to wait for a consume in userinfo endpoint
232 */ 244 */
233#define CONSUME_TIMEOUT GNUNET_TIME_relative_multiply ( \ 245#define CONSUME_TIMEOUT GNUNET_TIME_relative_multiply ( \
@@ -303,6 +315,11 @@ struct Plugin
303}; 315};
304 316
305/** 317/**
318 * @brief The RSA key used by the oidc enpoint
319 */
320json_t *oidc_jwk;
321
322/**
306 * OIDC needed variables 323 * OIDC needed variables
307 */ 324 */
308struct OIDC_Variables 325struct OIDC_Variables
@@ -859,6 +876,103 @@ cookie_identity_interpretation (struct RequestHandle *handle)
859 876
860 877
861/** 878/**
879 * @brief Read the the JSON Web Key in the given file and return it.
880 * Return NULL and emit warning if JSON can not be decoded or the key is
881 * invalid
882 *
883 * @param filename the file to read the JWK from
884 * @return json_t* the reed JWK
885 */
886json_t *
887read_jwk_from_file (const char *filename)
888{
889 json_t *jwk;
890 json_error_t error;
891
892 jwk = json_load_file (filename, JSON_DECODE_ANY, &error);
893
894 if (! jwk)
895 {
896 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
897 ("Could not read OIDC RSA key from config file; %s\n"),
898 error.text);
899 }
900
901 return jwk;
902}
903
904/**
905 * @brief Write the JWK to file. If unsuccessful emit warning
906 *
907 * @param filename the name of the file the JWK is writen to
908 * @param jwk the JWK that is going to be written
909 * @return int Return GNUNET_OK if write is sucessfull
910 */
911static int
912write_jwk_to_file (const char *filename,
913 json_t *jwk)
914{
915 if (json_dump_file (jwk, filename, JSON_INDENT (2)))
916 {
917 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
918 ("Could not write OIDC RSA key to file %s\n"),
919 filename);
920 return GNUNET_ERROR_TYPE_WARNING;
921 }
922 else
923 return GNUNET_OK;
924}
925
926/**
927 * @brief Generate a new RSA JSON Web Key
928 *
929 * @return json_t* the generated JWK
930 */
931json_t *
932generate_jwk ()
933{
934 json_t *jwk;
935 jwk = json_pack ("{s:s,s:i}", "kty", "RSA", "bits", 2048);
936 jose_jwk_gen (NULL, jwk);
937 json_incref (jwk);
938 return jwk;
939}
940
941/**
942 * Return the path to the RSA JWK key file
943 *
944 * @param cls the RequestHandle
945 */
946char *
947get_oidc_jwk_path (void *cls)
948{
949 char *oidc_directory;
950 char *oidc_jwk_path;
951 struct RequestHandle *handle = cls;
952
953 // Read OIDC directory from config
954 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg,
955 "reclaim-rest-plugin",
956 "oidc_dir",
957 &oidc_directory))
958 {
959 // Could not read Config file
960 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
961 handle->edesc = GNUNET_strdup ("gnunet configuration failed");
962 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
963 GNUNET_SCHEDULER_add_now (&do_error, handle);
964 return NULL;
965 }
966
967 // Create path to file
968 GNUNET_asprintf (&oidc_jwk_path, "%s/%s", oidc_directory,
969 OIDC_JWK_RSA_FILENAME);
970
971 return oidc_jwk_path;
972}
973
974
975/**
862 * Redirects to login page stored in configuration file 976 * Redirects to login page stored in configuration file
863 */ 977 */
864static void 978static void
@@ -1954,7 +2068,7 @@ check_authorization (struct RequestHandle *handle,
1954 // check client password 2068 // check client password
1955 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg, 2069 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg,
1956 "reclaim-rest-plugin", 2070 "reclaim-rest-plugin",
1957 "OIDC_CLIENT_SECRET", 2071 "OIDC_CLIENT_HMAC_SECRET",
1958 &expected_pass)) 2072 &expected_pass))
1959 { 2073 {
1960 if (0 != strcmp (expected_pass, received_cpw)) 2074 if (0 != strcmp (expected_pass, received_cpw))
@@ -2047,9 +2161,12 @@ token_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
2047 char *json_response; 2161 char *json_response;
2048 char *id_token; 2162 char *id_token;
2049 char *access_token; 2163 char *access_token;
2164 char *jwa;
2050 char *jwt_secret; 2165 char *jwt_secret;
2051 char *nonce = NULL; 2166 char *nonce = NULL;
2052 char *code_verifier; 2167 char *code_verifier;
2168 json_t *oidc_jwk;
2169 char *oidc_jwk_path;
2053 2170
2054 /* 2171 /*
2055 * Check Authorization 2172 * Check Authorization
@@ -2156,32 +2273,66 @@ token_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
2156 return; 2273 return;
2157 } 2274 }
2158 2275
2159 2276 // Check if HMAC or RSA should be used
2160 // TODO OPTIONAL acr,amr,azp
2161 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, 2277 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
2162 "reclaim-rest-plugin", 2278 "reclaim-rest-plugin",
2163 "jwt_secret", 2279 "oidc_json_web_algorithm",
2164 &jwt_secret)) 2280 &jwa))
2165 { 2281 {
2166 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST); 2282 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2167 handle->edesc = GNUNET_strdup ("No signing secret configured!"); 2283 "Could not read OIDC JSON Web Algorithm config attribute."
2168 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; 2284 "Defaulting to RS256.");
2169 GNUNET_free (code); 2285 jwa = JWT_ALG_VALUE_RSA;
2170 GNUNET_RECLAIM_attribute_list_destroy (cl); 2286 }
2171 GNUNET_RECLAIM_presentation_list_destroy (pl); 2287
2172 if (NULL != nonce) 2288 if (strcmp(jwa, JWT_ALG_VALUE_RSA))
2173 GNUNET_free (nonce); 2289 {
2174 GNUNET_SCHEDULER_add_now (&do_error, handle); 2290 // Replace for now
2175 return; 2291 oidc_jwk_path = get_oidc_jwk_path (cls);
2292 oidc_jwk = read_jwk_from_file (oidc_jwk_path);
2293 id_token = OIDC_generate_id_token_rsa (&ticket.audience,
2294 &ticket.identity,
2295 cl,
2296 pl,
2297 &expiration_time,
2298 (NULL != nonce) ? nonce : NULL,
2299 oidc_jwk);
2300 }
2301 else if (strcmp(jwa, JWT_ALG_VALUE_HMAC))
2302 {
2303 // TODO OPTIONAL acr,amr,azp
2304 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
2305 "reclaim-rest-plugin",
2306 "jwt_secret",
2307 &jwt_secret))
2308 {
2309 handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
2310 handle->edesc = GNUNET_strdup ("No signing secret configured!");
2311 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
2312 GNUNET_free (code);
2313 GNUNET_RECLAIM_attribute_list_destroy (cl);
2314 GNUNET_RECLAIM_presentation_list_destroy (pl);
2315 if (NULL != nonce)
2316 GNUNET_free (nonce);
2317 GNUNET_SCHEDULER_add_now (&do_error, handle);
2318 return;
2319 }
2320
2321 id_token = OIDC_generate_id_token_hmac (&ticket.audience,
2322 &ticket.identity,
2323 cl,
2324 pl,
2325 &expiration_time,
2326 (NULL != nonce) ? nonce : NULL,
2327 jwt_secret);
2328
2329 GNUNET_free (jwt_secret);
2330 }
2331 else
2332 {
2333 // TODO: OPTION NOT FOUND ERROR
2176 } 2334 }
2177 id_token = OIDC_generate_id_token (&ticket.audience, 2335
2178 &ticket.identity,
2179 cl,
2180 pl,
2181 &expiration_time,
2182 (NULL != nonce) ? nonce : NULL,
2183 jwt_secret);
2184 GNUNET_free (jwt_secret);
2185 if (NULL != nonce) 2336 if (NULL != nonce)
2186 GNUNET_free (nonce); 2337 GNUNET_free (nonce);
2187 access_token = OIDC_access_token_new (&ticket); 2338 access_token = OIDC_access_token_new (&ticket);
@@ -2474,6 +2625,59 @@ userinfo_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
2474 GNUNET_free (authorization); 2625 GNUNET_free (authorization);
2475} 2626}
2476 2627
2628/**
2629 * Responds to /jwks.json
2630 *
2631 * @param con_handle the connection handle
2632 * @param url the url
2633 * @param cls the RequestHandle
2634 */
2635static void
2636jwks_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
2637 const char *url,
2638 void *cls)
2639{
2640 char *oidc_directory;
2641 char *oidc_jwk_path;
2642 char *oidc_jwk_pub_str;
2643 json_t *oidc_jwk;
2644 struct MHD_Response *resp;
2645 struct RequestHandle *handle = cls;
2646
2647 oidc_jwk_path = get_oidc_jwk_path (cls);
2648 oidc_jwk = read_jwk_from_file (oidc_jwk_path);
2649
2650 // Check if secret JWK exists
2651 if (! oidc_jwk)
2652 {
2653 // Generate and save a new key
2654 oidc_jwk = generate_jwk ();
2655
2656 // Create new oidc directory
2657 if (GNUNET_OK != GNUNET_DISK_directory_create (oidc_directory))
2658 {
2659 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2660 ("Failed to create directory `%s' for storing oidc data\n"),
2661 oidc_directory);
2662 }
2663 else
2664 {
2665 write_jwk_to_file (oidc_jwk_path, oidc_jwk);
2666 }
2667 }
2668
2669 // Convert secret JWK to public JWK
2670 jose_jwk_pub (NULL, oidc_jwk);
2671
2672 // Encode JWK as string and return to API endpoint
2673 oidc_jwk_pub_str = json_dumps (oidc_jwk, JSON_INDENT (1));
2674 resp = GNUNET_REST_create_response (oidc_jwk_pub_str);
2675 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
2676 json_decref (oidc_jwk);
2677 GNUNET_free (oidc_jwk_pub_str);
2678 free (oidc_jwk_pub_str);
2679 cleanup_handle (handle);
2680}
2477 2681
2478/** 2682/**
2479 * If listing is enabled, prints information about the egos. 2683 * If listing is enabled, prints information about the egos.
@@ -2621,10 +2825,15 @@ oidc_config_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
2621 sig_algs = json_array (); 2825 sig_algs = json_array ();
2622 json_array_append_new (sig_algs, 2826 json_array_append_new (sig_algs,
2623 json_string ("HS512")); 2827 json_string ("HS512"));
2828 json_array_append_new (sig_algs,
2829 json_string ("RS256"));
2624 json_object_set_new (oidc_config, 2830 json_object_set_new (oidc_config,
2625 "id_token_signing_alg_values_supported", 2831 "id_token_signing_alg_values_supported",
2626 sig_algs); 2832 sig_algs);
2627 json_object_set_new (oidc_config, 2833 json_object_set_new (oidc_config,
2834 "jwks_uri",
2835 json_string ("http://localhost:7776/jwks.json"));
2836 json_object_set_new (oidc_config,
2628 "userinfo_endpoint", 2837 "userinfo_endpoint",
2629 json_string ("http://localhost:7776/openid/userinfo")); 2838 json_string ("http://localhost:7776/openid/userinfo"));
2630 scopes = json_array (); 2839 scopes = json_array ();
@@ -2719,6 +2928,7 @@ rest_identity_process_request (struct GNUNET_REST_RequestHandle *rest_handle,
2719 { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_TOKEN, &token_endpoint }, 2928 { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_TOKEN, &token_endpoint },
2720 { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_USERINFO, &userinfo_endpoint }, 2929 { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_USERINFO, &userinfo_endpoint },
2721 { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_USERINFO, &userinfo_endpoint }, 2930 { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_USERINFO, &userinfo_endpoint },
2931 { MHD_HTTP_METHOD_GET, GNUNET_REST_API_JWKS, &jwks_endpoint },
2722 { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_OIDC_CONFIG, 2932 { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_OIDC_CONFIG,
2723 &oidc_config_endpoint }, 2933 &oidc_config_endpoint },
2724 { MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_OIDC_CONFIG, 2934 { MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_OIDC_CONFIG,