Merge branch 'master' into schanzen/reclaim_256bit
[oweals/gnunet.git] / src / reclaim / plugin_rest_openid_connect.c
index 753c3fcae82723352217a14f24a2b0ad8e865a4f..14a96ed1924cd230b58998ceef3dc0178c278f75 100644 (file)
@@ -15,8 +15,8 @@
    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-     SPDX-License-Identifier: AGPL3.0-or-later
  */
+   SPDX-License-Identifier: AGPL3.0-or-later
+ */
 /**
  * @author Martin Schanzenbach
  * @author Philippe Buschmann
@@ -32,7 +32,7 @@
 #include "gnunet_gnsrecord_lib.h"
 #include "gnunet_identity_service.h"
 #include "gnunet_namestore_service.h"
-#include "gnunet_reclaim_attribute_lib.h"
+#include "gnunet_reclaim_lib.h"
 #include "gnunet_reclaim_service.h"
 #include "gnunet_rest_lib.h"
 #include "gnunet_rest_plugin.h"
  */
 #define OIDC_NONCE_KEY "nonce"
 
+/**
+ * OIDC claims key
+ */
+#define OIDC_CLAIMS_KEY "claims"
+
+/**
+ * OIDC PKCE code challenge
+ */
+#define OIDC_CODE_CHALLENGE_KEY "code_challenge"
+
+/**
+ * OIDC PKCE code verifier
+ */
+#define OIDC_CODE_VERIFIER_KEY "code_verifier"
+
 /**
  * OIDC cookie expiration (in seconds)
  */
 /**
  * OIDC ignored parameter array
  */
-static char *OIDC_ignored_parameter_array[] = {"display",
-                                               "prompt",
-                                               "ui_locales",
-                                               "response_mode",
-                                               "id_token_hint",
-                                               "login_hint",
-                                               "acr_values"};
+static char *OIDC_ignored_parameter_array[] = { "display",
+                                                "prompt",
+                                                "ui_locales",
+                                                "response_mode",
+                                                "id_token_hint",
+                                                "login_hint",
+                                                "acr_values" };
 
 /**
  * OIDC Hash map that keeps track of issued cookies
@@ -280,6 +295,11 @@ struct OIDC_Variables
    */
   char *nonce;
 
+  /**
+   * The OIDC claims
+   */
+  char *claims;
+
   /**
    * The OIDC response type
    */
@@ -295,6 +315,16 @@ struct OIDC_Variables
    */
   int user_cancelled;
 
+  /**
+   * The PKCE code_challenge
+   */
+  char *code_challenge;
+
+  /**
+   * The PKCE code_verifier
+   */
+  char *code_verifier;
+
   /**
    * The response JSON
    */
@@ -398,7 +428,13 @@ struct RequestHandle
   /**
    * Attribute claim list
    */
-  struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attr_list;
+  struct GNUNET_RECLAIM_AttributeList *attr_list;
+
+  /**
+   * Attestation list
+   */
+  struct GNUNET_RECLAIM_AttestationList *attests_list;
+
 
   /**
    * IDENTITY Operation
@@ -420,6 +456,12 @@ struct RequestHandle
    */
   struct GNUNET_RECLAIM_AttributeIterator *attr_it;
 
+  /**
+   * Attestation iterator
+   */
+  struct GNUNET_RECLAIM_AttestationIterator *attest_it;
+
+
   /**
    * Ticket iterator
    */
@@ -493,10 +535,9 @@ struct RequestHandle
 static void
 cleanup_handle (struct RequestHandle *handle)
 {
-  struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *claim_entry;
-  struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *claim_tmp;
   struct EgoEntry *ego_entry;
   struct EgoEntry *ego_tmp;
+
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up\n");
   if (NULL != handle->timeout_task)
     GNUNET_SCHEDULER_cancel (handle->timeout_task);
@@ -504,6 +545,8 @@ cleanup_handle (struct RequestHandle *handle)
     GNUNET_IDENTITY_disconnect (handle->identity_handle);
   if (NULL != handle->attr_it)
     GNUNET_RECLAIM_get_attributes_stop (handle->attr_it);
+  if (NULL != handle->attest_it)
+    GNUNET_RECLAIM_get_attestations_stop (handle->attest_it);
   if (NULL != handle->ticket_it)
     GNUNET_RECLAIM_ticket_iteration_stop (handle->ticket_it);
   if (NULL != handle->idp)
@@ -533,17 +576,9 @@ cleanup_handle (struct RequestHandle *handle)
     json_decref (handle->oidc->response);
     GNUNET_free (handle->oidc);
   }
-  if (NULL != handle->attr_list)
-  {
-    for (claim_entry = handle->attr_list->list_head; NULL != claim_entry;)
-    {
-      claim_tmp = claim_entry;
-      claim_entry = claim_entry->next;
-      GNUNET_free (claim_tmp->claim);
-      GNUNET_free (claim_tmp);
-    }
-    GNUNET_free (handle->attr_list);
-  }
+  GNUNET_RECLAIM_attribute_list_destroy (handle->attr_list);
+  GNUNET_RECLAIM_attestation_list_destroy (handle->attests_list);
+
   for (ego_entry = handle->ego_head; NULL != ego_entry;)
   {
     ego_tmp = ego_entry;
@@ -552,10 +587,10 @@ cleanup_handle (struct RequestHandle *handle)
     GNUNET_free (ego_tmp->keystring);
     GNUNET_free (ego_tmp);
   }
-  GNUNET_free_non_null (handle->attr_it);
   GNUNET_free (handle);
 }
 
+
 static void
 cleanup_handle_delayed (void *cls)
 {
@@ -632,6 +667,7 @@ do_redirect_error (void *cls)
   struct RequestHandle *handle = cls;
   struct MHD_Response *resp;
   char *redirect;
+
   GNUNET_asprintf (&redirect,
                    "%s?error=%s&error_description=%s%s%s",
                    handle->oidc->redirect_uri,
@@ -646,6 +682,7 @@ do_redirect_error (void *cls)
   GNUNET_free (redirect);
 }
 
+
 /**
  * Task run on timeout, sends error message.  Cleans up everything.
  *
@@ -660,6 +697,7 @@ do_timeout (void *cls)
   do_error (handle);
 }
 
+
 /**
  * Return attributes for claim
  *
@@ -673,7 +711,7 @@ return_userinfo_response (void *cls)
   struct MHD_Response *resp;
 
   result_str = json_dumps (handle->oidc->response, 0);
-
+  GNUNET_log (GNUNET_ERROR_TYPE_ERROR,"ID-Token: %s\n",result_str);
   resp = GNUNET_REST_create_response (result_str);
   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
   GNUNET_free (result_str);
@@ -724,7 +762,7 @@ cookie_identity_interpretation (struct RequestHandle *handle)
                       strlen (OIDC_COOKIE_HEADER_KEY),
                       &cache_key);
   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
-                                                             ->header_param_map,
+                                                           ->header_param_map,
                                                            &cache_key))
   {
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "No cookie found\n");
@@ -793,8 +831,10 @@ cookie_identity_interpretation (struct RequestHandle *handle)
   value = strtok (token, OIDC_COOKIE_HEADER_INFORMATION_KEY);
   GNUNET_assert (NULL != value);
   handle->oidc->login_identity = GNUNET_strdup (value);
+  GNUNET_free (cookies);
 }
 
+
 /**
  * Redirects to login page stored in configuration file
  */
@@ -812,7 +852,7 @@ login_redirect (void *cls)
                                                           &login_base_url))
   {
     GNUNET_asprintf (&new_redirect,
-                     "%s?%s=%s&%s=%s&%s=%s&%s=%s&%s=%s&%s=%s",
+                     "%s?%s=%s&%s=%s&%s=%s&%s=%s&%s=%s&%s=%s&%s=%s&%s=%s",
                      login_base_url,
                      OIDC_RESPONSE_TYPE_KEY,
                      handle->oidc->response_type,
@@ -824,8 +864,14 @@ login_redirect (void *cls)
                      handle->oidc->scope,
                      OIDC_STATE_KEY,
                      (NULL != handle->oidc->state) ? handle->oidc->state : "",
+                     OIDC_CODE_CHALLENGE_KEY,
+                     (NULL != handle->oidc->code_challenge) ?
+                     handle->oidc->code_challenge : "",
                      OIDC_NONCE_KEY,
-                     (NULL != handle->oidc->nonce) ? handle->oidc->nonce : "");
+                     (NULL != handle->oidc->nonce) ? handle->oidc->nonce : "",
+                     OIDC_CLAIMS_KEY,
+                     (NULL != handle->oidc->claims) ? handle->oidc->claims :
+                     "");
     resp = GNUNET_REST_create_response ("");
     MHD_add_response_header (resp, "Location", new_redirect);
     GNUNET_free (login_base_url);
@@ -843,6 +889,7 @@ login_redirect (void *cls)
   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
 }
 
+
 /**
  * Does internal server error when iteration failed.
  */
@@ -850,6 +897,7 @@ static void
 oidc_iteration_error (void *cls)
 {
   struct RequestHandle *handle = cls;
+
   handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
   handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
   GNUNET_SCHEDULER_add_now (&do_error, handle);
@@ -870,7 +918,6 @@ oidc_ticket_issue_cb (void *cls, const struct GNUNET_RECLAIM_Ticket *ticket)
   char *code_string;
 
   handle->idp_op = NULL;
-  handle->ticket = *ticket;
   if (NULL == ticket)
   {
     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
@@ -878,18 +925,20 @@ oidc_ticket_issue_cb (void *cls, const struct GNUNET_RECLAIM_Ticket *ticket)
     GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
     return;
   }
+  handle->ticket = *ticket;
   ticket_str =
     GNUNET_STRINGS_data_to_string_alloc (&handle->ticket,
-                                         sizeof (struct GNUNET_RECLAIM_Ticket));
+                                         sizeof(struct GNUNET_RECLAIM_Ticket));
   // TODO change if more attributes are needed (see max_age)
   code_string = OIDC_build_authz_code (&handle->priv_key,
                                        &handle->ticket,
                                        handle->attr_list,
-                                       handle->oidc->nonce);
+                                       handle->attests_list,
+                                       handle->oidc->nonce,
+                                       handle->oidc->code_challenge);
   if ((NULL != handle->redirect_prefix) && (NULL != handle->redirect_suffix) &&
       (NULL != handle->tld))
   {
-
     GNUNET_asprintf (&redirect_uri,
                      "%s.%s/%s?%s=%s&state=%s",
                      handle->redirect_prefix,
@@ -917,10 +966,58 @@ oidc_ticket_issue_cb (void *cls, const struct GNUNET_RECLAIM_Ticket *ticket)
   GNUNET_free (code_string);
 }
 
+
+static void
+oidc_attest_collect_finished_cb (void *cls)
+{
+  struct RequestHandle *handle = cls;
+
+  handle->attest_it = NULL;
+  handle->idp_op = GNUNET_RECLAIM_ticket_issue (handle->idp,
+                                                &handle->priv_key,
+                                                &handle->oidc->client_pkey,
+                                                handle->attr_list,
+                                                &oidc_ticket_issue_cb,
+                                                handle);
+}
+
+
+/**
+ * Collects all attributes for an ego if in scope parameter
+ */
+static void
+oidc_attest_collect (void *cls,
+                     const struct GNUNET_CRYPTO_EcdsaPublicKey *identity,
+                     const struct GNUNET_RECLAIM_Attestation *attest)
+{
+  struct RequestHandle *handle = cls;
+  struct GNUNET_RECLAIM_AttributeListEntry *le;
+
+  for (le = handle->attr_list->list_head; NULL != le; le = le->next)
+  {
+    if (GNUNET_NO == GNUNET_RECLAIM_id_is_equal (&le->attribute->attestation,
+                                                 &attest->id))
+    {
+      struct GNUNET_RECLAIM_AttestationListEntry *ale;
+      ale = GNUNET_new (struct GNUNET_RECLAIM_AttestationListEntry);
+      ale->attestation = GNUNET_RECLAIM_attestation_new (attest->name,
+                                                         attest->type,
+                                                         attest->data,
+                                                         attest->data_size);
+      GNUNET_CONTAINER_DLL_insert (handle->attests_list->list_head,
+                                   handle->attests_list->list_tail,
+                                   ale);
+    }
+  }
+  GNUNET_RECLAIM_get_attestations_next (handle->attest_it);
+}
+
+
 static void
-oidc_collect_finished_cb (void *cls)
+oidc_attr_collect_finished_cb (void *cls)
 {
   struct RequestHandle *handle = cls;
+
   handle->attr_it = NULL;
   handle->ticket_it = NULL;
   if (NULL == handle->attr_list->list_head)
@@ -930,6 +1027,17 @@ oidc_collect_finished_cb (void *cls)
     GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
     return;
   }
+  handle->attests_list = GNUNET_new (struct GNUNET_RECLAIM_AttestationList);
+  handle->attest_it =
+    GNUNET_RECLAIM_get_attestations_start (handle->idp,
+                                           &handle->priv_key,
+                                           &oidc_iteration_error,
+                                           handle,
+                                           &oidc_attest_collect,
+                                           handle,
+                                           &oidc_attest_collect_finished_cb,
+                                           handle);
+
   handle->idp_op = GNUNET_RECLAIM_ticket_issue (handle->idp,
                                                 &handle->priv_key,
                                                 &handle->oidc->client_pkey,
@@ -945,20 +1053,14 @@ oidc_collect_finished_cb (void *cls)
 static void
 oidc_attr_collect (void *cls,
                    const struct GNUNET_CRYPTO_EcdsaPublicKey *identity,
-                   const struct GNUNET_RECLAIM_ATTRIBUTE_Claim *attr)
+                   const struct GNUNET_RECLAIM_Attribute *attr)
 {
   struct RequestHandle *handle = cls;
-  struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *le;
+  struct GNUNET_RECLAIM_AttributeListEntry *le;
   char *scope_variables;
   char *scope_variable;
   char delimiter[] = " ";
 
-  if ((NULL == attr->name) || (NULL == attr->data))
-  {
-    GNUNET_RECLAIM_get_attributes_next (handle->attr_it);
-    return;
-  }
-
   scope_variables = GNUNET_strdup (handle->oidc->scope);
   scope_variable = strtok (scope_variables, delimiter);
   while (NULL != scope_variable)
@@ -971,17 +1073,19 @@ oidc_attr_collect (void *cls,
   {
     GNUNET_RECLAIM_get_attributes_next (handle->attr_it);
     GNUNET_free (scope_variables);
+    // We can ignore this
     return;
   }
   GNUNET_free (scope_variables);
-
-  le = GNUNET_new (struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry);
-  le->claim = GNUNET_RECLAIM_ATTRIBUTE_claim_new (attr->name,
-                                                  attr->type,
-                                                  attr->data,
-                                                  attr->data_size);
-  le->claim->id = attr->id;
-  le->claim->version = attr->version;
+  le = GNUNET_new (struct GNUNET_RECLAIM_AttributeListEntry);
+  le->attribute = GNUNET_RECLAIM_attribute_new (attr->name,
+                                                &attr->attestation,
+                                                attr->type,
+                                                attr->data,
+                                                attr->data_size);
+  le->attribute->id = attr->id;
+  le->attribute->flag = attr->flag;
+  le->attribute->attestation = attr->attestation;
   GNUNET_CONTAINER_DLL_insert (handle->attr_list->list_head,
                                handle->attr_list->list_tail,
                                le);
@@ -1020,10 +1124,10 @@ code_redirect (void *cls)
     {
       if (GNUNET_OK !=
           GNUNET_CRYPTO_ecdsa_public_key_from_string (handle->oidc
-                                                        ->login_identity,
+                                                      ->login_identity,
                                                       strlen (
                                                         handle->oidc
-                                                          ->login_identity),
+                                                        ->login_identity),
                                                       &pubkey))
       {
         handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_COOKIE);
@@ -1043,7 +1147,7 @@ code_redirect (void *cls)
             *GNUNET_IDENTITY_ego_get_private_key (handle->ego_entry->ego);
           handle->idp = GNUNET_RECLAIM_connect (cfg);
           handle->attr_list =
-            GNUNET_new (struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList);
+            GNUNET_new (struct GNUNET_RECLAIM_AttributeList);
           handle->attr_it =
             GNUNET_RECLAIM_get_attributes_start (handle->idp,
                                                  &handle->priv_key,
@@ -1051,7 +1155,7 @@ code_redirect (void *cls)
                                                  handle,
                                                  &oidc_attr_collect,
                                                  handle,
-                                                 &oidc_collect_finished_cb,
+                                                 &oidc_attr_collect_finished_cb,
                                                  handle);
           return;
         }
@@ -1134,25 +1238,40 @@ lookup_redirect_uri_result (void *cls,
     if (NULL == strstr (tmp, handle->oidc->client_id))
     {
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "Redirect uri %s does not contain client_id %s",
+                  "Redirect uri %s does not contain client_id %s\n",
                   tmp,
                   handle->oidc->client_id);
     }
     else
     {
-
       pos = strrchr (tmp, (unsigned char) '.');
+      if (NULL == pos)
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                    "Redirect uri %s contains client_id but is malformed\n",
+                    tmp);
+        GNUNET_free (tmp);
+        continue;
+      }
       *pos = '\0';
       handle->redirect_prefix = GNUNET_strdup (tmp);
       tmp_key_str = pos + 1;
       pos = strchr (tmp_key_str, (unsigned char) '/');
+      if (NULL == pos)
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                    "Redirect uri %s contains client_id but is malformed\n",
+                    tmp);
+        GNUNET_free (tmp);
+        continue;
+      }
       *pos = '\0';
       handle->redirect_suffix = GNUNET_strdup (pos + 1);
 
       GNUNET_STRINGS_string_to_data (tmp_key_str,
                                      strlen (tmp_key_str),
                                      &redirect_zone,
-                                     sizeof (redirect_zone));
+                                     sizeof(redirect_zone));
     }
     GNUNET_SCHEDULER_add_now (&build_redirect, handle);
     GNUNET_free (tmp);
@@ -1184,14 +1303,16 @@ client_redirect (void *cls)
                        handle);
 }
 
+
 static char *
 get_url_parameter_copy (const struct RequestHandle *handle, const char *key)
 {
   struct GNUNET_HashCode hc;
   char *value;
+
   GNUNET_CRYPTO_hash (key, strlen (key), &hc);
   if (GNUNET_YES != GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
-                                                              ->url_param_map,
+                                                            ->url_param_map,
                                                             &hc))
     return NULL;
   value =
@@ -1254,9 +1375,12 @@ build_authz_response (void *cls)
   // OPTIONAL value: nonce
   handle->oidc->nonce = get_url_parameter_copy (handle, OIDC_NONCE_KEY);
 
+  // OPTIONAL value: claims
+  handle->oidc->claims = get_url_parameter_copy (handle, OIDC_CLAIMS_KEY);
+
   // TODO check other values if needed
   number_of_ignored_parameter =
-    sizeof (OIDC_ignored_parameter_array) / sizeof (char *);
+    sizeof(OIDC_ignored_parameter_array) / sizeof(char *);
   for (iterator = 0; iterator < number_of_ignored_parameter; iterator++)
   {
     GNUNET_CRYPTO_hash (OIDC_ignored_parameter_array[iterator],
@@ -1264,7 +1388,7 @@ build_authz_response (void *cls)
                         &cache_key);
     if (GNUNET_YES ==
         GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
-                                                  ->url_param_map,
+                                                ->url_param_map,
                                                 &cache_key))
     {
       handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_ACCESS_DENIED);
@@ -1315,6 +1439,7 @@ build_authz_response (void *cls)
     GNUNET_SCHEDULER_add_now (&client_redirect, handle);
 }
 
+
 /**
  * Iterate over tlds in config
  */
@@ -1334,6 +1459,7 @@ tld_iter (void *cls, const char *section, const char *option, const char *value)
     handle->tld = GNUNET_strdup (option + 1);
 }
 
+
 /**
  * Responds to authorization GET and url-encoded POST request
  *
@@ -1367,6 +1493,15 @@ authorize_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
     return;
   }
 
+  // OPTIONAL value: code_challenge
+  handle->oidc->code_challenge = get_url_parameter_copy (handle,
+                                                         OIDC_CODE_CHALLENGE_KEY);
+  if (NULL == handle->oidc->code_challenge)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "OAuth authorization request does not contain PKCE parameters!\n");
+  }
+
   if (GNUNET_OK !=
       GNUNET_CRYPTO_ecdsa_public_key_from_string (handle->oidc->client_id,
                                                   strlen (
@@ -1381,18 +1516,6 @@ authorize_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
     return;
   }
 
-  if (NULL == handle->ego_head)
-  {
-    handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
-    handle->edesc = GNUNET_strdup ("Egos are missing");
-    handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
-    GNUNET_SCHEDULER_add_now (&do_error, handle);
-    return;
-  }
-
-  handle->ego_entry = handle->ego_head;
-  handle->priv_key =
-    *GNUNET_IDENTITY_ego_get_private_key (handle->ego_head->ego);
   // If we know this identity, translated the corresponding TLD
   // TODO: We might want to have a reverse lookup functionality for TLDs?
   for (tmp_ego = handle->ego_head; NULL != tmp_ego; tmp_ego = tmp_ego->next)
@@ -1405,6 +1528,9 @@ authorize_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
       handle->ego_entry = handle->ego_tail;
     }
   }
+  handle->oidc->scope = get_url_parameter_copy (handle, OIDC_SCOPE_KEY);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Scope: %s\n",GNUNET_strdup (
+                handle->oidc->scope));
   if (NULL == handle->tld)
     GNUNET_CONFIGURATION_iterate_section_values (cfg, "gns", tld_iter, handle);
   if (NULL == handle->tld)
@@ -1412,6 +1538,7 @@ authorize_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
   GNUNET_SCHEDULER_add_now (&build_authz_response, handle);
 }
 
+
 /**
  * Combines an identity with a login time and responds OK to login request
  *
@@ -1435,6 +1562,7 @@ login_cont (struct GNUNET_REST_RequestHandle *con_handle,
   json_error_t error;
   json_t *identity;
   char term_data[handle->rest_handle->data_size + 1];
+
   term_data[handle->rest_handle->data_size] = '\0';
   GNUNET_memcpy (term_data,
                  handle->rest_handle->data,
@@ -1481,6 +1609,7 @@ login_cont (struct GNUNET_REST_RequestHandle *con_handle,
   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
 }
 
+
 static int
 check_authorization (struct RequestHandle *handle,
                      struct GNUNET_CRYPTO_EcdsaPublicKey *cid)
@@ -1497,7 +1626,7 @@ check_authorization (struct RequestHandle *handle,
                       strlen (OIDC_AUTHORIZATION_HEADER_KEY),
                       &cache_key);
   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
-                                                             ->header_param_map,
+                                                           ->header_param_map,
                                                            &cache_key))
   {
     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
@@ -1511,7 +1640,7 @@ check_authorization (struct RequestHandle *handle,
 
   // split header in "Basic" and [content]
   credentials = strtok (authorization, " ");
-  if (0 != strcmp ("Basic", credentials))
+  if ((NULL == credentials) || (0 != strcmp ("Basic", credentials)))
   {
     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
     handle->response_code = MHD_HTTP_UNAUTHORIZED;
@@ -1577,8 +1706,7 @@ check_authorization (struct RequestHandle *handle,
   }
 
   // check client_id
-  for (handle->ego_entry = handle->ego_head;
-       NULL != handle->ego_entry;
+  for (handle->ego_entry = handle->ego_head; NULL != handle->ego_entry;
        handle->ego_entry = handle->ego_entry->next)
   {
     if (0 == strcmp (handle->ego_entry->keystring, client_id))
@@ -1594,12 +1722,13 @@ check_authorization (struct RequestHandle *handle,
   GNUNET_STRINGS_string_to_data (client_id,
                                  strlen (client_id),
                                  cid,
-                                 sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
+                                 sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey));
 
   GNUNET_free (basic_authorization);
   return GNUNET_OK;
 }
 
+
 const struct EgoEntry *
 find_ego (struct RequestHandle *handle,
           struct GNUNET_CRYPTO_EcdsaPublicKey *test_key)
@@ -1617,6 +1746,7 @@ find_ego (struct RequestHandle *handle,
   return NULL;
 }
 
+
 static void
 persist_access_token (const struct RequestHandle *handle,
                       const char *access_token,
@@ -1628,13 +1758,15 @@ persist_access_token (const struct RequestHandle *handle,
   GNUNET_CRYPTO_hash (access_token, strlen (access_token), &hc);
   ticketbuf = GNUNET_new (struct GNUNET_RECLAIM_Ticket);
   *ticketbuf = *ticket;
-  GNUNET_CONTAINER_multihashmap_put (
-    OIDC_access_token_map,
-    &hc,
-    ticketbuf,
-    GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
+  GNUNET_assert (GNUNET_SYSERR !=
+                 GNUNET_CONTAINER_multihashmap_put (
+                   OIDC_access_token_map,
+                   &hc,
+                   ticketbuf,
+                   GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
 }
 
+
 /**
  * Responds to token url-encoded POST request
  *
@@ -1650,7 +1782,8 @@ token_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
   struct RequestHandle *handle = cls;
   const struct EgoEntry *ego_entry;
   struct GNUNET_TIME_Relative expiration_time;
-  struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *cl;
+  struct GNUNET_RECLAIM_AttributeList *cl;
+  struct GNUNET_RECLAIM_AttestationList *al;
   struct GNUNET_RECLAIM_Ticket ticket;
   struct GNUNET_CRYPTO_EcdsaPublicKey cid;
   const struct GNUNET_CRYPTO_EcdsaPrivateKey *privkey;
@@ -1663,6 +1796,7 @@ token_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
   char *access_token;
   char *jwt_secret;
   char *nonce;
+  char *code_verifier;
 
   /*
    * Check Authorization
@@ -1699,10 +1833,11 @@ token_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
   {
     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_UNSUPPORTED_GRANT_TYPE);
     handle->response_code = MHD_HTTP_BAD_REQUEST;
+    GNUNET_free (grant_type);
     GNUNET_SCHEDULER_add_now (&do_error, handle);
     return;
   }
-
+  GNUNET_free (grant_type);
   // REQUIRED code
   code = get_url_parameter_copy (handle, OIDC_CODE_KEY);
   if (NULL == code)
@@ -1719,18 +1854,33 @@ token_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
     handle->edesc = GNUNET_strdup ("Unknown client");
     handle->response_code = MHD_HTTP_BAD_REQUEST;
+    GNUNET_free (code);
     GNUNET_SCHEDULER_add_now (&do_error, handle);
+    return;
   }
   privkey = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
+
+  // REQUIRED code verifier
+  code_verifier = get_url_parameter_copy (handle, OIDC_CODE_VERIFIER_KEY);
+  if (NULL == code_verifier)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "OAuth authorization request does not contain PKCE parameters!\n");
+
+  }
+
   // decode code
-  if (GNUNET_OK != OIDC_parse_authz_code (privkey, code, &ticket, &cl, &nonce))
+  if (GNUNET_OK != OIDC_parse_authz_code (privkey, code, code_verifier, &ticket,
+                                          &cl, &al, &nonce))
   {
     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
     handle->edesc = GNUNET_strdup ("invalid code");
     handle->response_code = MHD_HTTP_BAD_REQUEST;
+    GNUNET_free (code);
     GNUNET_SCHEDULER_add_now (&do_error, handle);
     return;
   }
+  GNUNET_free (code);
 
   // create jwt
   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (cfg,
@@ -1761,6 +1911,7 @@ token_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
   id_token = OIDC_id_token_new (&ticket.audience,
                                 &ticket.identity,
                                 cl,
+                                al,
                                 &expiration_time,
                                 (NULL != nonce) ? nonce : NULL,
                                 jwt_secret);
@@ -1776,38 +1927,103 @@ token_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
   MHD_add_response_header (resp, "Pragma", "no-cache");
   MHD_add_response_header (resp, "Content-Type", "application/json");
   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
-  GNUNET_RECLAIM_ATTRIBUTE_list_destroy (cl);
+  GNUNET_RECLAIM_attribute_list_destroy (cl);
+  GNUNET_RECLAIM_attestation_list_destroy (al);
   GNUNET_free (access_token);
   GNUNET_free (json_response);
   GNUNET_free (id_token);
   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
 }
 
+
 /**
  * Collects claims and stores them in handle
  */
 static void
 consume_ticket (void *cls,
                 const struct GNUNET_CRYPTO_EcdsaPublicKey *identity,
-                const struct GNUNET_RECLAIM_ATTRIBUTE_Claim *attr)
+                const struct GNUNET_RECLAIM_Attribute *attr,
+                const struct GNUNET_RECLAIM_Attestation *attest)
 {
   struct RequestHandle *handle = cls;
-  char *tmp_value;
-  json_t *value;
-
   if (NULL == identity)
   {
     GNUNET_SCHEDULER_add_now (&return_userinfo_response, handle);
     return;
   }
-  tmp_value = GNUNET_RECLAIM_ATTRIBUTE_value_to_string (attr->type,
-                                                        attr->data,
-                                                        attr->data_size);
-  value = json_string (tmp_value);
-  json_object_set_new (handle->oidc->response, attr->name, value);
-  GNUNET_free (tmp_value);
+  if (GNUNET_YES == GNUNET_RECLAIM_id_is_zero (&attr->attestation))
+  {
+    char *tmp_value;
+    json_t *value;
+    tmp_value = GNUNET_RECLAIM_attribute_value_to_string (attr->type,
+                                                          attr->data,
+                                                          attr->data_size);
+    value = json_string (tmp_value);
+    json_object_set_new (handle->oidc->response, attr->name, value);
+    GNUNET_free (tmp_value);
+    return;
+  }
+  json_t *claim_sources;
+  json_t *claim_sources_jwt;
+  json_t *claim_names;
+  char *attest_val_str;
+  claim_sources = json_object_get (handle->oidc->response,"_claim_sources");
+  claim_names = json_object_get (handle->oidc->response,"_claim_names");
+  attest_val_str =
+    GNUNET_RECLAIM_attestation_value_to_string (attest->type,
+                                                attest->data,
+                                                attest->data_size);
+  if ((NULL == claim_sources) && (NULL == claim_names) )
+  {
+    claim_sources = json_object ();
+    claim_names = json_object ();
+  }
+  char *source_name;
+  int i = 0;
+  GNUNET_asprintf (&source_name, "src%d", i);
+  while (NULL != (claim_sources_jwt = json_object_get (claim_sources,
+                                                       source_name)))
+  {
+    if (0 == strcmp (json_string_value (json_object_get (claim_sources_jwt,
+                                                         "JWT")),
+                     attest_val_str))
+    {
+      // Adapt only the claim names
+      json_object_set_new (claim_names, attr->data,
+                           json_string (source_name));
+      json_object_set (handle->oidc->response,
+                       "_claim_names", claim_names);
+      break;
+    }
+    i++;
+    GNUNET_free (source_name);
+    GNUNET_asprintf (&source_name, "src%d", i);
+  }
+
+  // Create new one
+  if (NULL == claim_sources_jwt)
+  {
+    claim_sources_jwt = json_object ();
+    // Set the JWT for names
+    json_object_set_new (claim_names, attr->data,
+                         json_string (source_name));
+    // Set the JWT for the inner source
+    json_object_set_new (claim_sources_jwt, "JWT",
+                         json_string (attest_val_str));
+    // Set the JWT for the source
+    json_object_set_new (claim_sources, source_name, claim_sources_jwt);
+    // Set as claims
+    json_object_set (handle->oidc->response, "_claim_names", claim_names);
+    json_object_set (handle->oidc->response, "_claim_sources",claim_sources);
+  }
+
+  json_decref (claim_sources);
+  json_decref (claim_names);
+  json_decref (claim_sources_jwt);
+  GNUNET_free (attest_val_str);
 }
 
+
 /**
  * Responds to userinfo GET and url-encoded POST request
  *
@@ -1835,7 +2051,7 @@ userinfo_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
                       strlen (OIDC_AUTHORIZATION_HEADER_KEY),
                       &cache_key);
   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
-                                                             ->header_param_map,
+                                                           ->header_param_map,
                                                            &cache_key))
   {
     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
@@ -1851,7 +2067,8 @@ userinfo_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
   // split header in "Bearer" and access_token
   authorization = GNUNET_strdup (authorization);
   authorization_type = strtok (authorization, delimiter);
-  if (0 != strcmp ("Bearer", authorization_type))
+  if ((NULL == authorization_type) ||
+      (0 != strcmp ("Bearer", authorization_type)))
   {
     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
     handle->edesc = GNUNET_strdup ("No Access Token");
@@ -1924,16 +2141,16 @@ init_cont (struct RequestHandle *handle)
 {
   struct GNUNET_REST_RequestHandlerError err;
   static const struct GNUNET_REST_RequestHandler handlers[] =
-    {{MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_AUTHORIZE, &authorize_endpoint},
-     {MHD_HTTP_METHOD_POST,
+  { { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_AUTHORIZE, &authorize_endpoint },
+    MHD_HTTP_METHOD_POST,
       GNUNET_REST_API_NS_AUTHORIZE,
-      &authorize_endpoint}, // url-encoded
-     {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_LOGIN, &login_cont},
-     {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_TOKEN, &token_endpoint},
-     {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_USERINFO, &userinfo_endpoint},
-     {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_USERINFO, &userinfo_endpoint},
-     {MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_OIDC, &options_cont},
-     GNUNET_REST_HANDLER_END};
+      &authorize_endpoint },   // url-encoded
+    { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_LOGIN, &login_cont },
+    { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_TOKEN, &token_endpoint },
+    { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_USERINFO, &userinfo_endpoint },
+    { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_USERINFO, &userinfo_endpoint },
+    { MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_OIDC, &options_cont },
+    GNUNET_REST_HANDLER_END };
 
   if (GNUNET_NO ==
       GNUNET_REST_handle_request (handle->rest_handle, handlers, &err, handle))
@@ -1943,6 +2160,7 @@ init_cont (struct RequestHandle *handle)
   }
 }
 
+
 /**
  * If listing is enabled, prints information about the egos.
  *
@@ -1992,7 +2210,9 @@ list_ego (void *cls,
     init_cont (handle);
     return;
   }
+  GNUNET_assert (NULL != ego);
   if (ID_REST_STATE_INIT == handle->state)
+
   {
     ego_entry = GNUNET_new (struct EgoEntry);
     GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
@@ -2047,12 +2267,14 @@ list_ego (void *cls,
   }
 }
 
+
 static void
 rest_identity_process_request (struct GNUNET_REST_RequestHandle *rest_handle,
                                GNUNET_REST_ResultProcessor proc,
                                void *proc_cls)
 {
   struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
+
   handle->oidc = GNUNET_new (struct OIDC_Variables);
   if (NULL == OIDC_cookie_jar_map)
     OIDC_cookie_jar_map = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
@@ -2078,6 +2300,7 @@ rest_identity_process_request (struct GNUNET_REST_RequestHandle *rest_handle,
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connected\n");
 }
 
+
 /**
  * Entry point for the plugin.
  *
@@ -2093,7 +2316,7 @@ libgnunet_plugin_rest_openid_connect_init (void *cls)
   cfg = cls;
   if (NULL != plugin.cfg)
     return NULL; /* can only initialize once! */
-  memset (&plugin, 0, sizeof (struct Plugin));
+  memset (&plugin, 0, sizeof(struct Plugin));
   plugin.cfg = cfg;
   api = GNUNET_new (struct GNUNET_REST_Plugin);
   api->cls = &plugin;
@@ -2124,6 +2347,7 @@ libgnunet_plugin_rest_openid_connect_done (void *cls)
 {
   struct GNUNET_REST_Plugin *api = cls;
   struct Plugin *plugin = api->cls;
+
   plugin->cfg = NULL;
 
   struct GNUNET_CONTAINER_MultiHashMapIterator *hashmap_it;
@@ -2133,6 +2357,7 @@ libgnunet_plugin_rest_openid_connect_done (void *cls)
   while (GNUNET_YES ==
          GNUNET_CONTAINER_multihashmap_iterator_next (hashmap_it, NULL, value))
     GNUNET_free_non_null (value);
+  GNUNET_CONTAINER_multihashmap_iterator_destroy (hashmap_it);
   GNUNET_CONTAINER_multihashmap_destroy (OIDC_cookie_jar_map);
 
   hashmap_it =
@@ -2149,4 +2374,5 @@ libgnunet_plugin_rest_openid_connect_done (void *cls)
   return NULL;
 }
 
+
 /* end of plugin_rest_openid_connect.c */