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