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