use NULL value in load_path_suffix to NOT load any files
[oweals/gnunet.git] / src / reclaim / plugin_rest_openid_connect.c
1 /*
2    This file is part of GNUnet.
3    Copyright (C) 2012-2018 GNUnet e.V.
4
5    GNUnet is free software: you can redistribute it and/or modify it
6    under the terms of the GNU Affero General Public License as published
7    by the Free Software Foundation, either version 3 of the License,
8    or (at your option) any later version.
9
10    GNUnet is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Affero General Public License for more details.
14
15    You should have received a copy of the GNU Affero General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18    SPDX-License-Identifier: AGPL3.0-or-later
19  */
20 /**
21  * @author Martin Schanzenbach
22  * @author Philippe Buschmann
23  * @file identity/plugin_rest_openid_connect.c
24  * @brief GNUnet Namestore REST plugin
25  *
26  */
27 #include "platform.h"
28 #include <inttypes.h>
29 #include <jansson.h>
30
31 #include "gnunet_gns_service.h"
32 #include "gnunet_gnsrecord_lib.h"
33 #include "gnunet_identity_service.h"
34 #include "gnunet_namestore_service.h"
35 #include "gnunet_reclaim_attribute_lib.h"
36 #include "gnunet_reclaim_service.h"
37 #include "gnunet_rest_lib.h"
38 #include "gnunet_rest_plugin.h"
39 #include "gnunet_signatures.h"
40 #include "microhttpd.h"
41 #include "oidc_helper.h"
42 /**
43  * REST root namespace
44  */
45 #define GNUNET_REST_API_NS_OIDC "/openid"
46
47 /**
48  * Authorize endpoint
49  */
50 #define GNUNET_REST_API_NS_AUTHORIZE "/openid/authorize"
51
52 /**
53  * Token endpoint
54  */
55 #define GNUNET_REST_API_NS_TOKEN "/openid/token"
56
57 /**
58  * UserInfo endpoint
59  */
60 #define GNUNET_REST_API_NS_USERINFO "/openid/userinfo"
61
62 /**
63  * Login namespace
64  */
65 #define GNUNET_REST_API_NS_LOGIN "/openid/login"
66
67 /**
68  * State while collecting all egos
69  */
70 #define ID_REST_STATE_INIT 0
71
72 /**
73  * Done collecting egos
74  */
75 #define ID_REST_STATE_POST_INIT 1
76
77 /**
78  * OIDC grant_type key
79  */
80 #define OIDC_GRANT_TYPE_KEY "grant_type"
81
82 /**
83  * OIDC grant_type key
84  */
85 #define OIDC_GRANT_TYPE_VALUE "authorization_code"
86
87 /**
88  * OIDC code key
89  */
90 #define OIDC_CODE_KEY "code"
91
92 /**
93  * OIDC response_type key
94  */
95 #define OIDC_RESPONSE_TYPE_KEY "response_type"
96
97 /**
98  * OIDC client_id key
99  */
100 #define OIDC_CLIENT_ID_KEY "client_id"
101
102 /**
103  * OIDC scope key
104  */
105 #define OIDC_SCOPE_KEY "scope"
106
107 /**
108  * OIDC redirect_uri key
109  */
110 #define OIDC_REDIRECT_URI_KEY "redirect_uri"
111
112 /**
113  * OIDC state key
114  */
115 #define OIDC_STATE_KEY "state"
116
117 /**
118  * OIDC nonce key
119  */
120 #define OIDC_NONCE_KEY "nonce"
121
122 /**
123  * OIDC claims key
124  */
125 #define OIDC_CLAIMS_KEY "claims"
126
127 /**
128  * OIDC PKCE code challenge
129  */
130 #define OIDC_CODE_CHALLENGE_KEY "code_challenge"
131
132 /**
133  * OIDC PKCE code verifier
134  */
135 #define OIDC_CODE_VERIFIER_KEY "code_verifier"
136
137 /**
138  * OIDC cookie expiration (in seconds)
139  */
140 #define OIDC_COOKIE_EXPIRATION 3
141
142 /**
143  * OIDC cookie header key
144  */
145 #define OIDC_COOKIE_HEADER_KEY "cookie"
146
147 /**
148  * OIDC cookie header information key
149  */
150 #define OIDC_AUTHORIZATION_HEADER_KEY "authorization"
151
152 /**
153  * OIDC cookie header information key
154  */
155 #define OIDC_COOKIE_HEADER_INFORMATION_KEY "Identity="
156
157 /**
158  * OIDC cookie header if user cancelled
159  */
160 #define OIDC_COOKIE_HEADER_ACCESS_DENIED "Identity=Denied"
161
162 /**
163  * OIDC expected response_type while authorizing
164  */
165 #define OIDC_EXPECTED_AUTHORIZATION_RESPONSE_TYPE "code"
166
167 /**
168  * OIDC expected scope part while authorizing
169  */
170 #define OIDC_EXPECTED_AUTHORIZATION_SCOPE "openid"
171
172 /**
173  * OIDC error key for invalid client
174  */
175 #define OIDC_ERROR_KEY_INVALID_CLIENT "invalid_client"
176
177 /**
178  * OIDC error key for invalid scopes
179  */
180 #define OIDC_ERROR_KEY_INVALID_SCOPE "invalid_scope"
181
182 /**
183  * OIDC error key for invalid requests
184  */
185 #define OIDC_ERROR_KEY_INVALID_REQUEST "invalid_request"
186
187 /**
188  * OIDC error key for invalid tokens
189  */
190 #define OIDC_ERROR_KEY_INVALID_TOKEN "invalid_token"
191
192 /**
193  * OIDC error key for invalid cookies
194  */
195 #define OIDC_ERROR_KEY_INVALID_COOKIE "invalid_cookie"
196
197 /**
198  * OIDC error key for generic server errors
199  */
200 #define OIDC_ERROR_KEY_SERVER_ERROR "server_error"
201
202 /**
203  * OIDC error key for unsupported grants
204  */
205 #define OIDC_ERROR_KEY_UNSUPPORTED_GRANT_TYPE "unsupported_grant_type"
206
207 /**
208  * OIDC error key for unsupported response types
209  */
210 #define OIDC_ERROR_KEY_UNSUPPORTED_RESPONSE_TYPE "unsupported_response_type"
211
212 /**
213  * OIDC error key for unauthorized clients
214  */
215 #define OIDC_ERROR_KEY_UNAUTHORIZED_CLIENT "unauthorized_client"
216
217 /**
218  * OIDC error key for denied access
219  */
220 #define OIDC_ERROR_KEY_ACCESS_DENIED "access_denied"
221
222
223 /**
224  * OIDC ignored parameter array
225  */
226 static char *OIDC_ignored_parameter_array[] = { "display",
227                                                 "prompt",
228                                                 "ui_locales",
229                                                 "response_mode",
230                                                 "id_token_hint",
231                                                 "login_hint",
232                                                 "acr_values" };
233
234 /**
235  * OIDC Hash map that keeps track of issued cookies
236  */
237 struct GNUNET_CONTAINER_MultiHashMap *OIDC_cookie_jar_map;
238
239 /**
240  * Hash map that links the issued access token to the corresponding ticket and
241  * ego
242  */
243 struct GNUNET_CONTAINER_MultiHashMap *OIDC_access_token_map;
244
245 /**
246  * The configuration handle
247  */
248 const struct GNUNET_CONFIGURATION_Handle *cfg;
249
250 /**
251  * HTTP methods allows for this plugin
252  */
253 static char *allow_methods;
254
255 /**
256  * @brief struct returned by the initialization function of the plugin
257  */
258 struct Plugin
259 {
260   const struct GNUNET_CONFIGURATION_Handle *cfg;
261 };
262
263 /**
264  * OIDC needed variables
265  */
266 struct OIDC_Variables
267 {
268   /**
269    * The RP client public key
270    */
271   struct GNUNET_CRYPTO_EcdsaPublicKey client_pkey;
272
273   /**
274    * The OIDC client id of the RP
275    */
276   char *client_id;
277
278   /**
279    * The OIDC redirect uri
280    */
281   char *redirect_uri;
282
283   /**
284    * The list of oidc scopes
285    */
286   char *scope;
287
288   /**
289    * The OIDC state
290    */
291   char *state;
292
293   /**
294    * The OIDC nonce
295    */
296   char *nonce;
297
298   /**
299    * The OIDC claims
300    */
301   char *claims;
302
303   /**
304    * The OIDC response type
305    */
306   char *response_type;
307
308   /**
309    * The identity chosen by the user to login
310    */
311   char *login_identity;
312
313   /**
314    * User cancelled authorization/login
315    */
316   int user_cancelled;
317
318   /**
319    * The PKCE code_challenge
320    */
321   char *code_challenge;
322
323   /**
324    * The PKCE code_verifier
325    */
326   char *code_verifier;
327
328   /**
329    * The response JSON
330    */
331   json_t *response;
332 };
333
334 /**
335  * The ego list
336  */
337 struct EgoEntry
338 {
339   /**
340    * DLL
341    */
342   struct EgoEntry *next;
343
344   /**
345    * DLL
346    */
347   struct EgoEntry *prev;
348
349   /**
350    * Ego Identifier
351    */
352   char *identifier;
353
354   /**
355    * Public key string
356    */
357   char *keystring;
358
359   /**
360    * The Ego
361    */
362   struct GNUNET_IDENTITY_Ego *ego;
363 };
364
365
366 struct RequestHandle
367 {
368   /**
369    * Ego list
370    */
371   struct EgoEntry *ego_head;
372
373   /**
374    * Ego list
375    */
376   struct EgoEntry *ego_tail;
377
378   /**
379    * Selected ego
380    */
381   struct EgoEntry *ego_entry;
382
383   /**
384    * Pointer to ego private key
385    */
386   struct GNUNET_CRYPTO_EcdsaPrivateKey priv_key;
387
388   /**
389    * OIDC variables
390    */
391   struct OIDC_Variables *oidc;
392
393   /**
394    * The processing state
395    */
396   int state;
397
398   /**
399    * Handle to Identity service.
400    */
401   struct GNUNET_IDENTITY_Handle *identity_handle;
402
403   /**
404    * Rest connection
405    */
406   struct GNUNET_REST_RequestHandle *rest_handle;
407
408   /**
409    * GNS handle
410    */
411   struct GNUNET_GNS_Handle *gns_handle;
412
413   /**
414    * GNS lookup op
415    */
416   struct GNUNET_GNS_LookupRequest *gns_op;
417
418   /**
419    * Handle to NAMESTORE
420    */
421   struct GNUNET_NAMESTORE_Handle *namestore_handle;
422
423   /**
424    * Iterator for NAMESTORE
425    */
426   struct GNUNET_NAMESTORE_ZoneIterator *namestore_handle_it;
427
428   /**
429    * Attribute claim list
430    */
431   struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *attr_list;
432
433   /**
434    * IDENTITY Operation
435    */
436   struct GNUNET_IDENTITY_Operation *op;
437
438   /**
439    * Identity Provider
440    */
441   struct GNUNET_RECLAIM_Handle *idp;
442
443   /**
444    * Idp Operation
445    */
446   struct GNUNET_RECLAIM_Operation *idp_op;
447
448   /**
449    * Attribute iterator
450    */
451   struct GNUNET_RECLAIM_AttributeIterator *attr_it;
452
453   /**
454    * Ticket iterator
455    */
456   struct GNUNET_RECLAIM_TicketIterator *ticket_it;
457
458   /**
459    * A ticket
460    */
461   struct GNUNET_RECLAIM_Ticket ticket;
462
463   /**
464    * Desired timeout for the lookup (default is no timeout).
465    */
466   struct GNUNET_TIME_Relative timeout;
467
468   /**
469    * ID of a task associated with the resolution process.
470    */
471   struct GNUNET_SCHEDULER_Task *timeout_task;
472
473   /**
474    * The plugin result processor
475    */
476   GNUNET_REST_ResultProcessor proc;
477
478   /**
479    * The closure of the result processor
480    */
481   void *proc_cls;
482
483   /**
484    * The url
485    */
486   char *url;
487
488   /**
489    * The tld for redirect
490    */
491   char *tld;
492
493   /**
494    * The redirect prefix
495    */
496   char *redirect_prefix;
497
498   /**
499    * The redirect suffix
500    */
501   char *redirect_suffix;
502
503   /**
504    * Error response message
505    */
506   char *emsg;
507
508   /**
509    * Error response description
510    */
511   char *edesc;
512
513   /**
514    * Reponse code
515    */
516   int response_code;
517 };
518
519 /**
520  * Cleanup lookup handle
521  * @param handle Handle to clean up
522  */
523 static void
524 cleanup_handle (struct RequestHandle *handle)
525 {
526   struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *claim_entry;
527   struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *claim_tmp;
528   struct EgoEntry *ego_entry;
529   struct EgoEntry *ego_tmp;
530
531   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up\n");
532   if (NULL != handle->timeout_task)
533     GNUNET_SCHEDULER_cancel (handle->timeout_task);
534   if (NULL != handle->identity_handle)
535     GNUNET_IDENTITY_disconnect (handle->identity_handle);
536   if (NULL != handle->attr_it)
537     GNUNET_RECLAIM_get_attributes_stop (handle->attr_it);
538   if (NULL != handle->ticket_it)
539     GNUNET_RECLAIM_ticket_iteration_stop (handle->ticket_it);
540   if (NULL != handle->idp)
541     GNUNET_RECLAIM_disconnect (handle->idp);
542   GNUNET_free_non_null (handle->url);
543   GNUNET_free_non_null (handle->tld);
544   GNUNET_free_non_null (handle->redirect_prefix);
545   GNUNET_free_non_null (handle->redirect_suffix);
546   GNUNET_free_non_null (handle->emsg);
547   GNUNET_free_non_null (handle->edesc);
548   if (NULL != handle->gns_op)
549     GNUNET_GNS_lookup_cancel (handle->gns_op);
550   if (NULL != handle->gns_handle)
551     GNUNET_GNS_disconnect (handle->gns_handle);
552
553   if (NULL != handle->namestore_handle)
554     GNUNET_NAMESTORE_disconnect (handle->namestore_handle);
555   if (NULL != handle->oidc)
556   {
557     GNUNET_free_non_null (handle->oidc->client_id);
558     GNUNET_free_non_null (handle->oidc->login_identity);
559     GNUNET_free_non_null (handle->oidc->nonce);
560     GNUNET_free_non_null (handle->oidc->redirect_uri);
561     GNUNET_free_non_null (handle->oidc->response_type);
562     GNUNET_free_non_null (handle->oidc->scope);
563     GNUNET_free_non_null (handle->oidc->state);
564     json_decref (handle->oidc->response);
565     GNUNET_free (handle->oidc);
566   }
567   if (NULL != handle->attr_list)
568   {
569     for (claim_entry = handle->attr_list->list_head; NULL != claim_entry;)
570     {
571       claim_tmp = claim_entry;
572       claim_entry = claim_entry->next;
573       if (NULL != claim_tmp->claim)
574         GNUNET_free (claim_tmp->claim);
575       if (NULL != claim_tmp->attest)
576         GNUNET_free (claim_tmp->attest);
577       if (NULL != claim_tmp->reference)
578         GNUNET_free (claim_tmp->reference);
579       GNUNET_free (claim_tmp);
580     }
581     GNUNET_free (handle->attr_list);
582   }
583   for (ego_entry = handle->ego_head; NULL != ego_entry;)
584   {
585     ego_tmp = ego_entry;
586     ego_entry = ego_entry->next;
587     GNUNET_free (ego_tmp->identifier);
588     GNUNET_free (ego_tmp->keystring);
589     GNUNET_free (ego_tmp);
590   }
591   GNUNET_free (handle);
592 }
593
594
595 static void
596 cleanup_handle_delayed (void *cls)
597 {
598   cleanup_handle (cls);
599 }
600
601
602 /**
603  * Task run on error, sends error message.  Cleans up everything.
604  *
605  * @param cls the `struct RequestHandle`
606  */
607 static void
608 do_error (void *cls)
609 {
610   struct RequestHandle *handle = cls;
611   struct MHD_Response *resp;
612   char *json_error;
613
614   GNUNET_asprintf (&json_error,
615                    "{ \"error\" : \"%s\", \"error_description\" : \"%s\"%s%s%s}",
616                    handle->emsg,
617                    (NULL != handle->edesc) ? handle->edesc : "",
618                    (NULL != handle->oidc->state) ? ", \"state\":\"" : "",
619                    (NULL != handle->oidc->state) ? handle->oidc->state : "",
620                    (NULL != handle->oidc->state) ? "\"" : "");
621   if (0 == handle->response_code)
622     handle->response_code = MHD_HTTP_BAD_REQUEST;
623   resp = GNUNET_REST_create_response (json_error);
624   if (MHD_HTTP_UNAUTHORIZED == handle->response_code)
625     MHD_add_response_header (resp, MHD_HTTP_HEADER_WWW_AUTHENTICATE, "Basic");
626   MHD_add_response_header (resp,
627                            MHD_HTTP_HEADER_CONTENT_TYPE,
628                            "application/json");
629   handle->proc (handle->proc_cls, resp, handle->response_code);
630   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
631   GNUNET_free (json_error);
632 }
633
634
635 /**
636  * Task run on error in userinfo endpoint, sends error header. Cleans up
637  * everything
638  *
639  * @param cls the `struct RequestHandle`
640  */
641 static void
642 do_userinfo_error (void *cls)
643 {
644   struct RequestHandle *handle = cls;
645   struct MHD_Response *resp;
646   char *error;
647
648   GNUNET_asprintf (&error,
649                    "error=\"%s\", error_description=\"%s\"",
650                    handle->emsg,
651                    (NULL != handle->edesc) ? handle->edesc : "");
652   resp = GNUNET_REST_create_response ("");
653   MHD_add_response_header (resp, MHD_HTTP_HEADER_WWW_AUTHENTICATE, "Bearer");
654   handle->proc (handle->proc_cls, resp, handle->response_code);
655   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
656   GNUNET_free (error);
657 }
658
659
660 /**
661  * Task run on error, sends error message and redirects. Cleans up everything.
662  *
663  * @param cls the `struct RequestHandle`
664  */
665 static void
666 do_redirect_error (void *cls)
667 {
668   struct RequestHandle *handle = cls;
669   struct MHD_Response *resp;
670   char *redirect;
671
672   GNUNET_asprintf (&redirect,
673                    "%s?error=%s&error_description=%s%s%s",
674                    handle->oidc->redirect_uri,
675                    handle->emsg,
676                    handle->edesc,
677                    (NULL != handle->oidc->state) ? "&state=" : "",
678                    (NULL != handle->oidc->state) ? handle->oidc->state : "");
679   resp = GNUNET_REST_create_response ("");
680   MHD_add_response_header (resp, "Location", redirect);
681   handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
682   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
683   GNUNET_free (redirect);
684 }
685
686
687 /**
688  * Task run on timeout, sends error message.  Cleans up everything.
689  *
690  * @param cls the `struct RequestHandle`
691  */
692 static void
693 do_timeout (void *cls)
694 {
695   struct RequestHandle *handle = cls;
696
697   handle->timeout_task = NULL;
698   do_error (handle);
699 }
700
701
702 /**
703  * Return attributes for claim
704  *
705  * @param cls the request handle
706  */
707 static void
708 return_userinfo_response (void *cls)
709 {
710   char *result_str;
711   struct RequestHandle *handle = cls;
712   struct MHD_Response *resp;
713
714   result_str = json_dumps (handle->oidc->response, 0);
715   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,"ID-Token: %s\n",result_str);
716   resp = GNUNET_REST_create_response (result_str);
717   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
718   GNUNET_free (result_str);
719   cleanup_handle (handle);
720 }
721
722
723 /**
724  * Respond to OPTIONS request
725  *
726  * @param con_handle the connection handle
727  * @param url the url
728  * @param cls the RequestHandle
729  */
730 static void
731 options_cont (struct GNUNET_REST_RequestHandle *con_handle,
732               const char *url,
733               void *cls)
734 {
735   struct MHD_Response *resp;
736   struct RequestHandle *handle = cls;
737
738   // For now, independent of path return all options
739   resp = GNUNET_REST_create_response (NULL);
740   MHD_add_response_header (resp, "Access-Control-Allow-Methods", allow_methods);
741   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
742   cleanup_handle (handle);
743   return;
744 }
745
746
747 /**
748  * Interprets cookie header and pass its identity keystring to handle
749  */
750 static void
751 cookie_identity_interpretation (struct RequestHandle *handle)
752 {
753   struct GNUNET_HashCode cache_key;
754   char *cookies;
755   struct GNUNET_TIME_Absolute current_time, *relog_time;
756   char delimiter[] = "; ";
757   char *tmp_cookies;
758   char *token;
759   char *value;
760
761   // gets identity of login try with cookie
762   GNUNET_CRYPTO_hash (OIDC_COOKIE_HEADER_KEY,
763                       strlen (OIDC_COOKIE_HEADER_KEY),
764                       &cache_key);
765   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
766                                                            ->header_param_map,
767                                                            &cache_key))
768   {
769     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "No cookie found\n");
770     return;
771   }
772   // splits cookies and find 'Identity' cookie
773   tmp_cookies =
774     GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->header_param_map,
775                                        &cache_key);
776   cookies = GNUNET_strdup (tmp_cookies);
777   token = strtok (cookies, delimiter);
778   handle->oidc->user_cancelled = GNUNET_NO;
779   handle->oidc->login_identity = NULL;
780   if (NULL == token)
781   {
782     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
783                 "Unable to parse cookie: %s\n",
784                 cookies);
785     GNUNET_free (cookies);
786     return;
787   }
788
789   while (NULL != token)
790   {
791     if (0 == strcmp (token, OIDC_COOKIE_HEADER_ACCESS_DENIED))
792     {
793       handle->oidc->user_cancelled = GNUNET_YES;
794       GNUNET_free (cookies);
795       return;
796     }
797     if (NULL != strstr (token, OIDC_COOKIE_HEADER_INFORMATION_KEY))
798       break;
799     token = strtok (NULL, delimiter);
800   }
801   if (NULL == token)
802   {
803     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
804                 "No cookie value to process: %s\n",
805                 cookies);
806     GNUNET_free (cookies);
807     return;
808   }
809   GNUNET_CRYPTO_hash (token, strlen (token), &cache_key);
810   if (GNUNET_NO ==
811       GNUNET_CONTAINER_multihashmap_contains (OIDC_cookie_jar_map, &cache_key))
812   {
813     GNUNET_log (
814       GNUNET_ERROR_TYPE_WARNING,
815       "Found cookie `%s', but no corresponding expiration entry present...\n",
816       token);
817     GNUNET_free (cookies);
818     return;
819   }
820   relog_time =
821     GNUNET_CONTAINER_multihashmap_get (OIDC_cookie_jar_map, &cache_key);
822   current_time = GNUNET_TIME_absolute_get ();
823   // 30 min after old login -> redirect to login
824   if (current_time.abs_value_us > relog_time->abs_value_us)
825   {
826     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
827                 "Found cookie `%s', but it is expired.\n",
828                 token);
829     GNUNET_free (cookies);
830     return;
831   }
832   value = strtok (token, OIDC_COOKIE_HEADER_INFORMATION_KEY);
833   GNUNET_assert (NULL != value);
834   handle->oidc->login_identity = GNUNET_strdup (value);
835   GNUNET_free (cookies);
836 }
837
838
839 /**
840  * Redirects to login page stored in configuration file
841  */
842 static void
843 login_redirect (void *cls)
844 {
845   char *login_base_url;
846   char *new_redirect;
847   struct MHD_Response *resp;
848   struct RequestHandle *handle = cls;
849
850   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg,
851                                                           "reclaim-rest-plugin",
852                                                           "address",
853                                                           &login_base_url))
854   {
855     GNUNET_asprintf (&new_redirect,
856                      "%s?%s=%s&%s=%s&%s=%s&%s=%s&%s=%s&%s=%s&%s=%s&%s=%s",
857                      login_base_url,
858                      OIDC_RESPONSE_TYPE_KEY,
859                      handle->oidc->response_type,
860                      OIDC_CLIENT_ID_KEY,
861                      handle->oidc->client_id,
862                      OIDC_REDIRECT_URI_KEY,
863                      handle->oidc->redirect_uri,
864                      OIDC_SCOPE_KEY,
865                      handle->oidc->scope,
866                      OIDC_STATE_KEY,
867                      (NULL != handle->oidc->state) ? handle->oidc->state : "",
868                      OIDC_CODE_CHALLENGE_KEY,
869                      (NULL != handle->oidc->code_challenge) ?
870                      handle->oidc->code_challenge : "",
871                      OIDC_NONCE_KEY,
872                      (NULL != handle->oidc->nonce) ? handle->oidc->nonce : "",
873                      OIDC_CLAIMS_KEY,
874                      (NULL != handle->oidc->claims) ? handle->oidc->claims :
875                      "");
876     resp = GNUNET_REST_create_response ("");
877     MHD_add_response_header (resp, "Location", new_redirect);
878     GNUNET_free (login_base_url);
879   }
880   else
881   {
882     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
883     handle->edesc = GNUNET_strdup ("gnunet configuration failed");
884     handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
885     GNUNET_SCHEDULER_add_now (&do_error, handle);
886     return;
887   }
888   handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
889   GNUNET_free (new_redirect);
890   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
891 }
892
893
894 /**
895  * Does internal server error when iteration failed.
896  */
897 static void
898 oidc_iteration_error (void *cls)
899 {
900   struct RequestHandle *handle = cls;
901
902   handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
903   handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
904   GNUNET_SCHEDULER_add_now (&do_error, handle);
905 }
906
907
908 /**
909  * Issues ticket and redirects to relying party with the authorization code as
910  * parameter. Otherwise redirects with error
911  */
912 static void
913 oidc_ticket_issue_cb (void *cls, const struct GNUNET_RECLAIM_Ticket *ticket)
914 {
915   struct RequestHandle *handle = cls;
916   struct MHD_Response *resp;
917   char *ticket_str;
918   char *redirect_uri;
919   char *code_string;
920
921   handle->idp_op = NULL;
922   if (NULL == ticket)
923   {
924     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
925     handle->edesc = GNUNET_strdup ("Server cannot generate ticket.");
926     GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
927     return;
928   }
929   handle->ticket = *ticket;
930   ticket_str =
931     GNUNET_STRINGS_data_to_string_alloc (&handle->ticket,
932                                          sizeof(struct GNUNET_RECLAIM_Ticket));
933   // TODO change if more attributes are needed (see max_age)
934   code_string = OIDC_build_authz_code (&handle->priv_key,
935                                        &handle->ticket,
936                                        handle->attr_list,
937                                        handle->oidc->nonce,
938                                        handle->oidc->code_challenge);
939   if ((NULL != handle->redirect_prefix) && (NULL != handle->redirect_suffix) &&
940       (NULL != handle->tld))
941   {
942     GNUNET_asprintf (&redirect_uri,
943                      "%s.%s/%s?%s=%s&state=%s",
944                      handle->redirect_prefix,
945                      handle->tld,
946                      handle->redirect_suffix,
947                      handle->oidc->response_type,
948                      code_string,
949                      handle->oidc->state);
950   }
951   else
952   {
953     GNUNET_asprintf (&redirect_uri,
954                      "%s?%s=%s&state=%s",
955                      handle->oidc->redirect_uri,
956                      handle->oidc->response_type,
957                      code_string,
958                      handle->oidc->state);
959   }
960   resp = GNUNET_REST_create_response ("");
961   MHD_add_response_header (resp, "Location", redirect_uri);
962   handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
963   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
964   GNUNET_free (redirect_uri);
965   GNUNET_free (ticket_str);
966   GNUNET_free (code_string);
967 }
968
969
970 static void
971 oidc_collect_finished_cb (void *cls)
972 {
973   struct RequestHandle *handle = cls;
974
975   handle->attr_it = NULL;
976   handle->ticket_it = NULL;
977   if (NULL == handle->attr_list->list_head)
978   {
979     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_SCOPE);
980     handle->edesc = GNUNET_strdup ("The requested scope is not available.");
981     GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
982     return;
983   }
984   handle->idp_op = GNUNET_RECLAIM_ticket_issue (handle->idp,
985                                                 &handle->priv_key,
986                                                 &handle->oidc->client_pkey,
987                                                 handle->attr_list,
988                                                 &oidc_ticket_issue_cb,
989                                                 handle);
990 }
991
992
993 /**
994  * Collects all attributes/references for an ego if in scope parameter
995  */
996 static void
997 oidc_attr_collect (void *cls,
998                    const struct GNUNET_CRYPTO_EcdsaPublicKey *identity,
999                    const struct GNUNET_RECLAIM_ATTRIBUTE_Claim *attr,
1000                    const struct GNUNET_RECLAIM_ATTESTATION_Claim *attest,
1001                    const struct GNUNET_RECLAIM_ATTESTATION_REFERENCE *reference)
1002 {
1003   struct RequestHandle *handle = cls;
1004   struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *le;
1005   char *scope_variables;
1006   char *scope_variable;
1007   char delimiter[] = " ";
1008
1009   if ((NULL == attr) && (NULL == reference))
1010   {
1011     GNUNET_RECLAIM_get_attributes_next (handle->attr_it);
1012     return;
1013   }
1014   if (NULL != reference)
1015   {
1016     if ((NULL == reference->name) || (NULL == reference->reference_value))
1017     {
1018       return;
1019     }
1020     scope_variables = GNUNET_strdup (handle->oidc->scope);
1021     scope_variable = strtok (scope_variables, delimiter);
1022     while (NULL != scope_variable)
1023     {
1024       if (0 == strcmp (reference->name, scope_variable))
1025         break;
1026       scope_variable = strtok (NULL, delimiter);
1027     }
1028     if (NULL == scope_variable)
1029     {
1030       GNUNET_free (scope_variables);
1031       return;
1032     }
1033     GNUNET_free (scope_variables);
1034     struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *le2;
1035     le2 = GNUNET_new (struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry);
1036     le = GNUNET_new (struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry);
1037     le->claim = NULL;
1038     le->reference = NULL;
1039     le->attest = GNUNET_RECLAIM_ATTESTATION_claim_new (attest->name,
1040                                                        attest->type,
1041                                                        attest->data,
1042                                                        attest->data_size);
1043     le->attest->id = attest->id;
1044     le2->attest = NULL;
1045     le2->claim = NULL;
1046     le2->reference = GNUNET_RECLAIM_ATTESTATION_reference_new (reference->name,
1047                                                                reference->
1048                                                                reference_value);
1049     le2->reference->id = reference->id;
1050     le2->reference->id_attest = reference->id_attest;
1051     GNUNET_CONTAINER_DLL_insert (handle->attr_list->list_head,
1052                                  handle->attr_list->list_tail,
1053                                  le);
1054     GNUNET_CONTAINER_DLL_insert (handle->attr_list->list_head,
1055                                  handle->attr_list->list_tail,
1056                                  le2);
1057   }
1058   else if (NULL != attr)
1059   {
1060     if ((NULL == attr->name) || (NULL == attr->data))
1061     {
1062       GNUNET_RECLAIM_get_attributes_next (handle->attr_it);
1063       return;
1064     }
1065     scope_variables = GNUNET_strdup (handle->oidc->scope);
1066     scope_variable = strtok (scope_variables, delimiter);
1067     while (NULL != scope_variable)
1068     {
1069       if (0 == strcmp (attr->name, scope_variable))
1070         break;
1071       scope_variable = strtok (NULL, delimiter);
1072     }
1073     if (NULL == scope_variable)
1074     {
1075       GNUNET_RECLAIM_get_attributes_next (handle->attr_it);
1076       GNUNET_free (scope_variables);
1077       return;
1078     }
1079     GNUNET_free (scope_variables);
1080     le = GNUNET_new (struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry);
1081     le->reference = NULL;
1082     le->attest = NULL;
1083     le->claim = GNUNET_RECLAIM_ATTRIBUTE_claim_new (attr->name,
1084                                                     attr->type,
1085                                                     attr->data,
1086                                                     attr->data_size);
1087     le->claim->id = attr->id;
1088     le->claim->flag = attr->flag;
1089
1090     GNUNET_CONTAINER_DLL_insert (handle->attr_list->list_head,
1091                                  handle->attr_list->list_tail,
1092                                  le);
1093     GNUNET_RECLAIM_get_attributes_next (handle->attr_it);
1094   }
1095 }
1096
1097
1098 /**
1099  * Checks time and cookie and redirects accordingly
1100  */
1101 static void
1102 code_redirect (void *cls)
1103 {
1104   struct RequestHandle *handle = cls;
1105   struct GNUNET_TIME_Absolute current_time;
1106   struct GNUNET_TIME_Absolute *relog_time;
1107   struct GNUNET_CRYPTO_EcdsaPublicKey pubkey;
1108   struct GNUNET_CRYPTO_EcdsaPublicKey ego_pkey;
1109   struct GNUNET_HashCode cache_key;
1110   char *identity_cookie;
1111
1112   GNUNET_asprintf (&identity_cookie,
1113                    "Identity=%s",
1114                    handle->oidc->login_identity);
1115   GNUNET_CRYPTO_hash (identity_cookie, strlen (identity_cookie), &cache_key);
1116   GNUNET_free (identity_cookie);
1117   // No login time for identity -> redirect to login
1118   if (GNUNET_YES ==
1119       GNUNET_CONTAINER_multihashmap_contains (OIDC_cookie_jar_map, &cache_key))
1120   {
1121     relog_time =
1122       GNUNET_CONTAINER_multihashmap_get (OIDC_cookie_jar_map, &cache_key);
1123     current_time = GNUNET_TIME_absolute_get ();
1124     // 30 min after old login -> redirect to login
1125     if (current_time.abs_value_us <= relog_time->abs_value_us)
1126     {
1127       if (GNUNET_OK !=
1128           GNUNET_CRYPTO_ecdsa_public_key_from_string (handle->oidc
1129                                                       ->login_identity,
1130                                                       strlen (
1131                                                         handle->oidc
1132                                                         ->login_identity),
1133                                                       &pubkey))
1134       {
1135         handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_COOKIE);
1136         handle->edesc =
1137           GNUNET_strdup ("The cookie of a login identity is not valid");
1138         GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1139         return;
1140       }
1141       // iterate over egos and compare their public key
1142       for (handle->ego_entry = handle->ego_head; NULL != handle->ego_entry;
1143            handle->ego_entry = handle->ego_entry->next)
1144       {
1145         GNUNET_IDENTITY_ego_get_public_key (handle->ego_entry->ego, &ego_pkey);
1146         if (0 == GNUNET_memcmp (&ego_pkey, &pubkey))
1147         {
1148           handle->priv_key =
1149             *GNUNET_IDENTITY_ego_get_private_key (handle->ego_entry->ego);
1150           handle->idp = GNUNET_RECLAIM_connect (cfg);
1151           handle->attr_list =
1152             GNUNET_new (struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList);
1153           handle->attr_it =
1154             GNUNET_RECLAIM_get_attributes_start (handle->idp,
1155                                                  &handle->priv_key,
1156                                                  &oidc_iteration_error,
1157                                                  handle,
1158                                                  &oidc_attr_collect,
1159                                                  handle,
1160                                                  &oidc_collect_finished_cb,
1161                                                  handle);
1162           return;
1163         }
1164       }
1165       GNUNET_SCHEDULER_add_now (&login_redirect, handle);
1166       return;
1167     }
1168   }
1169 }
1170
1171
1172 static void
1173 build_redirect (void *cls)
1174 {
1175   struct RequestHandle *handle = cls;
1176   struct MHD_Response *resp;
1177   char *redirect_uri;
1178
1179   if (GNUNET_YES == handle->oidc->user_cancelled)
1180   {
1181     if ((NULL != handle->redirect_prefix) &&
1182         (NULL != handle->redirect_suffix) && (NULL != handle->tld))
1183     {
1184       GNUNET_asprintf (&redirect_uri,
1185                        "%s.%s/%s?error=%s&error_description=%s&state=%s",
1186                        handle->redirect_prefix,
1187                        handle->tld,
1188                        handle->redirect_suffix,
1189                        "access_denied",
1190                        "User denied access",
1191                        handle->oidc->state);
1192     }
1193     else
1194     {
1195       GNUNET_asprintf (&redirect_uri,
1196                        "%s?error=%s&error_description=%s&state=%s",
1197                        handle->oidc->redirect_uri,
1198                        "access_denied",
1199                        "User denied access",
1200                        handle->oidc->state);
1201     }
1202     resp = GNUNET_REST_create_response ("");
1203     MHD_add_response_header (resp, "Location", redirect_uri);
1204     handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
1205     GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
1206     GNUNET_free (redirect_uri);
1207     return;
1208   }
1209   GNUNET_SCHEDULER_add_now (&code_redirect, handle);
1210 }
1211
1212
1213 static void
1214 lookup_redirect_uri_result (void *cls,
1215                             uint32_t rd_count,
1216                             const struct GNUNET_GNSRECORD_Data *rd)
1217 {
1218   struct RequestHandle *handle = cls;
1219   char *tmp;
1220   char *tmp_key_str;
1221   char *pos;
1222   struct GNUNET_CRYPTO_EcdsaPublicKey redirect_zone;
1223
1224   handle->gns_op = NULL;
1225   if (0 == rd_count)
1226   {
1227     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
1228     handle->edesc =
1229       GNUNET_strdup ("Server cannot generate ticket, redirect uri not found.");
1230     GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1231     return;
1232   }
1233   for (int i = 0; i < rd_count; i++)
1234   {
1235     if (GNUNET_GNSRECORD_TYPE_RECLAIM_OIDC_REDIRECT != rd[i].record_type)
1236       continue;
1237     if (0 != strncmp (rd[i].data, handle->oidc->redirect_uri, rd[i].data_size))
1238       continue;
1239     tmp = GNUNET_strndup (rd[i].data, rd[i].data_size);
1240     if (NULL == strstr (tmp, handle->oidc->client_id))
1241     {
1242       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1243                   "Redirect uri %s does not contain client_id %s\n",
1244                   tmp,
1245                   handle->oidc->client_id);
1246     }
1247     else
1248     {
1249       pos = strrchr (tmp, (unsigned char) '.');
1250       if (NULL == pos)
1251       {
1252         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1253                     "Redirect uri %s contains client_id but is malformed\n",
1254                     tmp);
1255         GNUNET_free (tmp);
1256         continue;
1257       }
1258       *pos = '\0';
1259       handle->redirect_prefix = GNUNET_strdup (tmp);
1260       tmp_key_str = pos + 1;
1261       pos = strchr (tmp_key_str, (unsigned char) '/');
1262       if (NULL == pos)
1263       {
1264         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1265                     "Redirect uri %s contains client_id but is malformed\n",
1266                     tmp);
1267         GNUNET_free (tmp);
1268         continue;
1269       }
1270       *pos = '\0';
1271       handle->redirect_suffix = GNUNET_strdup (pos + 1);
1272
1273       GNUNET_STRINGS_string_to_data (tmp_key_str,
1274                                      strlen (tmp_key_str),
1275                                      &redirect_zone,
1276                                      sizeof(redirect_zone));
1277     }
1278     GNUNET_SCHEDULER_add_now (&build_redirect, handle);
1279     GNUNET_free (tmp);
1280     return;
1281   }
1282   handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
1283   handle->edesc =
1284     GNUNET_strdup ("Server cannot generate ticket, redirect uri not found.");
1285   GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1286 }
1287
1288
1289 /**
1290  * Initiate redirect back to client.
1291  */
1292 static void
1293 client_redirect (void *cls)
1294 {
1295   struct RequestHandle *handle = cls;
1296
1297   /* Lookup client redirect uri to verify request */
1298   handle->gns_op =
1299     GNUNET_GNS_lookup (handle->gns_handle,
1300                        GNUNET_GNS_EMPTY_LABEL_AT,
1301                        &handle->oidc->client_pkey,
1302                        GNUNET_GNSRECORD_TYPE_RECLAIM_OIDC_REDIRECT,
1303                        GNUNET_GNS_LO_DEFAULT,
1304                        &lookup_redirect_uri_result,
1305                        handle);
1306 }
1307
1308
1309 static char *
1310 get_url_parameter_copy (const struct RequestHandle *handle, const char *key)
1311 {
1312   struct GNUNET_HashCode hc;
1313   char *value;
1314
1315   GNUNET_CRYPTO_hash (key, strlen (key), &hc);
1316   if (GNUNET_YES != GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
1317                                                             ->url_param_map,
1318                                                             &hc))
1319     return NULL;
1320   value =
1321     GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map, &hc);
1322   if (NULL == value)
1323     return NULL;
1324   return GNUNET_strdup (value);
1325 }
1326
1327
1328 /**
1329  * Iteration over all results finished, build final
1330  * response.
1331  *
1332  * @param cls the `struct RequestHandle`
1333  */
1334 static void
1335 build_authz_response (void *cls)
1336 {
1337   struct RequestHandle *handle = cls;
1338   struct GNUNET_HashCode cache_key;
1339
1340   char *expected_scope;
1341   char delimiter[] = " ";
1342   int number_of_ignored_parameter, iterator;
1343
1344
1345   // REQUIRED value: redirect_uri
1346   handle->oidc->redirect_uri =
1347     get_url_parameter_copy (handle, OIDC_REDIRECT_URI_KEY);
1348   if (NULL == handle->oidc->redirect_uri)
1349   {
1350     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1351     handle->edesc = GNUNET_strdup ("missing parameter redirect_uri");
1352     GNUNET_SCHEDULER_add_now (&do_error, handle);
1353     return;
1354   }
1355
1356   // REQUIRED value: response_type
1357   handle->oidc->response_type =
1358     get_url_parameter_copy (handle, OIDC_RESPONSE_TYPE_KEY);
1359   if (NULL == handle->oidc->response_type)
1360   {
1361     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1362     handle->edesc = GNUNET_strdup ("missing parameter response_type");
1363     GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1364     return;
1365   }
1366
1367   // REQUIRED value: scope
1368   handle->oidc->scope = get_url_parameter_copy (handle, OIDC_SCOPE_KEY);
1369   if (NULL == handle->oidc->scope)
1370   {
1371     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_SCOPE);
1372     handle->edesc = GNUNET_strdup ("missing parameter scope");
1373     GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1374     return;
1375   }
1376
1377   // OPTIONAL value: nonce
1378   handle->oidc->nonce = get_url_parameter_copy (handle, OIDC_NONCE_KEY);
1379
1380   // OPTIONAL value: claims
1381   handle->oidc->claims = get_url_parameter_copy (handle, OIDC_CLAIMS_KEY);
1382
1383   // TODO check other values if needed
1384   number_of_ignored_parameter =
1385     sizeof(OIDC_ignored_parameter_array) / sizeof(char *);
1386   for (iterator = 0; iterator < number_of_ignored_parameter; iterator++)
1387   {
1388     GNUNET_CRYPTO_hash (OIDC_ignored_parameter_array[iterator],
1389                         strlen (OIDC_ignored_parameter_array[iterator]),
1390                         &cache_key);
1391     if (GNUNET_YES ==
1392         GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
1393                                                 ->url_param_map,
1394                                                 &cache_key))
1395     {
1396       handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_ACCESS_DENIED);
1397       GNUNET_asprintf (&handle->edesc,
1398                        "Server will not handle parameter: %s",
1399                        OIDC_ignored_parameter_array[iterator]);
1400       GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1401       return;
1402     }
1403   }
1404
1405   // We only support authorization code flows.
1406   if (0 != strcmp (handle->oidc->response_type,
1407                    OIDC_EXPECTED_AUTHORIZATION_RESPONSE_TYPE))
1408   {
1409     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_UNSUPPORTED_RESPONSE_TYPE);
1410     handle->edesc = GNUNET_strdup ("The authorization server does not support "
1411                                    "obtaining this authorization code.");
1412     GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1413     return;
1414   }
1415
1416   // Checks if scope contains 'openid'
1417   expected_scope = GNUNET_strdup (handle->oidc->scope);
1418   char *test;
1419   test = strtok (expected_scope, delimiter);
1420   while (NULL != test)
1421   {
1422     if (0 == strcmp (OIDC_EXPECTED_AUTHORIZATION_SCOPE, expected_scope))
1423       break;
1424     test = strtok (NULL, delimiter);
1425   }
1426   if (NULL == test)
1427   {
1428     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_SCOPE);
1429     handle->edesc =
1430       GNUNET_strdup ("The requested scope is invalid, unknown, or malformed.");
1431     GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1432     GNUNET_free (expected_scope);
1433     return;
1434   }
1435
1436   GNUNET_free (expected_scope);
1437   if ((NULL == handle->oidc->login_identity) &&
1438       (GNUNET_NO == handle->oidc->user_cancelled))
1439     GNUNET_SCHEDULER_add_now (&login_redirect, handle);
1440   else
1441     GNUNET_SCHEDULER_add_now (&client_redirect, handle);
1442 }
1443
1444
1445 /**
1446  * Iterate over tlds in config
1447  */
1448 static void
1449 tld_iter (void *cls, const char *section, const char *option, const char *value)
1450 {
1451   struct RequestHandle *handle = cls;
1452   struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
1453
1454   if (GNUNET_OK !=
1455       GNUNET_CRYPTO_ecdsa_public_key_from_string (value, strlen (value), &pkey))
1456   {
1457     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Skipping non key %s\n", value);
1458     return;
1459   }
1460   if (0 == GNUNET_memcmp (&pkey, &handle->oidc->client_pkey))
1461     handle->tld = GNUNET_strdup (option + 1);
1462 }
1463
1464
1465 /**
1466  * Responds to authorization GET and url-encoded POST request
1467  *
1468  * @param con_handle the connection handle
1469  * @param url the url
1470  * @param cls the RequestHandle
1471  */
1472 static void
1473 authorize_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
1474                     const char *url,
1475                     void *cls)
1476 {
1477   struct RequestHandle *handle = cls;
1478   struct EgoEntry *tmp_ego;
1479   const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key;
1480   struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
1481
1482   cookie_identity_interpretation (handle);
1483
1484   // RECOMMENDED value: state - REQUIRED for answers
1485   handle->oidc->state = get_url_parameter_copy (handle, OIDC_STATE_KEY);
1486
1487   // REQUIRED value: client_id
1488   handle->oidc->client_id = get_url_parameter_copy (handle, OIDC_CLIENT_ID_KEY);
1489   if (NULL == handle->oidc->client_id)
1490   {
1491     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1492     handle->edesc = GNUNET_strdup ("missing parameter client_id");
1493     handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1494     GNUNET_SCHEDULER_add_now (&do_error, handle);
1495     return;
1496   }
1497
1498   // OPTIONAL value: code_challenge
1499   handle->oidc->code_challenge = get_url_parameter_copy (handle,
1500                                                          OIDC_CODE_CHALLENGE_KEY);
1501   if (NULL == handle->oidc->code_challenge)
1502   {
1503     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1504                 "OAuth authorization request does not contain PKCE parameters!\n");
1505   }
1506
1507   if (GNUNET_OK !=
1508       GNUNET_CRYPTO_ecdsa_public_key_from_string (handle->oidc->client_id,
1509                                                   strlen (
1510                                                     handle->oidc->client_id),
1511                                                   &handle->oidc->client_pkey))
1512   {
1513     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_UNAUTHORIZED_CLIENT);
1514     handle->edesc = GNUNET_strdup ("The client is not authorized to request an "
1515                                    "authorization code using this method.");
1516     handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1517     GNUNET_SCHEDULER_add_now (&do_error, handle);
1518     return;
1519   }
1520
1521   // If we know this identity, translated the corresponding TLD
1522   // TODO: We might want to have a reverse lookup functionality for TLDs?
1523   for (tmp_ego = handle->ego_head; NULL != tmp_ego; tmp_ego = tmp_ego->next)
1524   {
1525     priv_key = GNUNET_IDENTITY_ego_get_private_key (tmp_ego->ego);
1526     GNUNET_CRYPTO_ecdsa_key_get_public (priv_key, &pkey);
1527     if (0 == GNUNET_memcmp (&pkey, &handle->oidc->client_pkey))
1528     {
1529       handle->tld = GNUNET_strdup (tmp_ego->identifier);
1530       handle->ego_entry = handle->ego_tail;
1531     }
1532   }
1533   handle->oidc->scope = get_url_parameter_copy (handle, OIDC_SCOPE_KEY);
1534   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Scope: %s\n",GNUNET_strdup (
1535                 handle->oidc->scope));
1536   if (NULL == handle->tld)
1537     GNUNET_CONFIGURATION_iterate_section_values (cfg, "gns", tld_iter, handle);
1538   if (NULL == handle->tld)
1539     handle->tld = GNUNET_strdup (handle->oidc->client_id);
1540   GNUNET_SCHEDULER_add_now (&build_authz_response, handle);
1541 }
1542
1543
1544 /**
1545  * Combines an identity with a login time and responds OK to login request
1546  *
1547  * @param con_handle the connection handle
1548  * @param url the url
1549  * @param cls the RequestHandle
1550  */
1551 static void
1552 login_cont (struct GNUNET_REST_RequestHandle *con_handle,
1553             const char *url,
1554             void *cls)
1555 {
1556   struct MHD_Response *resp = GNUNET_REST_create_response ("");
1557   struct RequestHandle *handle = cls;
1558   struct GNUNET_HashCode cache_key;
1559   struct GNUNET_TIME_Absolute *current_time;
1560   struct GNUNET_TIME_Absolute *last_time;
1561   char *cookie;
1562   char *header_val;
1563   json_t *root;
1564   json_error_t error;
1565   json_t *identity;
1566   char term_data[handle->rest_handle->data_size + 1];
1567
1568   term_data[handle->rest_handle->data_size] = '\0';
1569   GNUNET_memcpy (term_data,
1570                  handle->rest_handle->data,
1571                  handle->rest_handle->data_size);
1572   root = json_loads (term_data, JSON_DECODE_ANY, &error);
1573   identity = json_object_get (root, "identity");
1574   if (! json_is_string (identity))
1575   {
1576     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1577                 "Error parsing json string from %s\n",
1578                 term_data);
1579     handle->proc (handle->proc_cls, resp, MHD_HTTP_BAD_REQUEST);
1580     json_decref (root);
1581     GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
1582     return;
1583   }
1584   GNUNET_asprintf (&cookie, "Identity=%s", json_string_value (identity));
1585   GNUNET_asprintf (&header_val,
1586                    "%s;Max-Age=%d",
1587                    cookie,
1588                    OIDC_COOKIE_EXPIRATION);
1589   MHD_add_response_header (resp, "Set-Cookie", header_val);
1590   MHD_add_response_header (resp, "Access-Control-Allow-Methods", "POST");
1591   GNUNET_CRYPTO_hash (cookie, strlen (cookie), &cache_key);
1592
1593   if (0 != strcmp (json_string_value (identity), "Denied"))
1594   {
1595     current_time = GNUNET_new (struct GNUNET_TIME_Absolute);
1596     *current_time = GNUNET_TIME_relative_to_absolute (
1597       GNUNET_TIME_relative_multiply (GNUNET_TIME_relative_get_second_ (),
1598                                      OIDC_COOKIE_EXPIRATION));
1599     last_time =
1600       GNUNET_CONTAINER_multihashmap_get (OIDC_cookie_jar_map, &cache_key);
1601     GNUNET_free_non_null (last_time);
1602     GNUNET_CONTAINER_multihashmap_put (OIDC_cookie_jar_map,
1603                                        &cache_key,
1604                                        current_time,
1605                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
1606   }
1607   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
1608   GNUNET_free (cookie);
1609   GNUNET_free (header_val);
1610   json_decref (root);
1611   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
1612 }
1613
1614
1615 static int
1616 check_authorization (struct RequestHandle *handle,
1617                      struct GNUNET_CRYPTO_EcdsaPublicKey *cid)
1618 {
1619   struct GNUNET_HashCode cache_key;
1620   char *authorization;
1621   char *credentials;
1622   char *basic_authorization;
1623   char *client_id;
1624   char *pass;
1625   char *expected_pass;
1626
1627   GNUNET_CRYPTO_hash (OIDC_AUTHORIZATION_HEADER_KEY,
1628                       strlen (OIDC_AUTHORIZATION_HEADER_KEY),
1629                       &cache_key);
1630   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
1631                                                            ->header_param_map,
1632                                                            &cache_key))
1633   {
1634     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1635     handle->edesc = GNUNET_strdup ("missing authorization");
1636     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1637     return GNUNET_SYSERR;
1638   }
1639   authorization =
1640     GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->header_param_map,
1641                                        &cache_key);
1642
1643   // split header in "Basic" and [content]
1644   credentials = strtok (authorization, " ");
1645   if ((NULL == credentials) || (0 != strcmp ("Basic", credentials)))
1646   {
1647     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1648     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1649     return GNUNET_SYSERR;
1650   }
1651   credentials = strtok (NULL, " ");
1652   if (NULL == credentials)
1653   {
1654     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1655     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1656     return GNUNET_SYSERR;
1657   }
1658   GNUNET_STRINGS_base64_decode (credentials,
1659                                 strlen (credentials),
1660                                 (void **) &basic_authorization);
1661
1662   if (NULL == basic_authorization)
1663   {
1664     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1665     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1666     return GNUNET_SYSERR;
1667   }
1668   client_id = strtok (basic_authorization, ":");
1669   if (NULL == client_id)
1670   {
1671     GNUNET_free_non_null (basic_authorization);
1672     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1673     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1674     return GNUNET_SYSERR;
1675   }
1676   pass = strtok (NULL, ":");
1677   if (NULL == pass)
1678   {
1679     GNUNET_free_non_null (basic_authorization);
1680     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1681     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1682     return GNUNET_SYSERR;
1683   }
1684
1685   // check client password
1686   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg,
1687                                                           "reclaim-rest-plugin",
1688                                                           "OIDC_CLIENT_SECRET",
1689                                                           &expected_pass))
1690   {
1691     if (0 != strcmp (expected_pass, pass))
1692     {
1693       GNUNET_free_non_null (basic_authorization);
1694       GNUNET_free (expected_pass);
1695       handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1696       handle->response_code = MHD_HTTP_UNAUTHORIZED;
1697       return GNUNET_SYSERR;
1698     }
1699     GNUNET_free (expected_pass);
1700   }
1701   else
1702   {
1703     GNUNET_free_non_null (basic_authorization);
1704     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
1705     handle->edesc = GNUNET_strdup ("gnunet configuration failed");
1706     handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1707     return GNUNET_SYSERR;
1708   }
1709
1710   // check client_id
1711   for (handle->ego_entry = handle->ego_head; NULL != handle->ego_entry;
1712        handle->ego_entry = handle->ego_entry->next)
1713   {
1714     if (0 == strcmp (handle->ego_entry->keystring, client_id))
1715       break;
1716   }
1717   if (NULL == handle->ego_entry)
1718   {
1719     GNUNET_free_non_null (basic_authorization);
1720     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1721     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1722     return GNUNET_SYSERR;
1723   }
1724   GNUNET_STRINGS_string_to_data (client_id,
1725                                  strlen (client_id),
1726                                  cid,
1727                                  sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey));
1728
1729   GNUNET_free (basic_authorization);
1730   return GNUNET_OK;
1731 }
1732
1733
1734 const struct EgoEntry *
1735 find_ego (struct RequestHandle *handle,
1736           struct GNUNET_CRYPTO_EcdsaPublicKey *test_key)
1737 {
1738   struct EgoEntry *ego_entry;
1739   struct GNUNET_CRYPTO_EcdsaPublicKey pub_key;
1740
1741   for (ego_entry = handle->ego_head; NULL != ego_entry;
1742        ego_entry = ego_entry->next)
1743   {
1744     GNUNET_IDENTITY_ego_get_public_key (ego_entry->ego, &pub_key);
1745     if (0 == GNUNET_memcmp (&pub_key, test_key))
1746       return ego_entry;
1747   }
1748   return NULL;
1749 }
1750
1751
1752 static void
1753 persist_access_token (const struct RequestHandle *handle,
1754                       const char *access_token,
1755                       const struct GNUNET_RECLAIM_Ticket *ticket)
1756 {
1757   struct GNUNET_HashCode hc;
1758   struct GNUNET_RECLAIM_Ticket *ticketbuf;
1759
1760   GNUNET_CRYPTO_hash (access_token, strlen (access_token), &hc);
1761   ticketbuf = GNUNET_new (struct GNUNET_RECLAIM_Ticket);
1762   *ticketbuf = *ticket;
1763   GNUNET_assert (GNUNET_SYSERR !=
1764                  GNUNET_CONTAINER_multihashmap_put (
1765                    OIDC_access_token_map,
1766                    &hc,
1767                    ticketbuf,
1768                    GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1769 }
1770
1771
1772 /**
1773  * Responds to token url-encoded POST request
1774  *
1775  * @param con_handle the connection handle
1776  * @param url the url
1777  * @param cls the RequestHandle
1778  */
1779 static void
1780 token_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
1781                 const char *url,
1782                 void *cls)
1783 {
1784   struct RequestHandle *handle = cls;
1785   const struct EgoEntry *ego_entry;
1786   struct GNUNET_TIME_Relative expiration_time;
1787   struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *cl;
1788   struct GNUNET_RECLAIM_Ticket ticket;
1789   struct GNUNET_CRYPTO_EcdsaPublicKey cid;
1790   const struct GNUNET_CRYPTO_EcdsaPrivateKey *privkey;
1791   struct GNUNET_HashCode cache_key;
1792   struct MHD_Response *resp;
1793   char *grant_type;
1794   char *code;
1795   char *json_response;
1796   char *id_token;
1797   char *access_token;
1798   char *jwt_secret;
1799   char *nonce;
1800   char *code_verifier;
1801
1802   /*
1803    * Check Authorization
1804    */
1805   if (GNUNET_SYSERR == check_authorization (handle, &cid))
1806   {
1807     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1808                 "OIDC authorization for token endpoint failed\n");
1809     GNUNET_SCHEDULER_add_now (&do_error, handle);
1810     return;
1811   }
1812
1813   /*
1814    * Check parameter
1815    */
1816
1817   // TODO Do not allow multiple equal parameter names
1818   // REQUIRED grant_type
1819   GNUNET_CRYPTO_hash (OIDC_GRANT_TYPE_KEY,
1820                       strlen (OIDC_GRANT_TYPE_KEY),
1821                       &cache_key);
1822   grant_type = get_url_parameter_copy (handle, OIDC_GRANT_TYPE_KEY);
1823   if (NULL == grant_type)
1824   {
1825     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1826     handle->edesc = GNUNET_strdup ("missing parameter grant_type");
1827     handle->response_code = MHD_HTTP_BAD_REQUEST;
1828     GNUNET_SCHEDULER_add_now (&do_error, handle);
1829     return;
1830   }
1831
1832   // Check parameter grant_type == "authorization_code"
1833   if (0 != strcmp (OIDC_GRANT_TYPE_VALUE, grant_type))
1834   {
1835     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_UNSUPPORTED_GRANT_TYPE);
1836     handle->response_code = MHD_HTTP_BAD_REQUEST;
1837     GNUNET_free (grant_type);
1838     GNUNET_SCHEDULER_add_now (&do_error, handle);
1839     return;
1840   }
1841   GNUNET_free (grant_type);
1842   // REQUIRED code
1843   code = get_url_parameter_copy (handle, OIDC_CODE_KEY);
1844   if (NULL == code)
1845   {
1846     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1847     handle->edesc = GNUNET_strdup ("missing parameter code");
1848     handle->response_code = MHD_HTTP_BAD_REQUEST;
1849     GNUNET_SCHEDULER_add_now (&do_error, handle);
1850     return;
1851   }
1852   ego_entry = find_ego (handle, &cid);
1853   if (NULL == ego_entry)
1854   {
1855     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1856     handle->edesc = GNUNET_strdup ("Unknown client");
1857     handle->response_code = MHD_HTTP_BAD_REQUEST;
1858     GNUNET_free (code);
1859     GNUNET_SCHEDULER_add_now (&do_error, handle);
1860     return;
1861   }
1862   privkey = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
1863
1864   // REQUIRED code verifier
1865   code_verifier = get_url_parameter_copy (handle, OIDC_CODE_VERIFIER_KEY);
1866   if (NULL == code_verifier)
1867   {
1868     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1869                 "OAuth authorization request does not contain PKCE parameters!\n");
1870
1871   }
1872
1873   // decode code
1874   if (GNUNET_OK != OIDC_parse_authz_code (privkey, code, code_verifier, &ticket,
1875                                           &cl, &nonce))
1876   {
1877     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1878     handle->edesc = GNUNET_strdup ("invalid code");
1879     handle->response_code = MHD_HTTP_BAD_REQUEST;
1880     GNUNET_free (code);
1881     GNUNET_SCHEDULER_add_now (&do_error, handle);
1882     return;
1883   }
1884   GNUNET_free (code);
1885
1886   // create jwt
1887   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (cfg,
1888                                                         "reclaim-rest-plugin",
1889                                                         "expiration_time",
1890                                                         &expiration_time))
1891   {
1892     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
1893     handle->edesc = GNUNET_strdup ("gnunet configuration failed");
1894     handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1895     GNUNET_SCHEDULER_add_now (&do_error, handle);
1896     return;
1897   }
1898
1899
1900   // TODO OPTIONAL acr,amr,azp
1901   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
1902                                                           "reclaim-rest-plugin",
1903                                                           "jwt_secret",
1904                                                           &jwt_secret))
1905   {
1906     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1907     handle->edesc = GNUNET_strdup ("No signing secret configured!");
1908     handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1909     GNUNET_SCHEDULER_add_now (&do_error, handle);
1910     return;
1911   }
1912   id_token = OIDC_id_token_new (&ticket.audience,
1913                                 &ticket.identity,
1914                                 cl,
1915                                 &expiration_time,
1916                                 (NULL != nonce) ? nonce : NULL,
1917                                 jwt_secret);
1918   access_token = OIDC_access_token_new ();
1919   OIDC_build_token_response (access_token,
1920                              id_token,
1921                              &expiration_time,
1922                              &json_response);
1923
1924   persist_access_token (handle, access_token, &ticket);
1925   resp = GNUNET_REST_create_response (json_response);
1926   MHD_add_response_header (resp, "Cache-Control", "no-store");
1927   MHD_add_response_header (resp, "Pragma", "no-cache");
1928   MHD_add_response_header (resp, "Content-Type", "application/json");
1929   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
1930   GNUNET_RECLAIM_ATTRIBUTE_list_destroy (cl);
1931   GNUNET_free (access_token);
1932   GNUNET_free (json_response);
1933   GNUNET_free (id_token);
1934   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
1935 }
1936
1937
1938 /**
1939      * Collects claims and stores them in handle
1940      */
1941 static void
1942 consume_ticket (void *cls,
1943                 const struct GNUNET_CRYPTO_EcdsaPublicKey *identity,
1944                 const struct GNUNET_RECLAIM_ATTRIBUTE_Claim *attr,
1945                 const struct GNUNET_RECLAIM_ATTESTATION_Claim *attest,
1946                 const struct GNUNET_RECLAIM_ATTESTATION_REFERENCE *reference)
1947 {
1948   struct RequestHandle *handle = cls;
1949   if (NULL == identity)
1950   {
1951     GNUNET_SCHEDULER_add_now (&return_userinfo_response, handle);
1952     return;
1953   }
1954   if (NULL != attr)
1955   {
1956     char *tmp_value;
1957     json_t *value;
1958     tmp_value = GNUNET_RECLAIM_ATTRIBUTE_value_to_string (attr->type,
1959                                                           attr->data,
1960                                                           attr->data_size);
1961     value = json_string (tmp_value);
1962     json_object_set_new (handle->oidc->response, attr->name, value);
1963     GNUNET_free (tmp_value);
1964   }
1965   else if ((NULL != attest) && (NULL != reference))
1966   {
1967     json_t *claim_sources;
1968     json_t *claim_sources_jwt;
1969     json_t *claim_names;
1970     char *attest_val_str;
1971     claim_sources=json_object_get(handle->oidc->response,"_claim_sources");
1972     claim_names=json_object_get(handle->oidc->response,"_claim_names");
1973     attest_val_str = GNUNET_RECLAIM_ATTESTATION_value_to_string (attest->type,
1974                                                                  attest->data,
1975                                                                  attest->
1976                                                                  data_size);
1977     if ((NULL == claim_sources) && (NULL == claim_names) )
1978     {
1979       claim_sources = json_object ();
1980       claim_names = json_object ();
1981     }
1982     char *source_name;
1983     int i = 0;
1984     GNUNET_asprintf (&source_name,"src%d",i);
1985     while (NULL != (claim_sources_jwt = json_object_get (claim_sources,
1986                                                          source_name)))
1987     {
1988       if (0 == strcmp (json_string_value (json_object_get (claim_sources_jwt,
1989                                                            "JWT")),
1990                        attest_val_str))
1991       {
1992         // Adapt only the claim names
1993         json_object_set_new (claim_names, reference->name, json_string (
1994                                source_name));
1995         json_object_set (handle->oidc->response, "_claim_names",claim_names);
1996         handle->oidc->response = json_deep_copy(handle->oidc->response);
1997         break;
1998       }
1999       i++;
2000       GNUNET_asprintf (&source_name,"src%d",i);
2001     }
2002
2003     // Create new one
2004     if (NULL == claim_sources_jwt)
2005     {
2006       claim_sources_jwt = json_object ();
2007       // Set the JWT for names
2008       json_object_set_new (claim_names, reference->name, json_string (
2009                              source_name));
2010       // Set the JWT for the inner source
2011       json_object_set_new (claim_sources_jwt, "JWT", json_string (
2012                              attest_val_str));
2013       // Set the JWT for the source
2014       json_object_set_new (claim_sources, source_name,claim_sources_jwt);
2015       // Set as claims
2016       json_object_set (handle->oidc->response, "_claim_names", claim_names);
2017       json_object_set (handle->oidc->response, "_claim_sources",claim_sources);
2018       handle->oidc->response = json_deep_copy(handle->oidc->response);
2019     }
2020
2021     json_decref (claim_sources);
2022     json_decref (claim_names);
2023     json_decref (claim_sources_jwt);
2024     GNUNET_free (attest_val_str);
2025   }
2026   else
2027   {
2028     // REMARK: We should not find any claim, one of attest/ref is NULL
2029   }
2030 }
2031
2032
2033 /**
2034  * Responds to userinfo GET and url-encoded POST request
2035  *
2036  * @param con_handle the connection handle
2037  * @param url the url
2038  * @param cls the RequestHandle
2039  */
2040 static void
2041 userinfo_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
2042                    const char *url,
2043                    void *cls)
2044 {
2045   // TODO expiration time
2046   struct RequestHandle *handle = cls;
2047   char delimiter[] = " ";
2048   struct GNUNET_HashCode cache_key;
2049   char *authorization;
2050   char *authorization_type;
2051   char *authorization_access_token;
2052   struct GNUNET_RECLAIM_Ticket *ticket;
2053   const struct EgoEntry *ego_entry;
2054   const struct GNUNET_CRYPTO_EcdsaPrivateKey *privkey;
2055
2056   GNUNET_CRYPTO_hash (OIDC_AUTHORIZATION_HEADER_KEY,
2057                       strlen (OIDC_AUTHORIZATION_HEADER_KEY),
2058                       &cache_key);
2059   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
2060                                                            ->header_param_map,
2061                                                            &cache_key))
2062   {
2063     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
2064     handle->edesc = GNUNET_strdup ("No Access Token");
2065     handle->response_code = MHD_HTTP_UNAUTHORIZED;
2066     GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2067     return;
2068   }
2069   authorization =
2070     GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->header_param_map,
2071                                        &cache_key);
2072
2073   // split header in "Bearer" and access_token
2074   authorization = GNUNET_strdup (authorization);
2075   authorization_type = strtok (authorization, delimiter);
2076   if ((NULL == authorization_type) ||
2077       (0 != strcmp ("Bearer", authorization_type)))
2078   {
2079     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
2080     handle->edesc = GNUNET_strdup ("No Access Token");
2081     handle->response_code = MHD_HTTP_UNAUTHORIZED;
2082     GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2083     GNUNET_free (authorization);
2084     return;
2085   }
2086   authorization_access_token = strtok (NULL, delimiter);
2087   if (NULL == authorization_access_token)
2088   {
2089     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
2090     handle->edesc = GNUNET_strdup ("Access token missing");
2091     handle->response_code = MHD_HTTP_UNAUTHORIZED;
2092     GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2093     GNUNET_free (authorization);
2094     return;
2095   }
2096
2097   GNUNET_CRYPTO_hash (authorization_access_token,
2098                       strlen (authorization_access_token),
2099                       &cache_key);
2100   if (GNUNET_NO ==
2101       GNUNET_CONTAINER_multihashmap_contains (OIDC_access_token_map,
2102                                               &cache_key))
2103   {
2104     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
2105     handle->edesc = GNUNET_strdup ("The access token expired");
2106     handle->response_code = MHD_HTTP_UNAUTHORIZED;
2107     GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2108     GNUNET_free (authorization);
2109     return;
2110   }
2111   ticket =
2112     GNUNET_CONTAINER_multihashmap_get (OIDC_access_token_map, &cache_key);
2113   GNUNET_assert (NULL != ticket);
2114   ego_entry = find_ego (handle, &ticket->audience);
2115   if (NULL == ego_entry)
2116   {
2117     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
2118     handle->edesc = GNUNET_strdup ("The access token expired");
2119     handle->response_code = MHD_HTTP_UNAUTHORIZED;
2120     GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2121     GNUNET_free (authorization);
2122     return;
2123   }
2124
2125   handle->idp = GNUNET_RECLAIM_connect (cfg);
2126   handle->oidc->response = json_object ();
2127   json_object_set_new (handle->oidc->response,
2128                        "sub",
2129                        json_string (ego_entry->keystring));
2130   privkey = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
2131   handle->idp_op = GNUNET_RECLAIM_ticket_consume (handle->idp,
2132                                                   privkey,
2133                                                   ticket,
2134                                                   consume_ticket,
2135                                                   handle);
2136   GNUNET_free (authorization);
2137 }
2138
2139
2140 /**
2141  * Handle rest request
2142  *
2143  * @param handle the request handle
2144  */
2145 static void
2146 init_cont (struct RequestHandle *handle)
2147 {
2148   struct GNUNET_REST_RequestHandlerError err;
2149   static const struct GNUNET_REST_RequestHandler handlers[] =
2150   { { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_AUTHORIZE, &authorize_endpoint },
2151     { MHD_HTTP_METHOD_POST,
2152       GNUNET_REST_API_NS_AUTHORIZE,
2153       &authorize_endpoint },   // url-encoded
2154     { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_LOGIN, &login_cont },
2155     { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_TOKEN, &token_endpoint },
2156     { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_USERINFO, &userinfo_endpoint },
2157     { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_USERINFO, &userinfo_endpoint },
2158     { MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_OIDC, &options_cont },
2159     GNUNET_REST_HANDLER_END };
2160
2161   if (GNUNET_NO ==
2162       GNUNET_REST_handle_request (handle->rest_handle, handlers, &err, handle))
2163   {
2164     handle->response_code = err.error_code;
2165     GNUNET_SCHEDULER_add_now (&do_error, handle);
2166   }
2167 }
2168
2169
2170 /**
2171  * If listing is enabled, prints information about the egos.
2172  *
2173  * This function is initially called for all egos and then again
2174  * whenever a ego's identifier changes or if it is deleted.  At the
2175  * end of the initial pass over all egos, the function is once called
2176  * with 'NULL' for 'ego'. That does NOT mean that the callback won't
2177  * be invoked in the future or that there was an error.
2178  *
2179  * When used with 'GNUNET_IDENTITY_create' or 'GNUNET_IDENTITY_get',
2180  * this function is only called ONCE, and 'NULL' being passed in
2181  * 'ego' does indicate an error (i.e. name is taken or no default
2182  * value is known).  If 'ego' is non-NULL and if '*ctx'
2183  * is set in those callbacks, the value WILL be passed to a subsequent
2184  * call to the identity callback of 'GNUNET_IDENTITY_connect' (if
2185  * that one was not NULL).
2186  *
2187  * When an identity is renamed, this function is called with the
2188  * (known) ego but the NEW identifier.
2189  *
2190  * When an identity is deleted, this function is called with the
2191  * (known) ego and "NULL" for the 'identifier'.  In this case,
2192  * the 'ego' is henceforth invalid (and the 'ctx' should also be
2193  * cleaned up).
2194  *
2195  * @param cls closure
2196  * @param ego ego handle
2197  * @param ctx context for application to store data for this ego
2198  *                 (during the lifetime of this process, initially NULL)
2199  * @param identifier identifier assigned by the user for this ego,
2200  *                   NULL if the user just deleted the ego and it
2201  *                   must thus no longer be used
2202  */
2203 static void
2204 list_ego (void *cls,
2205           struct GNUNET_IDENTITY_Ego *ego,
2206           void **ctx,
2207           const char *identifier)
2208 {
2209   struct RequestHandle *handle = cls;
2210   struct EgoEntry *ego_entry;
2211   struct GNUNET_CRYPTO_EcdsaPublicKey pk;
2212
2213   if ((NULL == ego) && (ID_REST_STATE_INIT == handle->state))
2214   {
2215     handle->state = ID_REST_STATE_POST_INIT;
2216     init_cont (handle);
2217     return;
2218   }
2219   GNUNET_assert (NULL != ego);
2220   if (ID_REST_STATE_INIT == handle->state)
2221
2222   {
2223     ego_entry = GNUNET_new (struct EgoEntry);
2224     GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
2225     ego_entry->keystring = GNUNET_CRYPTO_ecdsa_public_key_to_string (&pk);
2226     ego_entry->ego = ego;
2227     ego_entry->identifier = GNUNET_strdup (identifier);
2228     GNUNET_CONTAINER_DLL_insert_tail (handle->ego_head,
2229                                       handle->ego_tail,
2230                                       ego_entry);
2231     return;
2232   }
2233   /* Ego renamed or added */
2234   if (identifier != NULL)
2235   {
2236     for (ego_entry = handle->ego_head; NULL != ego_entry;
2237          ego_entry = ego_entry->next)
2238     {
2239       if (ego_entry->ego == ego)
2240       {
2241         /* Rename */
2242         GNUNET_free (ego_entry->identifier);
2243         ego_entry->identifier = GNUNET_strdup (identifier);
2244         break;
2245       }
2246     }
2247     if (NULL == ego_entry)
2248     {
2249       /* Add */
2250       ego_entry = GNUNET_new (struct EgoEntry);
2251       GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
2252       ego_entry->keystring = GNUNET_CRYPTO_ecdsa_public_key_to_string (&pk);
2253       ego_entry->ego = ego;
2254       ego_entry->identifier = GNUNET_strdup (identifier);
2255       GNUNET_CONTAINER_DLL_insert_tail (handle->ego_head,
2256                                         handle->ego_tail,
2257                                         ego_entry);
2258     }
2259   }
2260   else
2261   {
2262     /* Delete */
2263     for (ego_entry = handle->ego_head; NULL != ego_entry;
2264          ego_entry = ego_entry->next)
2265     {
2266       if (ego_entry->ego == ego)
2267         break;
2268     }
2269     if (NULL != ego_entry)
2270       GNUNET_CONTAINER_DLL_remove (handle->ego_head,
2271                                    handle->ego_tail,
2272                                    ego_entry);
2273   }
2274 }
2275
2276
2277 static void
2278 rest_identity_process_request (struct GNUNET_REST_RequestHandle *rest_handle,
2279                                GNUNET_REST_ResultProcessor proc,
2280                                void *proc_cls)
2281 {
2282   struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
2283
2284   handle->oidc = GNUNET_new (struct OIDC_Variables);
2285   if (NULL == OIDC_cookie_jar_map)
2286     OIDC_cookie_jar_map = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
2287   if (NULL == OIDC_access_token_map)
2288     OIDC_access_token_map =
2289       GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
2290   handle->response_code = 0;
2291   handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
2292   handle->proc_cls = proc_cls;
2293   handle->proc = proc;
2294   handle->state = ID_REST_STATE_INIT;
2295   handle->rest_handle = rest_handle;
2296
2297   handle->url = GNUNET_strdup (rest_handle->url);
2298   if (handle->url[strlen (handle->url) - 1] == '/')
2299     handle->url[strlen (handle->url) - 1] = '\0';
2300   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting...\n");
2301   handle->identity_handle = GNUNET_IDENTITY_connect (cfg, &list_ego, handle);
2302   handle->gns_handle = GNUNET_GNS_connect (cfg);
2303   handle->namestore_handle = GNUNET_NAMESTORE_connect (cfg);
2304   handle->timeout_task =
2305     GNUNET_SCHEDULER_add_delayed (handle->timeout, &do_timeout, handle);
2306   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connected\n");
2307 }
2308
2309
2310 /**
2311  * Entry point for the plugin.
2312  *
2313  * @param cls Config info
2314  * @return NULL on error, otherwise the plugin context
2315  */
2316 void *
2317 libgnunet_plugin_rest_openid_connect_init (void *cls)
2318 {
2319   static struct Plugin plugin;
2320   struct GNUNET_REST_Plugin *api;
2321
2322   cfg = cls;
2323   if (NULL != plugin.cfg)
2324     return NULL; /* can only initialize once! */
2325   memset (&plugin, 0, sizeof(struct Plugin));
2326   plugin.cfg = cfg;
2327   api = GNUNET_new (struct GNUNET_REST_Plugin);
2328   api->cls = &plugin;
2329   api->name = GNUNET_REST_API_NS_OIDC;
2330   api->process_request = &rest_identity_process_request;
2331   GNUNET_asprintf (&allow_methods,
2332                    "%s, %s, %s, %s, %s",
2333                    MHD_HTTP_METHOD_GET,
2334                    MHD_HTTP_METHOD_POST,
2335                    MHD_HTTP_METHOD_PUT,
2336                    MHD_HTTP_METHOD_DELETE,
2337                    MHD_HTTP_METHOD_OPTIONS);
2338
2339   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2340               _ ("OpenID Connect REST API initialized\n"));
2341   return api;
2342 }
2343
2344
2345 /**
2346  * Exit point from the plugin.
2347  *
2348  * @param cls the plugin context (as returned by "init")
2349  * @return always NULL
2350  */
2351 void *
2352 libgnunet_plugin_rest_openid_connect_done (void *cls)
2353 {
2354   struct GNUNET_REST_Plugin *api = cls;
2355   struct Plugin *plugin = api->cls;
2356
2357   plugin->cfg = NULL;
2358
2359   struct GNUNET_CONTAINER_MultiHashMapIterator *hashmap_it;
2360   void *value = NULL;
2361   hashmap_it =
2362     GNUNET_CONTAINER_multihashmap_iterator_create (OIDC_cookie_jar_map);
2363   while (GNUNET_YES ==
2364          GNUNET_CONTAINER_multihashmap_iterator_next (hashmap_it, NULL, value))
2365     GNUNET_free_non_null (value);
2366   GNUNET_CONTAINER_multihashmap_iterator_destroy (hashmap_it);
2367   GNUNET_CONTAINER_multihashmap_destroy (OIDC_cookie_jar_map);
2368
2369   hashmap_it =
2370     GNUNET_CONTAINER_multihashmap_iterator_create (OIDC_access_token_map);
2371   while (GNUNET_YES ==
2372          GNUNET_CONTAINER_multihashmap_iterator_next (hashmap_it, NULL, value))
2373     GNUNET_free_non_null (value);
2374   GNUNET_CONTAINER_multihashmap_destroy (OIDC_access_token_map);
2375   GNUNET_CONTAINER_multihashmap_iterator_destroy (hashmap_it);
2376   GNUNET_free_non_null (allow_methods);
2377   GNUNET_free (api);
2378   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2379               "OpenID Connect REST plugin is finished\n");
2380   return NULL;
2381 }
2382
2383
2384 /* end of plugin_rest_openid_connect.c */