Prepared Listing of References
[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
580 static void
581 cleanup_handle_delayed (void *cls)
582 {
583   cleanup_handle (cls);
584 }
585
586
587 /**
588  * Task run on error, sends error message.  Cleans up everything.
589  *
590  * @param cls the `struct RequestHandle`
591  */
592 static void
593 do_error (void *cls)
594 {
595   struct RequestHandle *handle = cls;
596   struct MHD_Response *resp;
597   char *json_error;
598
599   GNUNET_asprintf (&json_error,
600                    "{ \"error\" : \"%s\", \"error_description\" : \"%s\"%s%s%s}",
601                    handle->emsg,
602                    (NULL != handle->edesc) ? handle->edesc : "",
603                    (NULL != handle->oidc->state) ? ", \"state\":\"" : "",
604                    (NULL != handle->oidc->state) ? handle->oidc->state : "",
605                    (NULL != handle->oidc->state) ? "\"" : "");
606   if (0 == handle->response_code)
607     handle->response_code = MHD_HTTP_BAD_REQUEST;
608   resp = GNUNET_REST_create_response (json_error);
609   if (MHD_HTTP_UNAUTHORIZED == handle->response_code)
610     MHD_add_response_header (resp, MHD_HTTP_HEADER_WWW_AUTHENTICATE, "Basic");
611   MHD_add_response_header (resp,
612                            MHD_HTTP_HEADER_CONTENT_TYPE,
613                            "application/json");
614   handle->proc (handle->proc_cls, resp, handle->response_code);
615   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
616   GNUNET_free (json_error);
617 }
618
619
620 /**
621  * Task run on error in userinfo endpoint, sends error header. Cleans up
622  * everything
623  *
624  * @param cls the `struct RequestHandle`
625  */
626 static void
627 do_userinfo_error (void *cls)
628 {
629   struct RequestHandle *handle = cls;
630   struct MHD_Response *resp;
631   char *error;
632
633   GNUNET_asprintf (&error,
634                    "error=\"%s\", error_description=\"%s\"",
635                    handle->emsg,
636                    (NULL != handle->edesc) ? handle->edesc : "");
637   resp = GNUNET_REST_create_response ("");
638   MHD_add_response_header (resp, MHD_HTTP_HEADER_WWW_AUTHENTICATE, "Bearer");
639   handle->proc (handle->proc_cls, resp, handle->response_code);
640   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
641   GNUNET_free (error);
642 }
643
644
645 /**
646  * Task run on error, sends error message and redirects. Cleans up everything.
647  *
648  * @param cls the `struct RequestHandle`
649  */
650 static void
651 do_redirect_error (void *cls)
652 {
653   struct RequestHandle *handle = cls;
654   struct MHD_Response *resp;
655   char *redirect;
656
657   GNUNET_asprintf (&redirect,
658                    "%s?error=%s&error_description=%s%s%s",
659                    handle->oidc->redirect_uri,
660                    handle->emsg,
661                    handle->edesc,
662                    (NULL != handle->oidc->state) ? "&state=" : "",
663                    (NULL != handle->oidc->state) ? handle->oidc->state : "");
664   resp = GNUNET_REST_create_response ("");
665   MHD_add_response_header (resp, "Location", redirect);
666   handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
667   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
668   GNUNET_free (redirect);
669 }
670
671
672 /**
673  * Task run on timeout, sends error message.  Cleans up everything.
674  *
675  * @param cls the `struct RequestHandle`
676  */
677 static void
678 do_timeout (void *cls)
679 {
680   struct RequestHandle *handle = cls;
681
682   handle->timeout_task = NULL;
683   do_error (handle);
684 }
685
686
687 /**
688  * Return attributes for claim
689  *
690  * @param cls the request handle
691  */
692 static void
693 return_userinfo_response (void *cls)
694 {
695   char *result_str;
696   struct RequestHandle *handle = cls;
697   struct MHD_Response *resp;
698
699   result_str = json_dumps (handle->oidc->response, 0);
700
701   resp = GNUNET_REST_create_response (result_str);
702   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
703   GNUNET_free (result_str);
704   cleanup_handle (handle);
705 }
706
707
708 /**
709  * Respond to OPTIONS request
710  *
711  * @param con_handle the connection handle
712  * @param url the url
713  * @param cls the RequestHandle
714  */
715 static void
716 options_cont (struct GNUNET_REST_RequestHandle *con_handle,
717               const char *url,
718               void *cls)
719 {
720   struct MHD_Response *resp;
721   struct RequestHandle *handle = cls;
722
723   // For now, independent of path return all options
724   resp = GNUNET_REST_create_response (NULL);
725   MHD_add_response_header (resp, "Access-Control-Allow-Methods", allow_methods);
726   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
727   cleanup_handle (handle);
728   return;
729 }
730
731
732 /**
733  * Interprets cookie header and pass its identity keystring to handle
734  */
735 static void
736 cookie_identity_interpretation (struct RequestHandle *handle)
737 {
738   struct GNUNET_HashCode cache_key;
739   char *cookies;
740   struct GNUNET_TIME_Absolute current_time, *relog_time;
741   char delimiter[] = "; ";
742   char *tmp_cookies;
743   char *token;
744   char *value;
745
746   // gets identity of login try with cookie
747   GNUNET_CRYPTO_hash (OIDC_COOKIE_HEADER_KEY,
748                       strlen (OIDC_COOKIE_HEADER_KEY),
749                       &cache_key);
750   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
751                                                            ->header_param_map,
752                                                            &cache_key))
753   {
754     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "No cookie found\n");
755     return;
756   }
757   // splits cookies and find 'Identity' cookie
758   tmp_cookies =
759     GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->header_param_map,
760                                        &cache_key);
761   cookies = GNUNET_strdup (tmp_cookies);
762   token = strtok (cookies, delimiter);
763   handle->oidc->user_cancelled = GNUNET_NO;
764   handle->oidc->login_identity = NULL;
765   if (NULL == token)
766   {
767     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
768                 "Unable to parse cookie: %s\n",
769                 cookies);
770     GNUNET_free (cookies);
771     return;
772   }
773
774   while (NULL != token)
775   {
776     if (0 == strcmp (token, OIDC_COOKIE_HEADER_ACCESS_DENIED))
777     {
778       handle->oidc->user_cancelled = GNUNET_YES;
779       GNUNET_free (cookies);
780       return;
781     }
782     if (NULL != strstr (token, OIDC_COOKIE_HEADER_INFORMATION_KEY))
783       break;
784     token = strtok (NULL, delimiter);
785   }
786   if (NULL == token)
787   {
788     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
789                 "No cookie value to process: %s\n",
790                 cookies);
791     GNUNET_free (cookies);
792     return;
793   }
794   GNUNET_CRYPTO_hash (token, strlen (token), &cache_key);
795   if (GNUNET_NO ==
796       GNUNET_CONTAINER_multihashmap_contains (OIDC_cookie_jar_map, &cache_key))
797   {
798     GNUNET_log (
799       GNUNET_ERROR_TYPE_WARNING,
800       "Found cookie `%s', but no corresponding expiration entry present...\n",
801       token);
802     GNUNET_free (cookies);
803     return;
804   }
805   relog_time =
806     GNUNET_CONTAINER_multihashmap_get (OIDC_cookie_jar_map, &cache_key);
807   current_time = GNUNET_TIME_absolute_get ();
808   // 30 min after old login -> redirect to login
809   if (current_time.abs_value_us > relog_time->abs_value_us)
810   {
811     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
812                 "Found cookie `%s', but it is expired.\n",
813                 token);
814     GNUNET_free (cookies);
815     return;
816   }
817   value = strtok (token, OIDC_COOKIE_HEADER_INFORMATION_KEY);
818   GNUNET_assert (NULL != value);
819   handle->oidc->login_identity = GNUNET_strdup (value);
820   GNUNET_free (cookies);
821 }
822
823
824 /**
825  * Redirects to login page stored in configuration file
826  */
827 static void
828 login_redirect (void *cls)
829 {
830   char *login_base_url;
831   char *new_redirect;
832   struct MHD_Response *resp;
833   struct RequestHandle *handle = cls;
834
835   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg,
836                                                           "reclaim-rest-plugin",
837                                                           "address",
838                                                           &login_base_url))
839   {
840     GNUNET_asprintf (&new_redirect,
841                      "%s?%s=%s&%s=%s&%s=%s&%s=%s&%s=%s&%s=%s&%s=%s",
842                      login_base_url,
843                      OIDC_RESPONSE_TYPE_KEY,
844                      handle->oidc->response_type,
845                      OIDC_CLIENT_ID_KEY,
846                      handle->oidc->client_id,
847                      OIDC_REDIRECT_URI_KEY,
848                      handle->oidc->redirect_uri,
849                      OIDC_SCOPE_KEY,
850                      handle->oidc->scope,
851                      OIDC_STATE_KEY,
852                      (NULL != handle->oidc->state) ? handle->oidc->state : "",
853                      OIDC_CODE_CHALLENGE_KEY,
854                      (NULL != handle->oidc->code_challenge) ?
855                      handle->oidc->code_challenge : "",
856                      OIDC_NONCE_KEY,
857                      (NULL != handle->oidc->nonce) ? handle->oidc->nonce : "");
858     resp = GNUNET_REST_create_response ("");
859     MHD_add_response_header (resp, "Location", new_redirect);
860     GNUNET_free (login_base_url);
861   }
862   else
863   {
864     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
865     handle->edesc = GNUNET_strdup ("gnunet configuration failed");
866     handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
867     GNUNET_SCHEDULER_add_now (&do_error, handle);
868     return;
869   }
870   handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
871   GNUNET_free (new_redirect);
872   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
873 }
874
875
876 /**
877  * Does internal server error when iteration failed.
878  */
879 static void
880 oidc_iteration_error (void *cls)
881 {
882   struct RequestHandle *handle = cls;
883
884   handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
885   handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
886   GNUNET_SCHEDULER_add_now (&do_error, handle);
887 }
888
889
890 /**
891  * Issues ticket and redirects to relying party with the authorization code as
892  * parameter. Otherwise redirects with error
893  */
894 static void
895 oidc_ticket_issue_cb (void *cls, const struct GNUNET_RECLAIM_Ticket *ticket)
896 {
897   struct RequestHandle *handle = cls;
898   struct MHD_Response *resp;
899   char *ticket_str;
900   char *redirect_uri;
901   char *code_string;
902
903   handle->idp_op = NULL;
904   if (NULL == ticket)
905   {
906     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
907     handle->edesc = GNUNET_strdup ("Server cannot generate ticket.");
908     GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
909     return;
910   }
911   handle->ticket = *ticket;
912   ticket_str =
913     GNUNET_STRINGS_data_to_string_alloc (&handle->ticket,
914                                          sizeof(struct GNUNET_RECLAIM_Ticket));
915   // TODO change if more attributes are needed (see max_age)
916   code_string = OIDC_build_authz_code (&handle->priv_key,
917                                        &handle->ticket,
918                                        handle->attr_list,
919                                        handle->oidc->nonce,
920                                        handle->oidc->code_challenge);
921   if ((NULL != handle->redirect_prefix) && (NULL != handle->redirect_suffix) &&
922       (NULL != handle->tld))
923   {
924     GNUNET_asprintf (&redirect_uri,
925                      "%s.%s/%s?%s=%s&state=%s",
926                      handle->redirect_prefix,
927                      handle->tld,
928                      handle->redirect_suffix,
929                      handle->oidc->response_type,
930                      code_string,
931                      handle->oidc->state);
932   }
933   else
934   {
935     GNUNET_asprintf (&redirect_uri,
936                      "%s?%s=%s&state=%s",
937                      handle->oidc->redirect_uri,
938                      handle->oidc->response_type,
939                      code_string,
940                      handle->oidc->state);
941   }
942   resp = GNUNET_REST_create_response ("");
943   MHD_add_response_header (resp, "Location", redirect_uri);
944   handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
945   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
946   GNUNET_free (redirect_uri);
947   GNUNET_free (ticket_str);
948   GNUNET_free (code_string);
949 }
950
951
952 static void
953 oidc_collect_finished_cb (void *cls)
954 {
955   struct RequestHandle *handle = cls;
956
957   handle->attr_it = NULL;
958   handle->ticket_it = NULL;
959   if (NULL == handle->attr_list->list_head)
960   {
961     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_SCOPE);
962     handle->edesc = GNUNET_strdup ("The requested scope is not available.");
963     GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
964     return;
965   }
966   handle->idp_op = GNUNET_RECLAIM_ticket_issue (handle->idp,
967                                                 &handle->priv_key,
968                                                 &handle->oidc->client_pkey,
969                                                 handle->attr_list,
970                                                 &oidc_ticket_issue_cb,
971                                                 handle);
972 }
973
974
975 /**
976  * Collects all attributes for an ego if in scope parameter
977  */
978 static void
979 oidc_attr_collect (void *cls,
980                    const struct GNUNET_CRYPTO_EcdsaPublicKey *identity,
981                    const struct GNUNET_RECLAIM_ATTRIBUTE_Claim *attr,
982                    const struct GNUNET_RECLAIM_ATTESTATION_Claim *attest,
983                    const struct GNUNET_RECLAIM_ATTESTATION_REFERENCE *reference)
984 {
985   struct RequestHandle *handle = cls;
986   struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *le;
987   char *scope_variables;
988   char *scope_variable;
989   char delimiter[] = " ";
990
991   if ((NULL == attr->name) || (NULL == attr->data))
992   {
993     GNUNET_RECLAIM_get_attributes_next (handle->attr_it);
994     return;
995   }
996
997   scope_variables = GNUNET_strdup (handle->oidc->scope);
998   scope_variable = strtok (scope_variables, delimiter);
999   while (NULL != scope_variable)
1000   {
1001     if (0 == strcmp (attr->name, scope_variable))
1002       break;
1003     scope_variable = strtok (NULL, delimiter);
1004   }
1005   if (NULL == scope_variable)
1006   {
1007     GNUNET_RECLAIM_get_attributes_next (handle->attr_it);
1008     GNUNET_free (scope_variables);
1009     return;
1010   }
1011   GNUNET_free (scope_variables);
1012
1013   le = GNUNET_new (struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry);
1014   le->claim = GNUNET_RECLAIM_ATTRIBUTE_claim_new (attr->name,
1015                                                   attr->type,
1016                                                   attr->data,
1017                                                   attr->data_size);
1018   le->claim->id = attr->id;
1019   le->claim->flag = attr->flag;
1020   GNUNET_CONTAINER_DLL_insert (handle->attr_list->list_head,
1021                                handle->attr_list->list_tail,
1022                                le);
1023   GNUNET_RECLAIM_get_attributes_next (handle->attr_it);
1024 }
1025
1026
1027 /**
1028  * Checks time and cookie and redirects accordingly
1029  */
1030 static void
1031 code_redirect (void *cls)
1032 {
1033   struct RequestHandle *handle = cls;
1034   struct GNUNET_TIME_Absolute current_time;
1035   struct GNUNET_TIME_Absolute *relog_time;
1036   struct GNUNET_CRYPTO_EcdsaPublicKey pubkey;
1037   struct GNUNET_CRYPTO_EcdsaPublicKey ego_pkey;
1038   struct GNUNET_HashCode cache_key;
1039   char *identity_cookie;
1040
1041   GNUNET_asprintf (&identity_cookie,
1042                    "Identity=%s",
1043                    handle->oidc->login_identity);
1044   GNUNET_CRYPTO_hash (identity_cookie, strlen (identity_cookie), &cache_key);
1045   GNUNET_free (identity_cookie);
1046   // No login time for identity -> redirect to login
1047   if (GNUNET_YES ==
1048       GNUNET_CONTAINER_multihashmap_contains (OIDC_cookie_jar_map, &cache_key))
1049   {
1050     relog_time =
1051       GNUNET_CONTAINER_multihashmap_get (OIDC_cookie_jar_map, &cache_key);
1052     current_time = GNUNET_TIME_absolute_get ();
1053     // 30 min after old login -> redirect to login
1054     if (current_time.abs_value_us <= relog_time->abs_value_us)
1055     {
1056       if (GNUNET_OK !=
1057           GNUNET_CRYPTO_ecdsa_public_key_from_string (handle->oidc
1058                                                       ->login_identity,
1059                                                       strlen (
1060                                                         handle->oidc
1061                                                         ->login_identity),
1062                                                       &pubkey))
1063       {
1064         handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_COOKIE);
1065         handle->edesc =
1066           GNUNET_strdup ("The cookie of a login identity is not valid");
1067         GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1068         return;
1069       }
1070       // iterate over egos and compare their public key
1071       for (handle->ego_entry = handle->ego_head; NULL != handle->ego_entry;
1072            handle->ego_entry = handle->ego_entry->next)
1073       {
1074         GNUNET_IDENTITY_ego_get_public_key (handle->ego_entry->ego, &ego_pkey);
1075         if (0 == GNUNET_memcmp (&ego_pkey, &pubkey))
1076         {
1077           handle->priv_key =
1078             *GNUNET_IDENTITY_ego_get_private_key (handle->ego_entry->ego);
1079           handle->idp = GNUNET_RECLAIM_connect (cfg);
1080           handle->attr_list =
1081             GNUNET_new (struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList);
1082           handle->attr_it =
1083             GNUNET_RECLAIM_get_attributes_start (handle->idp,
1084                                                  &handle->priv_key,
1085                                                  &oidc_iteration_error,
1086                                                  handle,
1087                                                  &oidc_attr_collect,
1088                                                  handle,
1089                                                  &oidc_collect_finished_cb,
1090                                                  handle);
1091           return;
1092         }
1093       }
1094       GNUNET_SCHEDULER_add_now (&login_redirect, handle);
1095       return;
1096     }
1097   }
1098 }
1099
1100
1101 static void
1102 build_redirect (void *cls)
1103 {
1104   struct RequestHandle *handle = cls;
1105   struct MHD_Response *resp;
1106   char *redirect_uri;
1107
1108   if (GNUNET_YES == handle->oidc->user_cancelled)
1109   {
1110     if ((NULL != handle->redirect_prefix) &&
1111         (NULL != handle->redirect_suffix) && (NULL != handle->tld))
1112     {
1113       GNUNET_asprintf (&redirect_uri,
1114                        "%s.%s/%s?error=%s&error_description=%s&state=%s",
1115                        handle->redirect_prefix,
1116                        handle->tld,
1117                        handle->redirect_suffix,
1118                        "access_denied",
1119                        "User denied access",
1120                        handle->oidc->state);
1121     }
1122     else
1123     {
1124       GNUNET_asprintf (&redirect_uri,
1125                        "%s?error=%s&error_description=%s&state=%s",
1126                        handle->oidc->redirect_uri,
1127                        "access_denied",
1128                        "User denied access",
1129                        handle->oidc->state);
1130     }
1131     resp = GNUNET_REST_create_response ("");
1132     MHD_add_response_header (resp, "Location", redirect_uri);
1133     handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
1134     GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
1135     GNUNET_free (redirect_uri);
1136     return;
1137   }
1138   GNUNET_SCHEDULER_add_now (&code_redirect, handle);
1139 }
1140
1141
1142 static void
1143 lookup_redirect_uri_result (void *cls,
1144                             uint32_t rd_count,
1145                             const struct GNUNET_GNSRECORD_Data *rd)
1146 {
1147   struct RequestHandle *handle = cls;
1148   char *tmp;
1149   char *tmp_key_str;
1150   char *pos;
1151   struct GNUNET_CRYPTO_EcdsaPublicKey redirect_zone;
1152
1153   handle->gns_op = NULL;
1154   if (0 == rd_count)
1155   {
1156     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
1157     handle->edesc =
1158       GNUNET_strdup ("Server cannot generate ticket, redirect uri not found.");
1159     GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1160     return;
1161   }
1162   for (int i = 0; i < rd_count; i++)
1163   {
1164     if (GNUNET_GNSRECORD_TYPE_RECLAIM_OIDC_REDIRECT != rd[i].record_type)
1165       continue;
1166     if (0 != strncmp (rd[i].data, handle->oidc->redirect_uri, rd[i].data_size))
1167       continue;
1168     tmp = GNUNET_strndup (rd[i].data, rd[i].data_size);
1169     if (NULL == strstr (tmp, handle->oidc->client_id))
1170     {
1171       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1172                   "Redirect uri %s does not contain client_id %s\n",
1173                   tmp,
1174                   handle->oidc->client_id);
1175     }
1176     else
1177     {
1178       pos = strrchr (tmp, (unsigned char) '.');
1179       if (NULL == pos)
1180       {
1181         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1182                     "Redirect uri %s contains client_id but is malformed\n",
1183                     tmp);
1184         GNUNET_free (tmp);
1185         continue;
1186       }
1187       *pos = '\0';
1188       handle->redirect_prefix = GNUNET_strdup (tmp);
1189       tmp_key_str = pos + 1;
1190       pos = strchr (tmp_key_str, (unsigned char) '/');
1191       if (NULL == pos)
1192       {
1193         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1194                     "Redirect uri %s contains client_id but is malformed\n",
1195                     tmp);
1196         GNUNET_free (tmp);
1197         continue;
1198       }
1199       *pos = '\0';
1200       handle->redirect_suffix = GNUNET_strdup (pos + 1);
1201
1202       GNUNET_STRINGS_string_to_data (tmp_key_str,
1203                                      strlen (tmp_key_str),
1204                                      &redirect_zone,
1205                                      sizeof(redirect_zone));
1206     }
1207     GNUNET_SCHEDULER_add_now (&build_redirect, handle);
1208     GNUNET_free (tmp);
1209     return;
1210   }
1211   handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
1212   handle->edesc =
1213     GNUNET_strdup ("Server cannot generate ticket, redirect uri not found.");
1214   GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1215 }
1216
1217
1218 /**
1219  * Initiate redirect back to client.
1220  */
1221 static void
1222 client_redirect (void *cls)
1223 {
1224   struct RequestHandle *handle = cls;
1225
1226   /* Lookup client redirect uri to verify request */
1227   handle->gns_op =
1228     GNUNET_GNS_lookup (handle->gns_handle,
1229                        GNUNET_GNS_EMPTY_LABEL_AT,
1230                        &handle->oidc->client_pkey,
1231                        GNUNET_GNSRECORD_TYPE_RECLAIM_OIDC_REDIRECT,
1232                        GNUNET_GNS_LO_DEFAULT,
1233                        &lookup_redirect_uri_result,
1234                        handle);
1235 }
1236
1237
1238 static char *
1239 get_url_parameter_copy (const struct RequestHandle *handle, const char *key)
1240 {
1241   struct GNUNET_HashCode hc;
1242   char *value;
1243
1244   GNUNET_CRYPTO_hash (key, strlen (key), &hc);
1245   if (GNUNET_YES != GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
1246                                                             ->url_param_map,
1247                                                             &hc))
1248     return NULL;
1249   value =
1250     GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map, &hc);
1251   if (NULL == value)
1252     return NULL;
1253   return GNUNET_strdup (value);
1254 }
1255
1256
1257 /**
1258  * Iteration over all results finished, build final
1259  * response.
1260  *
1261  * @param cls the `struct RequestHandle`
1262  */
1263 static void
1264 build_authz_response (void *cls)
1265 {
1266   struct RequestHandle *handle = cls;
1267   struct GNUNET_HashCode cache_key;
1268
1269   char *expected_scope;
1270   char delimiter[] = " ";
1271   int number_of_ignored_parameter, iterator;
1272
1273
1274   // REQUIRED value: redirect_uri
1275   handle->oidc->redirect_uri =
1276     get_url_parameter_copy (handle, OIDC_REDIRECT_URI_KEY);
1277   if (NULL == handle->oidc->redirect_uri)
1278   {
1279     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1280     handle->edesc = GNUNET_strdup ("missing parameter redirect_uri");
1281     GNUNET_SCHEDULER_add_now (&do_error, handle);
1282     return;
1283   }
1284
1285   // REQUIRED value: response_type
1286   handle->oidc->response_type =
1287     get_url_parameter_copy (handle, OIDC_RESPONSE_TYPE_KEY);
1288   if (NULL == handle->oidc->response_type)
1289   {
1290     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1291     handle->edesc = GNUNET_strdup ("missing parameter response_type");
1292     GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1293     return;
1294   }
1295
1296   // REQUIRED value: scope
1297   handle->oidc->scope = get_url_parameter_copy (handle, OIDC_SCOPE_KEY);
1298   if (NULL == handle->oidc->scope)
1299   {
1300     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_SCOPE);
1301     handle->edesc = GNUNET_strdup ("missing parameter scope");
1302     GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1303     return;
1304   }
1305
1306   // OPTIONAL value: nonce
1307   handle->oidc->nonce = get_url_parameter_copy (handle, OIDC_NONCE_KEY);
1308
1309   // TODO check other values if needed
1310   number_of_ignored_parameter =
1311     sizeof(OIDC_ignored_parameter_array) / sizeof(char *);
1312   for (iterator = 0; iterator < number_of_ignored_parameter; iterator++)
1313   {
1314     GNUNET_CRYPTO_hash (OIDC_ignored_parameter_array[iterator],
1315                         strlen (OIDC_ignored_parameter_array[iterator]),
1316                         &cache_key);
1317     if (GNUNET_YES ==
1318         GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
1319                                                 ->url_param_map,
1320                                                 &cache_key))
1321     {
1322       handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_ACCESS_DENIED);
1323       GNUNET_asprintf (&handle->edesc,
1324                        "Server will not handle parameter: %s",
1325                        OIDC_ignored_parameter_array[iterator]);
1326       GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1327       return;
1328     }
1329   }
1330
1331   // We only support authorization code flows.
1332   if (0 != strcmp (handle->oidc->response_type,
1333                    OIDC_EXPECTED_AUTHORIZATION_RESPONSE_TYPE))
1334   {
1335     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_UNSUPPORTED_RESPONSE_TYPE);
1336     handle->edesc = GNUNET_strdup ("The authorization server does not support "
1337                                    "obtaining this authorization code.");
1338     GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1339     return;
1340   }
1341
1342   // Checks if scope contains 'openid'
1343   expected_scope = GNUNET_strdup (handle->oidc->scope);
1344   char *test;
1345   test = strtok (expected_scope, delimiter);
1346   while (NULL != test)
1347   {
1348     if (0 == strcmp (OIDC_EXPECTED_AUTHORIZATION_SCOPE, expected_scope))
1349       break;
1350     test = strtok (NULL, delimiter);
1351   }
1352   if (NULL == test)
1353   {
1354     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_SCOPE);
1355     handle->edesc =
1356       GNUNET_strdup ("The requested scope is invalid, unknown, or malformed.");
1357     GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1358     GNUNET_free (expected_scope);
1359     return;
1360   }
1361
1362   GNUNET_free (expected_scope);
1363   if ((NULL == handle->oidc->login_identity) &&
1364       (GNUNET_NO == handle->oidc->user_cancelled))
1365     GNUNET_SCHEDULER_add_now (&login_redirect, handle);
1366   else
1367     GNUNET_SCHEDULER_add_now (&client_redirect, handle);
1368 }
1369
1370
1371 /**
1372  * Iterate over tlds in config
1373  */
1374 static void
1375 tld_iter (void *cls, const char *section, const char *option, const char *value)
1376 {
1377   struct RequestHandle *handle = cls;
1378   struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
1379
1380   if (GNUNET_OK !=
1381       GNUNET_CRYPTO_ecdsa_public_key_from_string (value, strlen (value), &pkey))
1382   {
1383     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Skipping non key %s\n", value);
1384     return;
1385   }
1386   if (0 == GNUNET_memcmp (&pkey, &handle->oidc->client_pkey))
1387     handle->tld = GNUNET_strdup (option + 1);
1388 }
1389
1390
1391 /**
1392  * Responds to authorization GET and url-encoded POST request
1393  *
1394  * @param con_handle the connection handle
1395  * @param url the url
1396  * @param cls the RequestHandle
1397  */
1398 static void
1399 authorize_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
1400                     const char *url,
1401                     void *cls)
1402 {
1403   struct RequestHandle *handle = cls;
1404   struct EgoEntry *tmp_ego;
1405   const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key;
1406   struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
1407
1408   cookie_identity_interpretation (handle);
1409
1410   // RECOMMENDED value: state - REQUIRED for answers
1411   handle->oidc->state = get_url_parameter_copy (handle, OIDC_STATE_KEY);
1412
1413   // REQUIRED value: client_id
1414   handle->oidc->client_id = get_url_parameter_copy (handle, OIDC_CLIENT_ID_KEY);
1415   if (NULL == handle->oidc->client_id)
1416   {
1417     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1418     handle->edesc = GNUNET_strdup ("missing parameter client_id");
1419     handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1420     GNUNET_SCHEDULER_add_now (&do_error, handle);
1421     return;
1422   }
1423
1424   // OPTIONAL value: code_challenge
1425   handle->oidc->code_challenge = get_url_parameter_copy (handle,
1426                                                          OIDC_CODE_CHALLENGE_KEY);
1427   if (NULL == handle->oidc->code_challenge)
1428   {
1429     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1430                 "OAuth authorization request does not contain PKCE parameters!\n");
1431   }
1432
1433   if (GNUNET_OK !=
1434       GNUNET_CRYPTO_ecdsa_public_key_from_string (handle->oidc->client_id,
1435                                                   strlen (
1436                                                     handle->oidc->client_id),
1437                                                   &handle->oidc->client_pkey))
1438   {
1439     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_UNAUTHORIZED_CLIENT);
1440     handle->edesc = GNUNET_strdup ("The client is not authorized to request an "
1441                                    "authorization code using this method.");
1442     handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1443     GNUNET_SCHEDULER_add_now (&do_error, handle);
1444     return;
1445   }
1446
1447   // If we know this identity, translated the corresponding TLD
1448   // TODO: We might want to have a reverse lookup functionality for TLDs?
1449   for (tmp_ego = handle->ego_head; NULL != tmp_ego; tmp_ego = tmp_ego->next)
1450   {
1451     priv_key = GNUNET_IDENTITY_ego_get_private_key (tmp_ego->ego);
1452     GNUNET_CRYPTO_ecdsa_key_get_public (priv_key, &pkey);
1453     if (0 == GNUNET_memcmp (&pkey, &handle->oidc->client_pkey))
1454     {
1455       handle->tld = GNUNET_strdup (tmp_ego->identifier);
1456       handle->ego_entry = handle->ego_tail;
1457     }
1458   }
1459   if (NULL == handle->tld)
1460     GNUNET_CONFIGURATION_iterate_section_values (cfg, "gns", tld_iter, handle);
1461   if (NULL == handle->tld)
1462     handle->tld = GNUNET_strdup (handle->oidc->client_id);
1463   GNUNET_SCHEDULER_add_now (&build_authz_response, handle);
1464 }
1465
1466
1467 /**
1468  * Combines an identity with a login time and responds OK to login request
1469  *
1470  * @param con_handle the connection handle
1471  * @param url the url
1472  * @param cls the RequestHandle
1473  */
1474 static void
1475 login_cont (struct GNUNET_REST_RequestHandle *con_handle,
1476             const char *url,
1477             void *cls)
1478 {
1479   struct MHD_Response *resp = GNUNET_REST_create_response ("");
1480   struct RequestHandle *handle = cls;
1481   struct GNUNET_HashCode cache_key;
1482   struct GNUNET_TIME_Absolute *current_time;
1483   struct GNUNET_TIME_Absolute *last_time;
1484   char *cookie;
1485   char *header_val;
1486   json_t *root;
1487   json_error_t error;
1488   json_t *identity;
1489   char term_data[handle->rest_handle->data_size + 1];
1490
1491   term_data[handle->rest_handle->data_size] = '\0';
1492   GNUNET_memcpy (term_data,
1493                  handle->rest_handle->data,
1494                  handle->rest_handle->data_size);
1495   root = json_loads (term_data, JSON_DECODE_ANY, &error);
1496   identity = json_object_get (root, "identity");
1497   if (! json_is_string (identity))
1498   {
1499     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1500                 "Error parsing json string from %s\n",
1501                 term_data);
1502     handle->proc (handle->proc_cls, resp, MHD_HTTP_BAD_REQUEST);
1503     json_decref (root);
1504     GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
1505     return;
1506   }
1507   GNUNET_asprintf (&cookie, "Identity=%s", json_string_value (identity));
1508   GNUNET_asprintf (&header_val,
1509                    "%s;Max-Age=%d",
1510                    cookie,
1511                    OIDC_COOKIE_EXPIRATION);
1512   MHD_add_response_header (resp, "Set-Cookie", header_val);
1513   MHD_add_response_header (resp, "Access-Control-Allow-Methods", "POST");
1514   GNUNET_CRYPTO_hash (cookie, strlen (cookie), &cache_key);
1515
1516   if (0 != strcmp (json_string_value (identity), "Denied"))
1517   {
1518     current_time = GNUNET_new (struct GNUNET_TIME_Absolute);
1519     *current_time = GNUNET_TIME_relative_to_absolute (
1520       GNUNET_TIME_relative_multiply (GNUNET_TIME_relative_get_second_ (),
1521                                      OIDC_COOKIE_EXPIRATION));
1522     last_time =
1523       GNUNET_CONTAINER_multihashmap_get (OIDC_cookie_jar_map, &cache_key);
1524     GNUNET_free_non_null (last_time);
1525     GNUNET_CONTAINER_multihashmap_put (OIDC_cookie_jar_map,
1526                                        &cache_key,
1527                                        current_time,
1528                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
1529   }
1530   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
1531   GNUNET_free (cookie);
1532   GNUNET_free (header_val);
1533   json_decref (root);
1534   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
1535 }
1536
1537
1538 static int
1539 check_authorization (struct RequestHandle *handle,
1540                      struct GNUNET_CRYPTO_EcdsaPublicKey *cid)
1541 {
1542   struct GNUNET_HashCode cache_key;
1543   char *authorization;
1544   char *credentials;
1545   char *basic_authorization;
1546   char *client_id;
1547   char *pass;
1548   char *expected_pass;
1549
1550   GNUNET_CRYPTO_hash (OIDC_AUTHORIZATION_HEADER_KEY,
1551                       strlen (OIDC_AUTHORIZATION_HEADER_KEY),
1552                       &cache_key);
1553   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
1554                                                            ->header_param_map,
1555                                                            &cache_key))
1556   {
1557     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1558     handle->edesc = GNUNET_strdup ("missing authorization");
1559     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1560     return GNUNET_SYSERR;
1561   }
1562   authorization =
1563     GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->header_param_map,
1564                                        &cache_key);
1565
1566   // split header in "Basic" and [content]
1567   credentials = strtok (authorization, " ");
1568   if ((NULL == credentials) || (0 != strcmp ("Basic", credentials)))
1569   {
1570     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1571     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1572     return GNUNET_SYSERR;
1573   }
1574   credentials = strtok (NULL, " ");
1575   if (NULL == credentials)
1576   {
1577     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1578     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1579     return GNUNET_SYSERR;
1580   }
1581   GNUNET_STRINGS_base64_decode (credentials,
1582                                 strlen (credentials),
1583                                 (void **) &basic_authorization);
1584
1585   if (NULL == basic_authorization)
1586   {
1587     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1588     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1589     return GNUNET_SYSERR;
1590   }
1591   client_id = strtok (basic_authorization, ":");
1592   if (NULL == client_id)
1593   {
1594     GNUNET_free_non_null (basic_authorization);
1595     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1596     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1597     return GNUNET_SYSERR;
1598   }
1599   pass = strtok (NULL, ":");
1600   if (NULL == pass)
1601   {
1602     GNUNET_free_non_null (basic_authorization);
1603     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1604     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1605     return GNUNET_SYSERR;
1606   }
1607
1608   // check client password
1609   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg,
1610                                                           "reclaim-rest-plugin",
1611                                                           "OIDC_CLIENT_SECRET",
1612                                                           &expected_pass))
1613   {
1614     if (0 != strcmp (expected_pass, pass))
1615     {
1616       GNUNET_free_non_null (basic_authorization);
1617       GNUNET_free (expected_pass);
1618       handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1619       handle->response_code = MHD_HTTP_UNAUTHORIZED;
1620       return GNUNET_SYSERR;
1621     }
1622     GNUNET_free (expected_pass);
1623   }
1624   else
1625   {
1626     GNUNET_free_non_null (basic_authorization);
1627     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
1628     handle->edesc = GNUNET_strdup ("gnunet configuration failed");
1629     handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1630     return GNUNET_SYSERR;
1631   }
1632
1633   // check client_id
1634   for (handle->ego_entry = handle->ego_head; NULL != handle->ego_entry;
1635        handle->ego_entry = handle->ego_entry->next)
1636   {
1637     if (0 == strcmp (handle->ego_entry->keystring, client_id))
1638       break;
1639   }
1640   if (NULL == handle->ego_entry)
1641   {
1642     GNUNET_free_non_null (basic_authorization);
1643     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1644     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1645     return GNUNET_SYSERR;
1646   }
1647   GNUNET_STRINGS_string_to_data (client_id,
1648                                  strlen (client_id),
1649                                  cid,
1650                                  sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey));
1651
1652   GNUNET_free (basic_authorization);
1653   return GNUNET_OK;
1654 }
1655
1656
1657 const struct EgoEntry *
1658 find_ego (struct RequestHandle *handle,
1659           struct GNUNET_CRYPTO_EcdsaPublicKey *test_key)
1660 {
1661   struct EgoEntry *ego_entry;
1662   struct GNUNET_CRYPTO_EcdsaPublicKey pub_key;
1663
1664   for (ego_entry = handle->ego_head; NULL != ego_entry;
1665        ego_entry = ego_entry->next)
1666   {
1667     GNUNET_IDENTITY_ego_get_public_key (ego_entry->ego, &pub_key);
1668     if (0 == GNUNET_memcmp (&pub_key, test_key))
1669       return ego_entry;
1670   }
1671   return NULL;
1672 }
1673
1674
1675 static void
1676 persist_access_token (const struct RequestHandle *handle,
1677                       const char *access_token,
1678                       const struct GNUNET_RECLAIM_Ticket *ticket)
1679 {
1680   struct GNUNET_HashCode hc;
1681   struct GNUNET_RECLAIM_Ticket *ticketbuf;
1682
1683   GNUNET_CRYPTO_hash (access_token, strlen (access_token), &hc);
1684   ticketbuf = GNUNET_new (struct GNUNET_RECLAIM_Ticket);
1685   *ticketbuf = *ticket;
1686   GNUNET_assert (GNUNET_SYSERR !=
1687                  GNUNET_CONTAINER_multihashmap_put (
1688                    OIDC_access_token_map,
1689                    &hc,
1690                    ticketbuf,
1691                    GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1692 }
1693
1694
1695 /**
1696  * Responds to token url-encoded POST request
1697  *
1698  * @param con_handle the connection handle
1699  * @param url the url
1700  * @param cls the RequestHandle
1701  */
1702 static void
1703 token_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
1704                 const char *url,
1705                 void *cls)
1706 {
1707   struct RequestHandle *handle = cls;
1708   const struct EgoEntry *ego_entry;
1709   struct GNUNET_TIME_Relative expiration_time;
1710   struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *cl;
1711   struct GNUNET_RECLAIM_Ticket ticket;
1712   struct GNUNET_CRYPTO_EcdsaPublicKey cid;
1713   const struct GNUNET_CRYPTO_EcdsaPrivateKey *privkey;
1714   struct GNUNET_HashCode cache_key;
1715   struct MHD_Response *resp;
1716   char *grant_type;
1717   char *code;
1718   char *json_response;
1719   char *id_token;
1720   char *access_token;
1721   char *jwt_secret;
1722   char *nonce;
1723   char *code_verifier;
1724
1725   /*
1726    * Check Authorization
1727    */
1728   if (GNUNET_SYSERR == check_authorization (handle, &cid))
1729   {
1730     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1731                 "OIDC authorization for token endpoint failed\n");
1732     GNUNET_SCHEDULER_add_now (&do_error, handle);
1733     return;
1734   }
1735
1736   /*
1737    * Check parameter
1738    */
1739
1740   // TODO Do not allow multiple equal parameter names
1741   // REQUIRED grant_type
1742   GNUNET_CRYPTO_hash (OIDC_GRANT_TYPE_KEY,
1743                       strlen (OIDC_GRANT_TYPE_KEY),
1744                       &cache_key);
1745   grant_type = get_url_parameter_copy (handle, OIDC_GRANT_TYPE_KEY);
1746   if (NULL == grant_type)
1747   {
1748     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1749     handle->edesc = GNUNET_strdup ("missing parameter grant_type");
1750     handle->response_code = MHD_HTTP_BAD_REQUEST;
1751     GNUNET_SCHEDULER_add_now (&do_error, handle);
1752     return;
1753   }
1754
1755   // Check parameter grant_type == "authorization_code"
1756   if (0 != strcmp (OIDC_GRANT_TYPE_VALUE, grant_type))
1757   {
1758     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_UNSUPPORTED_GRANT_TYPE);
1759     handle->response_code = MHD_HTTP_BAD_REQUEST;
1760     GNUNET_free (grant_type);
1761     GNUNET_SCHEDULER_add_now (&do_error, handle);
1762     return;
1763   }
1764   GNUNET_free (grant_type);
1765   // REQUIRED code
1766   code = get_url_parameter_copy (handle, OIDC_CODE_KEY);
1767   if (NULL == code)
1768   {
1769     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1770     handle->edesc = GNUNET_strdup ("missing parameter code");
1771     handle->response_code = MHD_HTTP_BAD_REQUEST;
1772     GNUNET_SCHEDULER_add_now (&do_error, handle);
1773     return;
1774   }
1775   ego_entry = find_ego (handle, &cid);
1776   if (NULL == ego_entry)
1777   {
1778     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1779     handle->edesc = GNUNET_strdup ("Unknown client");
1780     handle->response_code = MHD_HTTP_BAD_REQUEST;
1781     GNUNET_free (code);
1782     GNUNET_SCHEDULER_add_now (&do_error, handle);
1783     return;
1784   }
1785   privkey = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
1786
1787   // REQUIRED code verifier
1788   code_verifier = get_url_parameter_copy (handle, OIDC_CODE_VERIFIER_KEY);
1789   if (NULL == code_verifier)
1790   {
1791     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1792                 "OAuth authorization request does not contain PKCE parameters!\n");
1793
1794   }
1795
1796   // decode code
1797   if (GNUNET_OK != OIDC_parse_authz_code (privkey, code, code_verifier, &ticket,
1798                                           &cl, &nonce))
1799   {
1800     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1801     handle->edesc = GNUNET_strdup ("invalid code");
1802     handle->response_code = MHD_HTTP_BAD_REQUEST;
1803     GNUNET_free (code);
1804     GNUNET_SCHEDULER_add_now (&do_error, handle);
1805     return;
1806   }
1807   GNUNET_free (code);
1808
1809   // create jwt
1810   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (cfg,
1811                                                         "reclaim-rest-plugin",
1812                                                         "expiration_time",
1813                                                         &expiration_time))
1814   {
1815     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
1816     handle->edesc = GNUNET_strdup ("gnunet configuration failed");
1817     handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1818     GNUNET_SCHEDULER_add_now (&do_error, handle);
1819     return;
1820   }
1821
1822
1823   // TODO OPTIONAL acr,amr,azp
1824   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
1825                                                           "reclaim-rest-plugin",
1826                                                           "jwt_secret",
1827                                                           &jwt_secret))
1828   {
1829     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1830     handle->edesc = GNUNET_strdup ("No signing secret configured!");
1831     handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1832     GNUNET_SCHEDULER_add_now (&do_error, handle);
1833     return;
1834   }
1835   id_token = OIDC_id_token_new (&ticket.audience,
1836                                 &ticket.identity,
1837                                 cl,
1838                                 &expiration_time,
1839                                 (NULL != nonce) ? nonce : NULL,
1840                                 jwt_secret);
1841   access_token = OIDC_access_token_new ();
1842   OIDC_build_token_response (access_token,
1843                              id_token,
1844                              &expiration_time,
1845                              &json_response);
1846
1847   persist_access_token (handle, access_token, &ticket);
1848   resp = GNUNET_REST_create_response (json_response);
1849   MHD_add_response_header (resp, "Cache-Control", "no-store");
1850   MHD_add_response_header (resp, "Pragma", "no-cache");
1851   MHD_add_response_header (resp, "Content-Type", "application/json");
1852   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
1853   GNUNET_RECLAIM_ATTRIBUTE_list_destroy (cl);
1854   GNUNET_free (access_token);
1855   GNUNET_free (json_response);
1856   GNUNET_free (id_token);
1857   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
1858 }
1859
1860
1861 /**
1862  * Collects claims and stores them in handle
1863  */
1864 static void
1865 consume_ticket (void *cls,
1866                 const struct GNUNET_CRYPTO_EcdsaPublicKey *identity,
1867                 const struct GNUNET_RECLAIM_ATTRIBUTE_Claim *attr,
1868                 const struct GNUNET_RECLAIM_ATTESTATION_Claim *attest,
1869                 const struct GNUNET_RECLAIM_ATTESTATION_REFERENCE *reference)
1870 {
1871   struct RequestHandle *handle = cls;
1872   char *tmp_value;
1873   json_t *value;
1874
1875   if (NULL == identity)
1876   {
1877     GNUNET_SCHEDULER_add_now (&return_userinfo_response, handle);
1878     return;
1879   }
1880   tmp_value = GNUNET_RECLAIM_ATTRIBUTE_value_to_string (attr->type,
1881                                                         attr->data,
1882                                                         attr->data_size);
1883   value = json_string (tmp_value);
1884   json_object_set_new (handle->oidc->response, attr->name, value);
1885   GNUNET_free (tmp_value);
1886 }
1887
1888
1889 /**
1890  * Responds to userinfo GET and url-encoded POST request
1891  *
1892  * @param con_handle the connection handle
1893  * @param url the url
1894  * @param cls the RequestHandle
1895  */
1896 static void
1897 userinfo_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
1898                    const char *url,
1899                    void *cls)
1900 {
1901   // TODO expiration time
1902   struct RequestHandle *handle = cls;
1903   char delimiter[] = " ";
1904   struct GNUNET_HashCode cache_key;
1905   char *authorization;
1906   char *authorization_type;
1907   char *authorization_access_token;
1908   struct GNUNET_RECLAIM_Ticket *ticket;
1909   const struct EgoEntry *ego_entry;
1910   const struct GNUNET_CRYPTO_EcdsaPrivateKey *privkey;
1911
1912   GNUNET_CRYPTO_hash (OIDC_AUTHORIZATION_HEADER_KEY,
1913                       strlen (OIDC_AUTHORIZATION_HEADER_KEY),
1914                       &cache_key);
1915   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
1916                                                            ->header_param_map,
1917                                                            &cache_key))
1918   {
1919     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
1920     handle->edesc = GNUNET_strdup ("No Access Token");
1921     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1922     GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
1923     return;
1924   }
1925   authorization =
1926     GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->header_param_map,
1927                                        &cache_key);
1928
1929   // split header in "Bearer" and access_token
1930   authorization = GNUNET_strdup (authorization);
1931   authorization_type = strtok (authorization, delimiter);
1932   if ((NULL == authorization_type) ||
1933       (0 != strcmp ("Bearer", authorization_type)))
1934   {
1935     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
1936     handle->edesc = GNUNET_strdup ("No Access Token");
1937     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1938     GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
1939     GNUNET_free (authorization);
1940     return;
1941   }
1942   authorization_access_token = strtok (NULL, delimiter);
1943   if (NULL == authorization_access_token)
1944   {
1945     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
1946     handle->edesc = GNUNET_strdup ("Access token missing");
1947     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1948     GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
1949     GNUNET_free (authorization);
1950     return;
1951   }
1952
1953   GNUNET_CRYPTO_hash (authorization_access_token,
1954                       strlen (authorization_access_token),
1955                       &cache_key);
1956   if (GNUNET_NO ==
1957       GNUNET_CONTAINER_multihashmap_contains (OIDC_access_token_map,
1958                                               &cache_key))
1959   {
1960     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
1961     handle->edesc = GNUNET_strdup ("The access token expired");
1962     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1963     GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
1964     GNUNET_free (authorization);
1965     return;
1966   }
1967   ticket =
1968     GNUNET_CONTAINER_multihashmap_get (OIDC_access_token_map, &cache_key);
1969   GNUNET_assert (NULL != ticket);
1970   ego_entry = find_ego (handle, &ticket->audience);
1971   if (NULL == ego_entry)
1972   {
1973     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
1974     handle->edesc = GNUNET_strdup ("The access token expired");
1975     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1976     GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
1977     GNUNET_free (authorization);
1978     return;
1979   }
1980
1981   handle->idp = GNUNET_RECLAIM_connect (cfg);
1982   handle->oidc->response = json_object ();
1983   json_object_set_new (handle->oidc->response,
1984                        "sub",
1985                        json_string (ego_entry->keystring));
1986   privkey = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
1987   handle->idp_op = GNUNET_RECLAIM_ticket_consume (handle->idp,
1988                                                   privkey,
1989                                                   ticket,
1990                                                   consume_ticket,
1991                                                   handle);
1992   GNUNET_free (authorization);
1993 }
1994
1995
1996 /**
1997  * Handle rest request
1998  *
1999  * @param handle the request handle
2000  */
2001 static void
2002 init_cont (struct RequestHandle *handle)
2003 {
2004   struct GNUNET_REST_RequestHandlerError err;
2005   static const struct GNUNET_REST_RequestHandler handlers[] =
2006   { { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_AUTHORIZE, &authorize_endpoint },
2007     { MHD_HTTP_METHOD_POST,
2008       GNUNET_REST_API_NS_AUTHORIZE,
2009       &authorize_endpoint },   // url-encoded
2010     { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_LOGIN, &login_cont },
2011     { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_TOKEN, &token_endpoint },
2012     { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_USERINFO, &userinfo_endpoint },
2013     { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_USERINFO, &userinfo_endpoint },
2014     { MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_OIDC, &options_cont },
2015     GNUNET_REST_HANDLER_END };
2016
2017   if (GNUNET_NO ==
2018       GNUNET_REST_handle_request (handle->rest_handle, handlers, &err, handle))
2019   {
2020     handle->response_code = err.error_code;
2021     GNUNET_SCHEDULER_add_now (&do_error, handle);
2022   }
2023 }
2024
2025
2026 /**
2027  * If listing is enabled, prints information about the egos.
2028  *
2029  * This function is initially called for all egos and then again
2030  * whenever a ego's identifier changes or if it is deleted.  At the
2031  * end of the initial pass over all egos, the function is once called
2032  * with 'NULL' for 'ego'. That does NOT mean that the callback won't
2033  * be invoked in the future or that there was an error.
2034  *
2035  * When used with 'GNUNET_IDENTITY_create' or 'GNUNET_IDENTITY_get',
2036  * this function is only called ONCE, and 'NULL' being passed in
2037  * 'ego' does indicate an error (i.e. name is taken or no default
2038  * value is known).  If 'ego' is non-NULL and if '*ctx'
2039  * is set in those callbacks, the value WILL be passed to a subsequent
2040  * call to the identity callback of 'GNUNET_IDENTITY_connect' (if
2041  * that one was not NULL).
2042  *
2043  * When an identity is renamed, this function is called with the
2044  * (known) ego but the NEW identifier.
2045  *
2046  * When an identity is deleted, this function is called with the
2047  * (known) ego and "NULL" for the 'identifier'.  In this case,
2048  * the 'ego' is henceforth invalid (and the 'ctx' should also be
2049  * cleaned up).
2050  *
2051  * @param cls closure
2052  * @param ego ego handle
2053  * @param ctx context for application to store data for this ego
2054  *                 (during the lifetime of this process, initially NULL)
2055  * @param identifier identifier assigned by the user for this ego,
2056  *                   NULL if the user just deleted the ego and it
2057  *                   must thus no longer be used
2058  */
2059 static void
2060 list_ego (void *cls,
2061           struct GNUNET_IDENTITY_Ego *ego,
2062           void **ctx,
2063           const char *identifier)
2064 {
2065   struct RequestHandle *handle = cls;
2066   struct EgoEntry *ego_entry;
2067   struct GNUNET_CRYPTO_EcdsaPublicKey pk;
2068
2069   if ((NULL == ego) && (ID_REST_STATE_INIT == handle->state))
2070   {
2071     handle->state = ID_REST_STATE_POST_INIT;
2072     init_cont (handle);
2073     return;
2074   }
2075   GNUNET_assert (NULL != ego);
2076   if (ID_REST_STATE_INIT == handle->state)
2077
2078   {
2079     ego_entry = GNUNET_new (struct EgoEntry);
2080     GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
2081     ego_entry->keystring = GNUNET_CRYPTO_ecdsa_public_key_to_string (&pk);
2082     ego_entry->ego = ego;
2083     ego_entry->identifier = GNUNET_strdup (identifier);
2084     GNUNET_CONTAINER_DLL_insert_tail (handle->ego_head,
2085                                       handle->ego_tail,
2086                                       ego_entry);
2087     return;
2088   }
2089   /* Ego renamed or added */
2090   if (identifier != NULL)
2091   {
2092     for (ego_entry = handle->ego_head; NULL != ego_entry;
2093          ego_entry = ego_entry->next)
2094     {
2095       if (ego_entry->ego == ego)
2096       {
2097         /* Rename */
2098         GNUNET_free (ego_entry->identifier);
2099         ego_entry->identifier = GNUNET_strdup (identifier);
2100         break;
2101       }
2102     }
2103     if (NULL == ego_entry)
2104     {
2105       /* Add */
2106       ego_entry = GNUNET_new (struct EgoEntry);
2107       GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
2108       ego_entry->keystring = GNUNET_CRYPTO_ecdsa_public_key_to_string (&pk);
2109       ego_entry->ego = ego;
2110       ego_entry->identifier = GNUNET_strdup (identifier);
2111       GNUNET_CONTAINER_DLL_insert_tail (handle->ego_head,
2112                                         handle->ego_tail,
2113                                         ego_entry);
2114     }
2115   }
2116   else
2117   {
2118     /* Delete */
2119     for (ego_entry = handle->ego_head; NULL != ego_entry;
2120          ego_entry = ego_entry->next)
2121     {
2122       if (ego_entry->ego == ego)
2123         break;
2124     }
2125     if (NULL != ego_entry)
2126       GNUNET_CONTAINER_DLL_remove (handle->ego_head,
2127                                    handle->ego_tail,
2128                                    ego_entry);
2129   }
2130 }
2131
2132
2133 static void
2134 rest_identity_process_request (struct GNUNET_REST_RequestHandle *rest_handle,
2135                                GNUNET_REST_ResultProcessor proc,
2136                                void *proc_cls)
2137 {
2138   struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
2139
2140   handle->oidc = GNUNET_new (struct OIDC_Variables);
2141   if (NULL == OIDC_cookie_jar_map)
2142     OIDC_cookie_jar_map = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
2143   if (NULL == OIDC_access_token_map)
2144     OIDC_access_token_map =
2145       GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
2146   handle->response_code = 0;
2147   handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
2148   handle->proc_cls = proc_cls;
2149   handle->proc = proc;
2150   handle->state = ID_REST_STATE_INIT;
2151   handle->rest_handle = rest_handle;
2152
2153   handle->url = GNUNET_strdup (rest_handle->url);
2154   if (handle->url[strlen (handle->url) - 1] == '/')
2155     handle->url[strlen (handle->url) - 1] = '\0';
2156   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting...\n");
2157   handle->identity_handle = GNUNET_IDENTITY_connect (cfg, &list_ego, handle);
2158   handle->gns_handle = GNUNET_GNS_connect (cfg);
2159   handle->namestore_handle = GNUNET_NAMESTORE_connect (cfg);
2160   handle->timeout_task =
2161     GNUNET_SCHEDULER_add_delayed (handle->timeout, &do_timeout, handle);
2162   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connected\n");
2163 }
2164
2165
2166 /**
2167  * Entry point for the plugin.
2168  *
2169  * @param cls Config info
2170  * @return NULL on error, otherwise the plugin context
2171  */
2172 void *
2173 libgnunet_plugin_rest_openid_connect_init (void *cls)
2174 {
2175   static struct Plugin plugin;
2176   struct GNUNET_REST_Plugin *api;
2177
2178   cfg = cls;
2179   if (NULL != plugin.cfg)
2180     return NULL; /* can only initialize once! */
2181   memset (&plugin, 0, sizeof(struct Plugin));
2182   plugin.cfg = cfg;
2183   api = GNUNET_new (struct GNUNET_REST_Plugin);
2184   api->cls = &plugin;
2185   api->name = GNUNET_REST_API_NS_OIDC;
2186   api->process_request = &rest_identity_process_request;
2187   GNUNET_asprintf (&allow_methods,
2188                    "%s, %s, %s, %s, %s",
2189                    MHD_HTTP_METHOD_GET,
2190                    MHD_HTTP_METHOD_POST,
2191                    MHD_HTTP_METHOD_PUT,
2192                    MHD_HTTP_METHOD_DELETE,
2193                    MHD_HTTP_METHOD_OPTIONS);
2194
2195   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2196               _ ("OpenID Connect REST API initialized\n"));
2197   return api;
2198 }
2199
2200
2201 /**
2202  * Exit point from the plugin.
2203  *
2204  * @param cls the plugin context (as returned by "init")
2205  * @return always NULL
2206  */
2207 void *
2208 libgnunet_plugin_rest_openid_connect_done (void *cls)
2209 {
2210   struct GNUNET_REST_Plugin *api = cls;
2211   struct Plugin *plugin = api->cls;
2212
2213   plugin->cfg = NULL;
2214
2215   struct GNUNET_CONTAINER_MultiHashMapIterator *hashmap_it;
2216   void *value = NULL;
2217   hashmap_it =
2218     GNUNET_CONTAINER_multihashmap_iterator_create (OIDC_cookie_jar_map);
2219   while (GNUNET_YES ==
2220          GNUNET_CONTAINER_multihashmap_iterator_next (hashmap_it, NULL, value))
2221     GNUNET_free_non_null (value);
2222   GNUNET_CONTAINER_multihashmap_iterator_destroy (hashmap_it);
2223   GNUNET_CONTAINER_multihashmap_destroy (OIDC_cookie_jar_map);
2224
2225   hashmap_it =
2226     GNUNET_CONTAINER_multihashmap_iterator_create (OIDC_access_token_map);
2227   while (GNUNET_YES ==
2228          GNUNET_CONTAINER_multihashmap_iterator_next (hashmap_it, NULL, value))
2229     GNUNET_free_non_null (value);
2230   GNUNET_CONTAINER_multihashmap_destroy (OIDC_access_token_map);
2231   GNUNET_CONTAINER_multihashmap_iterator_destroy (hashmap_it);
2232   GNUNET_free_non_null (allow_methods);
2233   GNUNET_free (api);
2234   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2235               "OpenID Connect REST plugin is finished\n");
2236   return NULL;
2237 }
2238
2239
2240 /* end of plugin_rest_openid_connect.c */