bugfixes; CLI improvements
[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_AttributeList *attr_list;
432
433   /**
434    * Attestation list
435    */
436   struct GNUNET_RECLAIM_AttestationList *attests_list;
437
438
439   /**
440    * IDENTITY Operation
441    */
442   struct GNUNET_IDENTITY_Operation *op;
443
444   /**
445    * Identity Provider
446    */
447   struct GNUNET_RECLAIM_Handle *idp;
448
449   /**
450    * Idp Operation
451    */
452   struct GNUNET_RECLAIM_Operation *idp_op;
453
454   /**
455    * Attribute iterator
456    */
457   struct GNUNET_RECLAIM_AttributeIterator *attr_it;
458
459   /**
460    * Attestation iterator
461    */
462   struct GNUNET_RECLAIM_AttestationIterator *attest_it;
463
464
465   /**
466    * Ticket iterator
467    */
468   struct GNUNET_RECLAIM_TicketIterator *ticket_it;
469
470   /**
471    * A ticket
472    */
473   struct GNUNET_RECLAIM_Ticket ticket;
474
475   /**
476    * Desired timeout for the lookup (default is no timeout).
477    */
478   struct GNUNET_TIME_Relative timeout;
479
480   /**
481    * ID of a task associated with the resolution process.
482    */
483   struct GNUNET_SCHEDULER_Task *timeout_task;
484
485   /**
486    * The plugin result processor
487    */
488   GNUNET_REST_ResultProcessor proc;
489
490   /**
491    * The closure of the result processor
492    */
493   void *proc_cls;
494
495   /**
496    * The url
497    */
498   char *url;
499
500   /**
501    * The tld for redirect
502    */
503   char *tld;
504
505   /**
506    * The redirect prefix
507    */
508   char *redirect_prefix;
509
510   /**
511    * The redirect suffix
512    */
513   char *redirect_suffix;
514
515   /**
516    * Error response message
517    */
518   char *emsg;
519
520   /**
521    * Error response description
522    */
523   char *edesc;
524
525   /**
526    * Reponse code
527    */
528   int response_code;
529 };
530
531 /**
532  * Cleanup lookup handle
533  * @param handle Handle to clean up
534  */
535 static void
536 cleanup_handle (struct RequestHandle *handle)
537 {
538   struct EgoEntry *ego_entry;
539   struct EgoEntry *ego_tmp;
540
541   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Cleaning up\n");
542   if (NULL != handle->timeout_task)
543     GNUNET_SCHEDULER_cancel (handle->timeout_task);
544   if (NULL != handle->identity_handle)
545     GNUNET_IDENTITY_disconnect (handle->identity_handle);
546   if (NULL != handle->attr_it)
547     GNUNET_RECLAIM_get_attributes_stop (handle->attr_it);
548   if (NULL != handle->attest_it)
549     GNUNET_RECLAIM_get_attestations_stop (handle->attest_it);
550   if (NULL != handle->ticket_it)
551     GNUNET_RECLAIM_ticket_iteration_stop (handle->ticket_it);
552   if (NULL != handle->idp)
553     GNUNET_RECLAIM_disconnect (handle->idp);
554   GNUNET_free_non_null (handle->url);
555   GNUNET_free_non_null (handle->tld);
556   GNUNET_free_non_null (handle->redirect_prefix);
557   GNUNET_free_non_null (handle->redirect_suffix);
558   GNUNET_free_non_null (handle->emsg);
559   GNUNET_free_non_null (handle->edesc);
560   if (NULL != handle->gns_op)
561     GNUNET_GNS_lookup_cancel (handle->gns_op);
562   if (NULL != handle->gns_handle)
563     GNUNET_GNS_disconnect (handle->gns_handle);
564
565   if (NULL != handle->namestore_handle)
566     GNUNET_NAMESTORE_disconnect (handle->namestore_handle);
567   if (NULL != handle->oidc)
568   {
569     GNUNET_free_non_null (handle->oidc->client_id);
570     GNUNET_free_non_null (handle->oidc->login_identity);
571     GNUNET_free_non_null (handle->oidc->nonce);
572     GNUNET_free_non_null (handle->oidc->redirect_uri);
573     GNUNET_free_non_null (handle->oidc->response_type);
574     GNUNET_free_non_null (handle->oidc->scope);
575     GNUNET_free_non_null (handle->oidc->state);
576     json_decref (handle->oidc->response);
577     GNUNET_free (handle->oidc);
578   }
579   GNUNET_RECLAIM_attribute_list_destroy (handle->attr_list);
580   GNUNET_RECLAIM_attestation_list_destroy (handle->attests_list);
581
582   for (ego_entry = handle->ego_head; NULL != ego_entry;)
583   {
584     ego_tmp = ego_entry;
585     ego_entry = ego_entry->next;
586     GNUNET_free (ego_tmp->identifier);
587     GNUNET_free (ego_tmp->keystring);
588     GNUNET_free (ego_tmp);
589   }
590   GNUNET_free (handle);
591 }
592
593
594 static void
595 cleanup_handle_delayed (void *cls)
596 {
597   cleanup_handle (cls);
598 }
599
600
601 /**
602  * Task run on error, sends error message.  Cleans up everything.
603  *
604  * @param cls the `struct RequestHandle`
605  */
606 static void
607 do_error (void *cls)
608 {
609   struct RequestHandle *handle = cls;
610   struct MHD_Response *resp;
611   char *json_error;
612
613   GNUNET_asprintf (&json_error,
614                    "{ \"error\" : \"%s\", \"error_description\" : \"%s\"%s%s%s}",
615                    handle->emsg,
616                    (NULL != handle->edesc) ? handle->edesc : "",
617                    (NULL != handle->oidc->state) ? ", \"state\":\"" : "",
618                    (NULL != handle->oidc->state) ? handle->oidc->state : "",
619                    (NULL != handle->oidc->state) ? "\"" : "");
620   if (0 == handle->response_code)
621     handle->response_code = MHD_HTTP_BAD_REQUEST;
622   resp = GNUNET_REST_create_response (json_error);
623   if (MHD_HTTP_UNAUTHORIZED == handle->response_code)
624     MHD_add_response_header (resp, MHD_HTTP_HEADER_WWW_AUTHENTICATE, "Basic");
625   MHD_add_response_header (resp,
626                            MHD_HTTP_HEADER_CONTENT_TYPE,
627                            "application/json");
628   handle->proc (handle->proc_cls, resp, handle->response_code);
629   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
630   GNUNET_free (json_error);
631 }
632
633
634 /**
635  * Task run on error in userinfo endpoint, sends error header. Cleans up
636  * everything
637  *
638  * @param cls the `struct RequestHandle`
639  */
640 static void
641 do_userinfo_error (void *cls)
642 {
643   struct RequestHandle *handle = cls;
644   struct MHD_Response *resp;
645   char *error;
646
647   GNUNET_asprintf (&error,
648                    "error=\"%s\", error_description=\"%s\"",
649                    handle->emsg,
650                    (NULL != handle->edesc) ? handle->edesc : "");
651   resp = GNUNET_REST_create_response ("");
652   MHD_add_response_header (resp, MHD_HTTP_HEADER_WWW_AUTHENTICATE, "Bearer");
653   handle->proc (handle->proc_cls, resp, handle->response_code);
654   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
655   GNUNET_free (error);
656 }
657
658
659 /**
660  * Task run on error, sends error message and redirects. Cleans up everything.
661  *
662  * @param cls the `struct RequestHandle`
663  */
664 static void
665 do_redirect_error (void *cls)
666 {
667   struct RequestHandle *handle = cls;
668   struct MHD_Response *resp;
669   char *redirect;
670
671   GNUNET_asprintf (&redirect,
672                    "%s?error=%s&error_description=%s%s%s",
673                    handle->oidc->redirect_uri,
674                    handle->emsg,
675                    handle->edesc,
676                    (NULL != handle->oidc->state) ? "&state=" : "",
677                    (NULL != handle->oidc->state) ? handle->oidc->state : "");
678   resp = GNUNET_REST_create_response ("");
679   MHD_add_response_header (resp, "Location", redirect);
680   handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
681   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
682   GNUNET_free (redirect);
683 }
684
685
686 /**
687  * Task run on timeout, sends error message.  Cleans up everything.
688  *
689  * @param cls the `struct RequestHandle`
690  */
691 static void
692 do_timeout (void *cls)
693 {
694   struct RequestHandle *handle = cls;
695
696   handle->timeout_task = NULL;
697   do_error (handle);
698 }
699
700
701 /**
702  * Return attributes for claim
703  *
704  * @param cls the request handle
705  */
706 static void
707 return_userinfo_response (void *cls)
708 {
709   char *result_str;
710   struct RequestHandle *handle = cls;
711   struct MHD_Response *resp;
712
713   result_str = json_dumps (handle->oidc->response, 0);
714   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,"ID-Token: %s\n",result_str);
715   resp = GNUNET_REST_create_response (result_str);
716   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
717   GNUNET_free (result_str);
718   cleanup_handle (handle);
719 }
720
721
722 /**
723  * Respond to OPTIONS request
724  *
725  * @param con_handle the connection handle
726  * @param url the url
727  * @param cls the RequestHandle
728  */
729 static void
730 options_cont (struct GNUNET_REST_RequestHandle *con_handle,
731               const char *url,
732               void *cls)
733 {
734   struct MHD_Response *resp;
735   struct RequestHandle *handle = cls;
736
737   // For now, independent of path return all options
738   resp = GNUNET_REST_create_response (NULL);
739   MHD_add_response_header (resp, "Access-Control-Allow-Methods", allow_methods);
740   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
741   cleanup_handle (handle);
742   return;
743 }
744
745
746 /**
747  * Interprets cookie header and pass its identity keystring to handle
748  */
749 static void
750 cookie_identity_interpretation (struct RequestHandle *handle)
751 {
752   struct GNUNET_HashCode cache_key;
753   char *cookies;
754   struct GNUNET_TIME_Absolute current_time, *relog_time;
755   char delimiter[] = "; ";
756   char *tmp_cookies;
757   char *token;
758   char *value;
759
760   // gets identity of login try with cookie
761   GNUNET_CRYPTO_hash (OIDC_COOKIE_HEADER_KEY,
762                       strlen (OIDC_COOKIE_HEADER_KEY),
763                       &cache_key);
764   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
765                                                            ->header_param_map,
766                                                            &cache_key))
767   {
768     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "No cookie found\n");
769     return;
770   }
771   // splits cookies and find 'Identity' cookie
772   tmp_cookies =
773     GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->header_param_map,
774                                        &cache_key);
775   cookies = GNUNET_strdup (tmp_cookies);
776   token = strtok (cookies, delimiter);
777   handle->oidc->user_cancelled = GNUNET_NO;
778   handle->oidc->login_identity = NULL;
779   if (NULL == token)
780   {
781     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
782                 "Unable to parse cookie: %s\n",
783                 cookies);
784     GNUNET_free (cookies);
785     return;
786   }
787
788   while (NULL != token)
789   {
790     if (0 == strcmp (token, OIDC_COOKIE_HEADER_ACCESS_DENIED))
791     {
792       handle->oidc->user_cancelled = GNUNET_YES;
793       GNUNET_free (cookies);
794       return;
795     }
796     if (NULL != strstr (token, OIDC_COOKIE_HEADER_INFORMATION_KEY))
797       break;
798     token = strtok (NULL, delimiter);
799   }
800   if (NULL == token)
801   {
802     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
803                 "No cookie value to process: %s\n",
804                 cookies);
805     GNUNET_free (cookies);
806     return;
807   }
808   GNUNET_CRYPTO_hash (token, strlen (token), &cache_key);
809   if (GNUNET_NO ==
810       GNUNET_CONTAINER_multihashmap_contains (OIDC_cookie_jar_map, &cache_key))
811   {
812     GNUNET_log (
813       GNUNET_ERROR_TYPE_WARNING,
814       "Found cookie `%s', but no corresponding expiration entry present...\n",
815       token);
816     GNUNET_free (cookies);
817     return;
818   }
819   relog_time =
820     GNUNET_CONTAINER_multihashmap_get (OIDC_cookie_jar_map, &cache_key);
821   current_time = GNUNET_TIME_absolute_get ();
822   // 30 min after old login -> redirect to login
823   if (current_time.abs_value_us > relog_time->abs_value_us)
824   {
825     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
826                 "Found cookie `%s', but it is expired.\n",
827                 token);
828     GNUNET_free (cookies);
829     return;
830   }
831   value = strtok (token, OIDC_COOKIE_HEADER_INFORMATION_KEY);
832   GNUNET_assert (NULL != value);
833   handle->oidc->login_identity = GNUNET_strdup (value);
834   GNUNET_free (cookies);
835 }
836
837
838 /**
839  * Redirects to login page stored in configuration file
840  */
841 static void
842 login_redirect (void *cls)
843 {
844   char *login_base_url;
845   char *new_redirect;
846   struct MHD_Response *resp;
847   struct RequestHandle *handle = cls;
848
849   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg,
850                                                           "reclaim-rest-plugin",
851                                                           "address",
852                                                           &login_base_url))
853   {
854     GNUNET_asprintf (&new_redirect,
855                      "%s?%s=%s&%s=%s&%s=%s&%s=%s&%s=%s&%s=%s&%s=%s&%s=%s",
856                      login_base_url,
857                      OIDC_RESPONSE_TYPE_KEY,
858                      handle->oidc->response_type,
859                      OIDC_CLIENT_ID_KEY,
860                      handle->oidc->client_id,
861                      OIDC_REDIRECT_URI_KEY,
862                      handle->oidc->redirect_uri,
863                      OIDC_SCOPE_KEY,
864                      handle->oidc->scope,
865                      OIDC_STATE_KEY,
866                      (NULL != handle->oidc->state) ? handle->oidc->state : "",
867                      OIDC_CODE_CHALLENGE_KEY,
868                      (NULL != handle->oidc->code_challenge) ?
869                      handle->oidc->code_challenge : "",
870                      OIDC_NONCE_KEY,
871                      (NULL != handle->oidc->nonce) ? handle->oidc->nonce : "",
872                      OIDC_CLAIMS_KEY,
873                      (NULL != handle->oidc->claims) ? handle->oidc->claims :
874                      "");
875     resp = GNUNET_REST_create_response ("");
876     MHD_add_response_header (resp, "Location", new_redirect);
877     GNUNET_free (login_base_url);
878   }
879   else
880   {
881     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
882     handle->edesc = GNUNET_strdup ("gnunet configuration failed");
883     handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
884     GNUNET_SCHEDULER_add_now (&do_error, handle);
885     return;
886   }
887   handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
888   GNUNET_free (new_redirect);
889   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
890 }
891
892
893 /**
894  * Does internal server error when iteration failed.
895  */
896 static void
897 oidc_iteration_error (void *cls)
898 {
899   struct RequestHandle *handle = cls;
900
901   handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
902   handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
903   GNUNET_SCHEDULER_add_now (&do_error, handle);
904 }
905
906
907 /**
908  * Issues ticket and redirects to relying party with the authorization code as
909  * parameter. Otherwise redirects with error
910  */
911 static void
912 oidc_ticket_issue_cb (void *cls, const struct GNUNET_RECLAIM_Ticket *ticket)
913 {
914   struct RequestHandle *handle = cls;
915   struct MHD_Response *resp;
916   char *ticket_str;
917   char *redirect_uri;
918   char *code_string;
919
920   handle->idp_op = NULL;
921   if (NULL == ticket)
922   {
923     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
924     handle->edesc = GNUNET_strdup ("Server cannot generate ticket.");
925     GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
926     return;
927   }
928   handle->ticket = *ticket;
929   ticket_str =
930     GNUNET_STRINGS_data_to_string_alloc (&handle->ticket,
931                                          sizeof(struct GNUNET_RECLAIM_Ticket));
932   // TODO change if more attributes are needed (see max_age)
933   code_string = OIDC_build_authz_code (&handle->priv_key,
934                                        &handle->ticket,
935                                        handle->attr_list,
936                                        handle->attests_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_attest_collect_finished_cb (void *cls)
972 {
973   struct RequestHandle *handle = cls;
974
975   handle->attest_it = NULL;
976   handle->idp_op = GNUNET_RECLAIM_ticket_issue (handle->idp,
977                                                 &handle->priv_key,
978                                                 &handle->oidc->client_pkey,
979                                                 handle->attr_list,
980                                                 &oidc_ticket_issue_cb,
981                                                 handle);
982 }
983
984
985 /**
986  * Collects all attributes for an ego if in scope parameter
987  */
988 static void
989 oidc_attest_collect (void *cls,
990                      const struct GNUNET_CRYPTO_EcdsaPublicKey *identity,
991                      const struct GNUNET_RECLAIM_Attestation *attest,
992                      const struct GNUNET_RECLAIM_AttributeList *attrs)
993 {
994   struct RequestHandle *handle = cls;
995   struct GNUNET_RECLAIM_AttributeListEntry *le;
996
997   for (le = handle->attr_list->list_head; NULL != le; le = le->next)
998   {
999     if (GNUNET_NO == GNUNET_RECLAIM_id_is_equal (&le->attribute->attestation,
1000                                                  &attest->id))
1001     {
1002       struct GNUNET_RECLAIM_AttestationListEntry *ale;
1003       ale = GNUNET_new (struct GNUNET_RECLAIM_AttestationListEntry);
1004       ale->attestation = GNUNET_RECLAIM_attestation_new (attest->name,
1005                                                          attest->type,
1006                                                          attest->data,
1007                                                          attest->data_size);
1008       GNUNET_CONTAINER_DLL_insert (handle->attests_list->list_head,
1009                                    handle->attests_list->list_tail,
1010                                    ale);
1011     }
1012   }
1013   GNUNET_RECLAIM_get_attestations_next (handle->attest_it);
1014 }
1015
1016
1017 static void
1018 oidc_attr_collect_finished_cb (void *cls)
1019 {
1020   struct RequestHandle *handle = cls;
1021
1022   handle->attr_it = NULL;
1023   handle->ticket_it = NULL;
1024   if (NULL == handle->attr_list->list_head)
1025   {
1026     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_SCOPE);
1027     handle->edesc = GNUNET_strdup ("The requested scope is not available.");
1028     GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1029     return;
1030   }
1031   handle->attests_list = GNUNET_new (struct GNUNET_RECLAIM_AttestationList);
1032   handle->attest_it =
1033     GNUNET_RECLAIM_get_attestations_start (handle->idp,
1034                                            &handle->priv_key,
1035                                            &oidc_iteration_error,
1036                                            handle,
1037                                            &oidc_attest_collect,
1038                                            handle,
1039                                            &oidc_attest_collect_finished_cb,
1040                                            handle);
1041
1042   handle->idp_op = GNUNET_RECLAIM_ticket_issue (handle->idp,
1043                                                 &handle->priv_key,
1044                                                 &handle->oidc->client_pkey,
1045                                                 handle->attr_list,
1046                                                 &oidc_ticket_issue_cb,
1047                                                 handle);
1048 }
1049
1050
1051 /**
1052  * Collects all attributes for an ego if in scope parameter
1053  */
1054 static void
1055 oidc_attr_collect (void *cls,
1056                    const struct GNUNET_CRYPTO_EcdsaPublicKey *identity,
1057                    const struct GNUNET_RECLAIM_Attribute *attr)
1058 {
1059   struct RequestHandle *handle = cls;
1060   struct GNUNET_RECLAIM_AttributeListEntry *le;
1061   char *scope_variables;
1062   char *scope_variable;
1063   char delimiter[] = " ";
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     // We can ignore this
1078     return;
1079   }
1080   GNUNET_free (scope_variables);
1081   le = GNUNET_new (struct GNUNET_RECLAIM_AttributeListEntry);
1082   le->attribute = GNUNET_RECLAIM_attribute_new (attr->name,
1083                                                 &attr->attestation,
1084                                                 attr->type,
1085                                                 attr->data,
1086                                                 attr->data_size);
1087   le->attribute->id = attr->id;
1088   le->attribute->flag = attr->flag;
1089   le->attribute->attestation = attr->attestation;
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  * Checks time and cookie and redirects accordingly
1099  */
1100 static void
1101 code_redirect (void *cls)
1102 {
1103   struct RequestHandle *handle = cls;
1104   struct GNUNET_TIME_Absolute current_time;
1105   struct GNUNET_TIME_Absolute *relog_time;
1106   struct GNUNET_CRYPTO_EcdsaPublicKey pubkey;
1107   struct GNUNET_CRYPTO_EcdsaPublicKey ego_pkey;
1108   struct GNUNET_HashCode cache_key;
1109   char *identity_cookie;
1110
1111   GNUNET_asprintf (&identity_cookie,
1112                    "Identity=%s",
1113                    handle->oidc->login_identity);
1114   GNUNET_CRYPTO_hash (identity_cookie, strlen (identity_cookie), &cache_key);
1115   GNUNET_free (identity_cookie);
1116   // No login time for identity -> redirect to login
1117   if (GNUNET_YES ==
1118       GNUNET_CONTAINER_multihashmap_contains (OIDC_cookie_jar_map, &cache_key))
1119   {
1120     relog_time =
1121       GNUNET_CONTAINER_multihashmap_get (OIDC_cookie_jar_map, &cache_key);
1122     current_time = GNUNET_TIME_absolute_get ();
1123     // 30 min after old login -> redirect to login
1124     if (current_time.abs_value_us <= relog_time->abs_value_us)
1125     {
1126       if (GNUNET_OK !=
1127           GNUNET_CRYPTO_ecdsa_public_key_from_string (handle->oidc
1128                                                       ->login_identity,
1129                                                       strlen (
1130                                                         handle->oidc
1131                                                         ->login_identity),
1132                                                       &pubkey))
1133       {
1134         handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_COOKIE);
1135         handle->edesc =
1136           GNUNET_strdup ("The cookie of a login identity is not valid");
1137         GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1138         return;
1139       }
1140       // iterate over egos and compare their public key
1141       for (handle->ego_entry = handle->ego_head; NULL != handle->ego_entry;
1142            handle->ego_entry = handle->ego_entry->next)
1143       {
1144         GNUNET_IDENTITY_ego_get_public_key (handle->ego_entry->ego, &ego_pkey);
1145         if (0 == GNUNET_memcmp (&ego_pkey, &pubkey))
1146         {
1147           handle->priv_key =
1148             *GNUNET_IDENTITY_ego_get_private_key (handle->ego_entry->ego);
1149           handle->idp = GNUNET_RECLAIM_connect (cfg);
1150           handle->attr_list =
1151             GNUNET_new (struct GNUNET_RECLAIM_AttributeList);
1152           handle->attr_it =
1153             GNUNET_RECLAIM_get_attributes_start (handle->idp,
1154                                                  &handle->priv_key,
1155                                                  &oidc_iteration_error,
1156                                                  handle,
1157                                                  &oidc_attr_collect,
1158                                                  handle,
1159                                                  &oidc_attr_collect_finished_cb,
1160                                                  handle);
1161           return;
1162         }
1163       }
1164       GNUNET_SCHEDULER_add_now (&login_redirect, handle);
1165       return;
1166     }
1167   }
1168 }
1169
1170
1171 static void
1172 build_redirect (void *cls)
1173 {
1174   struct RequestHandle *handle = cls;
1175   struct MHD_Response *resp;
1176   char *redirect_uri;
1177
1178   if (GNUNET_YES == handle->oidc->user_cancelled)
1179   {
1180     if ((NULL != handle->redirect_prefix) &&
1181         (NULL != handle->redirect_suffix) && (NULL != handle->tld))
1182     {
1183       GNUNET_asprintf (&redirect_uri,
1184                        "%s.%s/%s?error=%s&error_description=%s&state=%s",
1185                        handle->redirect_prefix,
1186                        handle->tld,
1187                        handle->redirect_suffix,
1188                        "access_denied",
1189                        "User denied access",
1190                        handle->oidc->state);
1191     }
1192     else
1193     {
1194       GNUNET_asprintf (&redirect_uri,
1195                        "%s?error=%s&error_description=%s&state=%s",
1196                        handle->oidc->redirect_uri,
1197                        "access_denied",
1198                        "User denied access",
1199                        handle->oidc->state);
1200     }
1201     resp = GNUNET_REST_create_response ("");
1202     MHD_add_response_header (resp, "Location", redirect_uri);
1203     handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
1204     GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
1205     GNUNET_free (redirect_uri);
1206     return;
1207   }
1208   GNUNET_SCHEDULER_add_now (&code_redirect, handle);
1209 }
1210
1211
1212 static void
1213 lookup_redirect_uri_result (void *cls,
1214                             uint32_t rd_count,
1215                             const struct GNUNET_GNSRECORD_Data *rd)
1216 {
1217   struct RequestHandle *handle = cls;
1218   char *tmp;
1219   char *tmp_key_str;
1220   char *pos;
1221   struct GNUNET_CRYPTO_EcdsaPublicKey redirect_zone;
1222
1223   handle->gns_op = NULL;
1224   if (0 == rd_count)
1225   {
1226     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
1227     handle->edesc =
1228       GNUNET_strdup ("Server cannot generate ticket, redirect uri not found.");
1229     GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1230     return;
1231   }
1232   for (int i = 0; i < rd_count; i++)
1233   {
1234     if (GNUNET_GNSRECORD_TYPE_RECLAIM_OIDC_REDIRECT != rd[i].record_type)
1235       continue;
1236     if (0 != strncmp (rd[i].data, handle->oidc->redirect_uri, rd[i].data_size))
1237       continue;
1238     tmp = GNUNET_strndup (rd[i].data, rd[i].data_size);
1239     if (NULL == strstr (tmp, handle->oidc->client_id))
1240     {
1241       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1242                   "Redirect uri %s does not contain client_id %s\n",
1243                   tmp,
1244                   handle->oidc->client_id);
1245     }
1246     else
1247     {
1248       pos = strrchr (tmp, (unsigned char) '.');
1249       if (NULL == pos)
1250       {
1251         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1252                     "Redirect uri %s contains client_id but is malformed\n",
1253                     tmp);
1254         GNUNET_free (tmp);
1255         continue;
1256       }
1257       *pos = '\0';
1258       handle->redirect_prefix = GNUNET_strdup (tmp);
1259       tmp_key_str = pos + 1;
1260       pos = strchr (tmp_key_str, (unsigned char) '/');
1261       if (NULL == pos)
1262       {
1263         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1264                     "Redirect uri %s contains client_id but is malformed\n",
1265                     tmp);
1266         GNUNET_free (tmp);
1267         continue;
1268       }
1269       *pos = '\0';
1270       handle->redirect_suffix = GNUNET_strdup (pos + 1);
1271
1272       GNUNET_STRINGS_string_to_data (tmp_key_str,
1273                                      strlen (tmp_key_str),
1274                                      &redirect_zone,
1275                                      sizeof(redirect_zone));
1276     }
1277     GNUNET_SCHEDULER_add_now (&build_redirect, handle);
1278     GNUNET_free (tmp);
1279     return;
1280   }
1281   handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
1282   handle->edesc =
1283     GNUNET_strdup ("Server cannot generate ticket, redirect uri not found.");
1284   GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1285 }
1286
1287
1288 /**
1289  * Initiate redirect back to client.
1290  */
1291 static void
1292 client_redirect (void *cls)
1293 {
1294   struct RequestHandle *handle = cls;
1295
1296   /* Lookup client redirect uri to verify request */
1297   handle->gns_op =
1298     GNUNET_GNS_lookup (handle->gns_handle,
1299                        GNUNET_GNS_EMPTY_LABEL_AT,
1300                        &handle->oidc->client_pkey,
1301                        GNUNET_GNSRECORD_TYPE_RECLAIM_OIDC_REDIRECT,
1302                        GNUNET_GNS_LO_DEFAULT,
1303                        &lookup_redirect_uri_result,
1304                        handle);
1305 }
1306
1307
1308 static char *
1309 get_url_parameter_copy (const struct RequestHandle *handle, const char *key)
1310 {
1311   struct GNUNET_HashCode hc;
1312   char *value;
1313
1314   GNUNET_CRYPTO_hash (key, strlen (key), &hc);
1315   if (GNUNET_YES != GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
1316                                                             ->url_param_map,
1317                                                             &hc))
1318     return NULL;
1319   value =
1320     GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map, &hc);
1321   if (NULL == value)
1322     return NULL;
1323   return GNUNET_strdup (value);
1324 }
1325
1326
1327 /**
1328  * Iteration over all results finished, build final
1329  * response.
1330  *
1331  * @param cls the `struct RequestHandle`
1332  */
1333 static void
1334 build_authz_response (void *cls)
1335 {
1336   struct RequestHandle *handle = cls;
1337   struct GNUNET_HashCode cache_key;
1338
1339   char *expected_scope;
1340   char delimiter[] = " ";
1341   int number_of_ignored_parameter, iterator;
1342
1343
1344   // REQUIRED value: redirect_uri
1345   handle->oidc->redirect_uri =
1346     get_url_parameter_copy (handle, OIDC_REDIRECT_URI_KEY);
1347   if (NULL == handle->oidc->redirect_uri)
1348   {
1349     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1350     handle->edesc = GNUNET_strdup ("missing parameter redirect_uri");
1351     GNUNET_SCHEDULER_add_now (&do_error, handle);
1352     return;
1353   }
1354
1355   // REQUIRED value: response_type
1356   handle->oidc->response_type =
1357     get_url_parameter_copy (handle, OIDC_RESPONSE_TYPE_KEY);
1358   if (NULL == handle->oidc->response_type)
1359   {
1360     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1361     handle->edesc = GNUNET_strdup ("missing parameter response_type");
1362     GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1363     return;
1364   }
1365
1366   // REQUIRED value: scope
1367   handle->oidc->scope = get_url_parameter_copy (handle, OIDC_SCOPE_KEY);
1368   if (NULL == handle->oidc->scope)
1369   {
1370     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_SCOPE);
1371     handle->edesc = GNUNET_strdup ("missing parameter scope");
1372     GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1373     return;
1374   }
1375
1376   // OPTIONAL value: nonce
1377   handle->oidc->nonce = get_url_parameter_copy (handle, OIDC_NONCE_KEY);
1378
1379   // OPTIONAL value: claims
1380   handle->oidc->claims = get_url_parameter_copy (handle, OIDC_CLAIMS_KEY);
1381
1382   // TODO check other values if needed
1383   number_of_ignored_parameter =
1384     sizeof(OIDC_ignored_parameter_array) / sizeof(char *);
1385   for (iterator = 0; iterator < number_of_ignored_parameter; iterator++)
1386   {
1387     GNUNET_CRYPTO_hash (OIDC_ignored_parameter_array[iterator],
1388                         strlen (OIDC_ignored_parameter_array[iterator]),
1389                         &cache_key);
1390     if (GNUNET_YES ==
1391         GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
1392                                                 ->url_param_map,
1393                                                 &cache_key))
1394     {
1395       handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_ACCESS_DENIED);
1396       GNUNET_asprintf (&handle->edesc,
1397                        "Server will not handle parameter: %s",
1398                        OIDC_ignored_parameter_array[iterator]);
1399       GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1400       return;
1401     }
1402   }
1403
1404   // We only support authorization code flows.
1405   if (0 != strcmp (handle->oidc->response_type,
1406                    OIDC_EXPECTED_AUTHORIZATION_RESPONSE_TYPE))
1407   {
1408     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_UNSUPPORTED_RESPONSE_TYPE);
1409     handle->edesc = GNUNET_strdup ("The authorization server does not support "
1410                                    "obtaining this authorization code.");
1411     GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1412     return;
1413   }
1414
1415   // Checks if scope contains 'openid'
1416   expected_scope = GNUNET_strdup (handle->oidc->scope);
1417   char *test;
1418   test = strtok (expected_scope, delimiter);
1419   while (NULL != test)
1420   {
1421     if (0 == strcmp (OIDC_EXPECTED_AUTHORIZATION_SCOPE, expected_scope))
1422       break;
1423     test = strtok (NULL, delimiter);
1424   }
1425   if (NULL == test)
1426   {
1427     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_SCOPE);
1428     handle->edesc =
1429       GNUNET_strdup ("The requested scope is invalid, unknown, or malformed.");
1430     GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1431     GNUNET_free (expected_scope);
1432     return;
1433   }
1434
1435   GNUNET_free (expected_scope);
1436   if ((NULL == handle->oidc->login_identity) &&
1437       (GNUNET_NO == handle->oidc->user_cancelled))
1438     GNUNET_SCHEDULER_add_now (&login_redirect, handle);
1439   else
1440     GNUNET_SCHEDULER_add_now (&client_redirect, handle);
1441 }
1442
1443
1444 /**
1445  * Iterate over tlds in config
1446  */
1447 static void
1448 tld_iter (void *cls, const char *section, const char *option, const char *value)
1449 {
1450   struct RequestHandle *handle = cls;
1451   struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
1452
1453   if (GNUNET_OK !=
1454       GNUNET_CRYPTO_ecdsa_public_key_from_string (value, strlen (value), &pkey))
1455   {
1456     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Skipping non key %s\n", value);
1457     return;
1458   }
1459   if (0 == GNUNET_memcmp (&pkey, &handle->oidc->client_pkey))
1460     handle->tld = GNUNET_strdup (option + 1);
1461 }
1462
1463
1464 /**
1465  * Responds to authorization GET and url-encoded POST request
1466  *
1467  * @param con_handle the connection handle
1468  * @param url the url
1469  * @param cls the RequestHandle
1470  */
1471 static void
1472 authorize_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
1473                     const char *url,
1474                     void *cls)
1475 {
1476   struct RequestHandle *handle = cls;
1477   struct EgoEntry *tmp_ego;
1478   const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key;
1479   struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
1480
1481   cookie_identity_interpretation (handle);
1482
1483   // RECOMMENDED value: state - REQUIRED for answers
1484   handle->oidc->state = get_url_parameter_copy (handle, OIDC_STATE_KEY);
1485
1486   // REQUIRED value: client_id
1487   handle->oidc->client_id = get_url_parameter_copy (handle, OIDC_CLIENT_ID_KEY);
1488   if (NULL == handle->oidc->client_id)
1489   {
1490     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1491     handle->edesc = GNUNET_strdup ("missing parameter client_id");
1492     handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1493     GNUNET_SCHEDULER_add_now (&do_error, handle);
1494     return;
1495   }
1496
1497   // OPTIONAL value: code_challenge
1498   handle->oidc->code_challenge = get_url_parameter_copy (handle,
1499                                                          OIDC_CODE_CHALLENGE_KEY);
1500   if (NULL == handle->oidc->code_challenge)
1501   {
1502     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1503                 "OAuth authorization request does not contain PKCE parameters!\n");
1504   }
1505
1506   if (GNUNET_OK !=
1507       GNUNET_CRYPTO_ecdsa_public_key_from_string (handle->oidc->client_id,
1508                                                   strlen (
1509                                                     handle->oidc->client_id),
1510                                                   &handle->oidc->client_pkey))
1511   {
1512     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_UNAUTHORIZED_CLIENT);
1513     handle->edesc = GNUNET_strdup ("The client is not authorized to request an "
1514                                    "authorization code using this method.");
1515     handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1516     GNUNET_SCHEDULER_add_now (&do_error, handle);
1517     return;
1518   }
1519
1520   // If we know this identity, translated the corresponding TLD
1521   // TODO: We might want to have a reverse lookup functionality for TLDs?
1522   for (tmp_ego = handle->ego_head; NULL != tmp_ego; tmp_ego = tmp_ego->next)
1523   {
1524     priv_key = GNUNET_IDENTITY_ego_get_private_key (tmp_ego->ego);
1525     GNUNET_CRYPTO_ecdsa_key_get_public (priv_key, &pkey);
1526     if (0 == GNUNET_memcmp (&pkey, &handle->oidc->client_pkey))
1527     {
1528       handle->tld = GNUNET_strdup (tmp_ego->identifier);
1529       handle->ego_entry = handle->ego_tail;
1530     }
1531   }
1532   handle->oidc->scope = get_url_parameter_copy (handle, OIDC_SCOPE_KEY);
1533   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Scope: %s\n",GNUNET_strdup (
1534                 handle->oidc->scope));
1535   if (NULL == handle->tld)
1536     GNUNET_CONFIGURATION_iterate_section_values (cfg, "gns", tld_iter, handle);
1537   if (NULL == handle->tld)
1538     handle->tld = GNUNET_strdup (handle->oidc->client_id);
1539   GNUNET_SCHEDULER_add_now (&build_authz_response, handle);
1540 }
1541
1542
1543 /**
1544  * Combines an identity with a login time and responds OK to login request
1545  *
1546  * @param con_handle the connection handle
1547  * @param url the url
1548  * @param cls the RequestHandle
1549  */
1550 static void
1551 login_cont (struct GNUNET_REST_RequestHandle *con_handle,
1552             const char *url,
1553             void *cls)
1554 {
1555   struct MHD_Response *resp = GNUNET_REST_create_response ("");
1556   struct RequestHandle *handle = cls;
1557   struct GNUNET_HashCode cache_key;
1558   struct GNUNET_TIME_Absolute *current_time;
1559   struct GNUNET_TIME_Absolute *last_time;
1560   char *cookie;
1561   char *header_val;
1562   json_t *root;
1563   json_error_t error;
1564   json_t *identity;
1565   char term_data[handle->rest_handle->data_size + 1];
1566
1567   term_data[handle->rest_handle->data_size] = '\0';
1568   GNUNET_memcpy (term_data,
1569                  handle->rest_handle->data,
1570                  handle->rest_handle->data_size);
1571   root = json_loads (term_data, JSON_DECODE_ANY, &error);
1572   identity = json_object_get (root, "identity");
1573   if (! json_is_string (identity))
1574   {
1575     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1576                 "Error parsing json string from %s\n",
1577                 term_data);
1578     handle->proc (handle->proc_cls, resp, MHD_HTTP_BAD_REQUEST);
1579     json_decref (root);
1580     GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
1581     return;
1582   }
1583   GNUNET_asprintf (&cookie, "Identity=%s", json_string_value (identity));
1584   GNUNET_asprintf (&header_val,
1585                    "%s;Max-Age=%d",
1586                    cookie,
1587                    OIDC_COOKIE_EXPIRATION);
1588   MHD_add_response_header (resp, "Set-Cookie", header_val);
1589   MHD_add_response_header (resp, "Access-Control-Allow-Methods", "POST");
1590   GNUNET_CRYPTO_hash (cookie, strlen (cookie), &cache_key);
1591
1592   if (0 != strcmp (json_string_value (identity), "Denied"))
1593   {
1594     current_time = GNUNET_new (struct GNUNET_TIME_Absolute);
1595     *current_time = GNUNET_TIME_relative_to_absolute (
1596       GNUNET_TIME_relative_multiply (GNUNET_TIME_relative_get_second_ (),
1597                                      OIDC_COOKIE_EXPIRATION));
1598     last_time =
1599       GNUNET_CONTAINER_multihashmap_get (OIDC_cookie_jar_map, &cache_key);
1600     GNUNET_free_non_null (last_time);
1601     GNUNET_CONTAINER_multihashmap_put (OIDC_cookie_jar_map,
1602                                        &cache_key,
1603                                        current_time,
1604                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
1605   }
1606   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
1607   GNUNET_free (cookie);
1608   GNUNET_free (header_val);
1609   json_decref (root);
1610   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
1611 }
1612
1613
1614 static int
1615 check_authorization (struct RequestHandle *handle,
1616                      struct GNUNET_CRYPTO_EcdsaPublicKey *cid)
1617 {
1618   struct GNUNET_HashCode cache_key;
1619   char *authorization;
1620   char *credentials;
1621   char *basic_authorization;
1622   char *client_id;
1623   char *pass;
1624   char *expected_pass;
1625
1626   GNUNET_CRYPTO_hash (OIDC_AUTHORIZATION_HEADER_KEY,
1627                       strlen (OIDC_AUTHORIZATION_HEADER_KEY),
1628                       &cache_key);
1629   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
1630                                                            ->header_param_map,
1631                                                            &cache_key))
1632   {
1633     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1634     handle->edesc = GNUNET_strdup ("missing authorization");
1635     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1636     return GNUNET_SYSERR;
1637   }
1638   authorization =
1639     GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->header_param_map,
1640                                        &cache_key);
1641
1642   // split header in "Basic" and [content]
1643   credentials = strtok (authorization, " ");
1644   if ((NULL == credentials) || (0 != strcmp ("Basic", credentials)))
1645   {
1646     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1647     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1648     return GNUNET_SYSERR;
1649   }
1650   credentials = strtok (NULL, " ");
1651   if (NULL == credentials)
1652   {
1653     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1654     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1655     return GNUNET_SYSERR;
1656   }
1657   GNUNET_STRINGS_base64_decode (credentials,
1658                                 strlen (credentials),
1659                                 (void **) &basic_authorization);
1660
1661   if (NULL == basic_authorization)
1662   {
1663     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1664     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1665     return GNUNET_SYSERR;
1666   }
1667   client_id = strtok (basic_authorization, ":");
1668   if (NULL == client_id)
1669   {
1670     GNUNET_free_non_null (basic_authorization);
1671     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1672     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1673     return GNUNET_SYSERR;
1674   }
1675   pass = strtok (NULL, ":");
1676   if (NULL == pass)
1677   {
1678     GNUNET_free_non_null (basic_authorization);
1679     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1680     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1681     return GNUNET_SYSERR;
1682   }
1683
1684   // check client password
1685   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg,
1686                                                           "reclaim-rest-plugin",
1687                                                           "OIDC_CLIENT_SECRET",
1688                                                           &expected_pass))
1689   {
1690     if (0 != strcmp (expected_pass, pass))
1691     {
1692       GNUNET_free_non_null (basic_authorization);
1693       GNUNET_free (expected_pass);
1694       handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1695       handle->response_code = MHD_HTTP_UNAUTHORIZED;
1696       return GNUNET_SYSERR;
1697     }
1698     GNUNET_free (expected_pass);
1699   }
1700   else
1701   {
1702     GNUNET_free_non_null (basic_authorization);
1703     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
1704     handle->edesc = GNUNET_strdup ("gnunet configuration failed");
1705     handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1706     return GNUNET_SYSERR;
1707   }
1708
1709   // check client_id
1710   for (handle->ego_entry = handle->ego_head; NULL != handle->ego_entry;
1711        handle->ego_entry = handle->ego_entry->next)
1712   {
1713     if (0 == strcmp (handle->ego_entry->keystring, client_id))
1714       break;
1715   }
1716   if (NULL == handle->ego_entry)
1717   {
1718     GNUNET_free_non_null (basic_authorization);
1719     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1720     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1721     return GNUNET_SYSERR;
1722   }
1723   GNUNET_STRINGS_string_to_data (client_id,
1724                                  strlen (client_id),
1725                                  cid,
1726                                  sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey));
1727
1728   GNUNET_free (basic_authorization);
1729   return GNUNET_OK;
1730 }
1731
1732
1733 const struct EgoEntry *
1734 find_ego (struct RequestHandle *handle,
1735           struct GNUNET_CRYPTO_EcdsaPublicKey *test_key)
1736 {
1737   struct EgoEntry *ego_entry;
1738   struct GNUNET_CRYPTO_EcdsaPublicKey pub_key;
1739
1740   for (ego_entry = handle->ego_head; NULL != ego_entry;
1741        ego_entry = ego_entry->next)
1742   {
1743     GNUNET_IDENTITY_ego_get_public_key (ego_entry->ego, &pub_key);
1744     if (0 == GNUNET_memcmp (&pub_key, test_key))
1745       return ego_entry;
1746   }
1747   return NULL;
1748 }
1749
1750
1751 static void
1752 persist_access_token (const struct RequestHandle *handle,
1753                       const char *access_token,
1754                       const struct GNUNET_RECLAIM_Ticket *ticket)
1755 {
1756   struct GNUNET_HashCode hc;
1757   struct GNUNET_RECLAIM_Ticket *ticketbuf;
1758
1759   GNUNET_CRYPTO_hash (access_token, strlen (access_token), &hc);
1760   ticketbuf = GNUNET_new (struct GNUNET_RECLAIM_Ticket);
1761   *ticketbuf = *ticket;
1762   GNUNET_assert (GNUNET_SYSERR !=
1763                  GNUNET_CONTAINER_multihashmap_put (
1764                    OIDC_access_token_map,
1765                    &hc,
1766                    ticketbuf,
1767                    GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1768 }
1769
1770
1771 /**
1772  * Responds to token url-encoded POST request
1773  *
1774  * @param con_handle the connection handle
1775  * @param url the url
1776  * @param cls the RequestHandle
1777  */
1778 static void
1779 token_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
1780                 const char *url,
1781                 void *cls)
1782 {
1783   struct RequestHandle *handle = cls;
1784   const struct EgoEntry *ego_entry;
1785   struct GNUNET_TIME_Relative expiration_time;
1786   struct GNUNET_RECLAIM_AttributeList *cl;
1787   struct GNUNET_RECLAIM_AttestationList *al;
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, &al, &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                                 al,
1916                                 &expiration_time,
1917                                 (NULL != nonce) ? nonce : NULL,
1918                                 jwt_secret);
1919   access_token = OIDC_access_token_new ();
1920   OIDC_build_token_response (access_token,
1921                              id_token,
1922                              &expiration_time,
1923                              &json_response);
1924
1925   persist_access_token (handle, access_token, &ticket);
1926   resp = GNUNET_REST_create_response (json_response);
1927   MHD_add_response_header (resp, "Cache-Control", "no-store");
1928   MHD_add_response_header (resp, "Pragma", "no-cache");
1929   MHD_add_response_header (resp, "Content-Type", "application/json");
1930   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
1931   GNUNET_RECLAIM_attribute_list_destroy (cl);
1932   GNUNET_RECLAIM_attestation_list_destroy (al);
1933   GNUNET_free (access_token);
1934   GNUNET_free (json_response);
1935   GNUNET_free (id_token);
1936   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
1937 }
1938
1939
1940 /**
1941  * Collects claims and stores them in handle
1942  */
1943 static void
1944 consume_ticket (void *cls,
1945                 const struct GNUNET_CRYPTO_EcdsaPublicKey *identity,
1946                 const struct GNUNET_RECLAIM_Attribute *attr,
1947                 const struct GNUNET_RECLAIM_Attestation *attest)
1948 {
1949   struct RequestHandle *handle = cls;
1950   if (NULL == identity)
1951   {
1952     GNUNET_SCHEDULER_add_now (&return_userinfo_response, handle);
1953     return;
1954   }
1955   if (GNUNET_YES == GNUNET_RECLAIM_id_is_zero (&attr->attestation))
1956   {
1957     char *tmp_value;
1958     json_t *value;
1959     tmp_value = GNUNET_RECLAIM_attribute_value_to_string (attr->type,
1960                                                           attr->data,
1961                                                           attr->data_size);
1962     value = json_string (tmp_value);
1963     json_object_set_new (handle->oidc->response, attr->name, value);
1964     GNUNET_free (tmp_value);
1965     return;
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 =
1974     GNUNET_RECLAIM_attestation_value_to_string (attest->type,
1975                                                 attest->data,
1976                                                 attest->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, attr->data,
1994                            json_string (source_name));
1995       json_object_set (handle->oidc->response,
1996                        "_claim_names", claim_names);
1997       break;
1998     }
1999     i++;
2000     GNUNET_free (source_name);
2001     GNUNET_asprintf (&source_name, "src%d", i);
2002   }
2003
2004   // Create new one
2005   if (NULL == claim_sources_jwt)
2006   {
2007     claim_sources_jwt = json_object ();
2008     // Set the JWT for names
2009     json_object_set_new (claim_names, attr->data,
2010                          json_string (source_name));
2011     // Set the JWT for the inner source
2012     json_object_set_new (claim_sources_jwt, "JWT",
2013                          json_string (attest_val_str));
2014     // Set the JWT for the source
2015     json_object_set_new (claim_sources, source_name, claim_sources_jwt);
2016     // Set as claims
2017     json_object_set (handle->oidc->response, "_claim_names", claim_names);
2018     json_object_set (handle->oidc->response, "_claim_sources",claim_sources);
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
2027
2028 /**
2029  * Responds to userinfo GET and url-encoded POST request
2030  *
2031  * @param con_handle the connection handle
2032  * @param url the url
2033  * @param cls the RequestHandle
2034  */
2035 static void
2036 userinfo_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
2037                    const char *url,
2038                    void *cls)
2039 {
2040   // TODO expiration time
2041   struct RequestHandle *handle = cls;
2042   char delimiter[] = " ";
2043   struct GNUNET_HashCode cache_key;
2044   char *authorization;
2045   char *authorization_type;
2046   char *authorization_access_token;
2047   struct GNUNET_RECLAIM_Ticket *ticket;
2048   const struct EgoEntry *ego_entry;
2049   const struct GNUNET_CRYPTO_EcdsaPrivateKey *privkey;
2050
2051   GNUNET_CRYPTO_hash (OIDC_AUTHORIZATION_HEADER_KEY,
2052                       strlen (OIDC_AUTHORIZATION_HEADER_KEY),
2053                       &cache_key);
2054   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
2055                                                            ->header_param_map,
2056                                                            &cache_key))
2057   {
2058     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
2059     handle->edesc = GNUNET_strdup ("No Access Token");
2060     handle->response_code = MHD_HTTP_UNAUTHORIZED;
2061     GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2062     return;
2063   }
2064   authorization =
2065     GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->header_param_map,
2066                                        &cache_key);
2067
2068   // split header in "Bearer" and access_token
2069   authorization = GNUNET_strdup (authorization);
2070   authorization_type = strtok (authorization, delimiter);
2071   if ((NULL == authorization_type) ||
2072       (0 != strcmp ("Bearer", authorization_type)))
2073   {
2074     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
2075     handle->edesc = GNUNET_strdup ("No Access Token");
2076     handle->response_code = MHD_HTTP_UNAUTHORIZED;
2077     GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2078     GNUNET_free (authorization);
2079     return;
2080   }
2081   authorization_access_token = strtok (NULL, delimiter);
2082   if (NULL == authorization_access_token)
2083   {
2084     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
2085     handle->edesc = GNUNET_strdup ("Access token missing");
2086     handle->response_code = MHD_HTTP_UNAUTHORIZED;
2087     GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2088     GNUNET_free (authorization);
2089     return;
2090   }
2091
2092   GNUNET_CRYPTO_hash (authorization_access_token,
2093                       strlen (authorization_access_token),
2094                       &cache_key);
2095   if (GNUNET_NO ==
2096       GNUNET_CONTAINER_multihashmap_contains (OIDC_access_token_map,
2097                                               &cache_key))
2098   {
2099     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
2100     handle->edesc = GNUNET_strdup ("The access token expired");
2101     handle->response_code = MHD_HTTP_UNAUTHORIZED;
2102     GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2103     GNUNET_free (authorization);
2104     return;
2105   }
2106   ticket =
2107     GNUNET_CONTAINER_multihashmap_get (OIDC_access_token_map, &cache_key);
2108   GNUNET_assert (NULL != ticket);
2109   ego_entry = find_ego (handle, &ticket->audience);
2110   if (NULL == ego_entry)
2111   {
2112     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
2113     handle->edesc = GNUNET_strdup ("The access token expired");
2114     handle->response_code = MHD_HTTP_UNAUTHORIZED;
2115     GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2116     GNUNET_free (authorization);
2117     return;
2118   }
2119
2120   handle->idp = GNUNET_RECLAIM_connect (cfg);
2121   handle->oidc->response = json_object ();
2122   json_object_set_new (handle->oidc->response,
2123                        "sub",
2124                        json_string (ego_entry->keystring));
2125   privkey = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
2126   handle->idp_op = GNUNET_RECLAIM_ticket_consume (handle->idp,
2127                                                   privkey,
2128                                                   ticket,
2129                                                   consume_ticket,
2130                                                   handle);
2131   GNUNET_free (authorization);
2132 }
2133
2134
2135 /**
2136  * Handle rest request
2137  *
2138  * @param handle the request handle
2139  */
2140 static void
2141 init_cont (struct RequestHandle *handle)
2142 {
2143   struct GNUNET_REST_RequestHandlerError err;
2144   static const struct GNUNET_REST_RequestHandler handlers[] =
2145   { { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_AUTHORIZE, &authorize_endpoint },
2146     { MHD_HTTP_METHOD_POST,
2147       GNUNET_REST_API_NS_AUTHORIZE,
2148       &authorize_endpoint },   // url-encoded
2149     { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_LOGIN, &login_cont },
2150     { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_TOKEN, &token_endpoint },
2151     { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_USERINFO, &userinfo_endpoint },
2152     { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_USERINFO, &userinfo_endpoint },
2153     { MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_OIDC, &options_cont },
2154     GNUNET_REST_HANDLER_END };
2155
2156   if (GNUNET_NO ==
2157       GNUNET_REST_handle_request (handle->rest_handle, handlers, &err, handle))
2158   {
2159     handle->response_code = err.error_code;
2160     GNUNET_SCHEDULER_add_now (&do_error, handle);
2161   }
2162 }
2163
2164
2165 /**
2166  * If listing is enabled, prints information about the egos.
2167  *
2168  * This function is initially called for all egos and then again
2169  * whenever a ego's identifier changes or if it is deleted.  At the
2170  * end of the initial pass over all egos, the function is once called
2171  * with 'NULL' for 'ego'. That does NOT mean that the callback won't
2172  * be invoked in the future or that there was an error.
2173  *
2174  * When used with 'GNUNET_IDENTITY_create' or 'GNUNET_IDENTITY_get',
2175  * this function is only called ONCE, and 'NULL' being passed in
2176  * 'ego' does indicate an error (i.e. name is taken or no default
2177  * value is known).  If 'ego' is non-NULL and if '*ctx'
2178  * is set in those callbacks, the value WILL be passed to a subsequent
2179  * call to the identity callback of 'GNUNET_IDENTITY_connect' (if
2180  * that one was not NULL).
2181  *
2182  * When an identity is renamed, this function is called with the
2183  * (known) ego but the NEW identifier.
2184  *
2185  * When an identity is deleted, this function is called with the
2186  * (known) ego and "NULL" for the 'identifier'.  In this case,
2187  * the 'ego' is henceforth invalid (and the 'ctx' should also be
2188  * cleaned up).
2189  *
2190  * @param cls closure
2191  * @param ego ego handle
2192  * @param ctx context for application to store data for this ego
2193  *                 (during the lifetime of this process, initially NULL)
2194  * @param identifier identifier assigned by the user for this ego,
2195  *                   NULL if the user just deleted the ego and it
2196  *                   must thus no longer be used
2197  */
2198 static void
2199 list_ego (void *cls,
2200           struct GNUNET_IDENTITY_Ego *ego,
2201           void **ctx,
2202           const char *identifier)
2203 {
2204   struct RequestHandle *handle = cls;
2205   struct EgoEntry *ego_entry;
2206   struct GNUNET_CRYPTO_EcdsaPublicKey pk;
2207
2208   if ((NULL == ego) && (ID_REST_STATE_INIT == handle->state))
2209   {
2210     handle->state = ID_REST_STATE_POST_INIT;
2211     init_cont (handle);
2212     return;
2213   }
2214   GNUNET_assert (NULL != ego);
2215   if (ID_REST_STATE_INIT == handle->state)
2216
2217   {
2218     ego_entry = GNUNET_new (struct EgoEntry);
2219     GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
2220     ego_entry->keystring = GNUNET_CRYPTO_ecdsa_public_key_to_string (&pk);
2221     ego_entry->ego = ego;
2222     ego_entry->identifier = GNUNET_strdup (identifier);
2223     GNUNET_CONTAINER_DLL_insert_tail (handle->ego_head,
2224                                       handle->ego_tail,
2225                                       ego_entry);
2226     return;
2227   }
2228   /* Ego renamed or added */
2229   if (identifier != NULL)
2230   {
2231     for (ego_entry = handle->ego_head; NULL != ego_entry;
2232          ego_entry = ego_entry->next)
2233     {
2234       if (ego_entry->ego == ego)
2235       {
2236         /* Rename */
2237         GNUNET_free (ego_entry->identifier);
2238         ego_entry->identifier = GNUNET_strdup (identifier);
2239         break;
2240       }
2241     }
2242     if (NULL == ego_entry)
2243     {
2244       /* Add */
2245       ego_entry = GNUNET_new (struct EgoEntry);
2246       GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
2247       ego_entry->keystring = GNUNET_CRYPTO_ecdsa_public_key_to_string (&pk);
2248       ego_entry->ego = ego;
2249       ego_entry->identifier = GNUNET_strdup (identifier);
2250       GNUNET_CONTAINER_DLL_insert_tail (handle->ego_head,
2251                                         handle->ego_tail,
2252                                         ego_entry);
2253     }
2254   }
2255   else
2256   {
2257     /* Delete */
2258     for (ego_entry = handle->ego_head; NULL != ego_entry;
2259          ego_entry = ego_entry->next)
2260     {
2261       if (ego_entry->ego == ego)
2262         break;
2263     }
2264     if (NULL != ego_entry)
2265       GNUNET_CONTAINER_DLL_remove (handle->ego_head,
2266                                    handle->ego_tail,
2267                                    ego_entry);
2268   }
2269 }
2270
2271
2272 static void
2273 rest_identity_process_request (struct GNUNET_REST_RequestHandle *rest_handle,
2274                                GNUNET_REST_ResultProcessor proc,
2275                                void *proc_cls)
2276 {
2277   struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
2278
2279   handle->oidc = GNUNET_new (struct OIDC_Variables);
2280   if (NULL == OIDC_cookie_jar_map)
2281     OIDC_cookie_jar_map = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
2282   if (NULL == OIDC_access_token_map)
2283     OIDC_access_token_map =
2284       GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
2285   handle->response_code = 0;
2286   handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
2287   handle->proc_cls = proc_cls;
2288   handle->proc = proc;
2289   handle->state = ID_REST_STATE_INIT;
2290   handle->rest_handle = rest_handle;
2291
2292   handle->url = GNUNET_strdup (rest_handle->url);
2293   if (handle->url[strlen (handle->url) - 1] == '/')
2294     handle->url[strlen (handle->url) - 1] = '\0';
2295   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting...\n");
2296   handle->identity_handle = GNUNET_IDENTITY_connect (cfg, &list_ego, handle);
2297   handle->gns_handle = GNUNET_GNS_connect (cfg);
2298   handle->namestore_handle = GNUNET_NAMESTORE_connect (cfg);
2299   handle->timeout_task =
2300     GNUNET_SCHEDULER_add_delayed (handle->timeout, &do_timeout, handle);
2301   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connected\n");
2302 }
2303
2304
2305 /**
2306  * Entry point for the plugin.
2307  *
2308  * @param cls Config info
2309  * @return NULL on error, otherwise the plugin context
2310  */
2311 void *
2312 libgnunet_plugin_rest_openid_connect_init (void *cls)
2313 {
2314   static struct Plugin plugin;
2315   struct GNUNET_REST_Plugin *api;
2316
2317   cfg = cls;
2318   if (NULL != plugin.cfg)
2319     return NULL; /* can only initialize once! */
2320   memset (&plugin, 0, sizeof(struct Plugin));
2321   plugin.cfg = cfg;
2322   api = GNUNET_new (struct GNUNET_REST_Plugin);
2323   api->cls = &plugin;
2324   api->name = GNUNET_REST_API_NS_OIDC;
2325   api->process_request = &rest_identity_process_request;
2326   GNUNET_asprintf (&allow_methods,
2327                    "%s, %s, %s, %s, %s",
2328                    MHD_HTTP_METHOD_GET,
2329                    MHD_HTTP_METHOD_POST,
2330                    MHD_HTTP_METHOD_PUT,
2331                    MHD_HTTP_METHOD_DELETE,
2332                    MHD_HTTP_METHOD_OPTIONS);
2333
2334   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2335               _ ("OpenID Connect REST API initialized\n"));
2336   return api;
2337 }
2338
2339
2340 /**
2341  * Exit point from the plugin.
2342  *
2343  * @param cls the plugin context (as returned by "init")
2344  * @return always NULL
2345  */
2346 void *
2347 libgnunet_plugin_rest_openid_connect_done (void *cls)
2348 {
2349   struct GNUNET_REST_Plugin *api = cls;
2350   struct Plugin *plugin = api->cls;
2351
2352   plugin->cfg = NULL;
2353
2354   struct GNUNET_CONTAINER_MultiHashMapIterator *hashmap_it;
2355   void *value = NULL;
2356   hashmap_it =
2357     GNUNET_CONTAINER_multihashmap_iterator_create (OIDC_cookie_jar_map);
2358   while (GNUNET_YES ==
2359          GNUNET_CONTAINER_multihashmap_iterator_next (hashmap_it, NULL, value))
2360     GNUNET_free_non_null (value);
2361   GNUNET_CONTAINER_multihashmap_iterator_destroy (hashmap_it);
2362   GNUNET_CONTAINER_multihashmap_destroy (OIDC_cookie_jar_map);
2363
2364   hashmap_it =
2365     GNUNET_CONTAINER_multihashmap_iterator_create (OIDC_access_token_map);
2366   while (GNUNET_YES ==
2367          GNUNET_CONTAINER_multihashmap_iterator_next (hashmap_it, NULL, value))
2368     GNUNET_free_non_null (value);
2369   GNUNET_CONTAINER_multihashmap_destroy (OIDC_access_token_map);
2370   GNUNET_CONTAINER_multihashmap_iterator_destroy (hashmap_it);
2371   GNUNET_free_non_null (allow_methods);
2372   GNUNET_free (api);
2373   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2374               "OpenID Connect REST plugin is finished\n");
2375   return NULL;
2376 }
2377
2378
2379 /* end of plugin_rest_openid_connect.c */