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