b6f82df84cab33b12da89090e20234aff8d23197
[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 (ego_entry,
588                                  handle->ego_head,
589                                  handle->ego_tail);
590     GNUNET_free (ego_entry->identifier);
591     GNUNET_free (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   // TODO change if more attributes are needed (see max_age)
937   code_string = OIDC_build_authz_code (&handle->priv_key,
938                                        &handle->ticket,
939                                        handle->attr_list,
940                                        handle->attests_list,
941                                        handle->oidc->nonce,
942                                        handle->oidc->code_challenge);
943   if ((NULL != handle->redirect_prefix) && (NULL != handle->redirect_suffix) &&
944       (NULL != handle->tld))
945   {
946     GNUNET_asprintf (&redirect_uri,
947                      "%s.%s/%s?%s=%s&state=%s",
948                      handle->redirect_prefix,
949                      handle->tld,
950                      handle->redirect_suffix,
951                      handle->oidc->response_type,
952                      code_string,
953                      handle->oidc->state);
954   }
955   else
956   {
957     GNUNET_asprintf (&redirect_uri,
958                      "%s?%s=%s&state=%s",
959                      handle->oidc->redirect_uri,
960                      handle->oidc->response_type,
961                      code_string,
962                      handle->oidc->state);
963   }
964   resp = GNUNET_REST_create_response ("");
965   MHD_add_response_header (resp, "Location", redirect_uri);
966   handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
967   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
968   GNUNET_free (redirect_uri);
969   GNUNET_free (ticket_str);
970   GNUNET_free (code_string);
971 }
972
973
974 static void
975 oidc_attest_collect_finished_cb (void *cls)
976 {
977   struct RequestHandle *handle = cls;
978
979   handle->attest_it = NULL;
980   handle->idp_op = GNUNET_RECLAIM_ticket_issue (handle->idp,
981                                                 &handle->priv_key,
982                                                 &handle->oidc->client_pkey,
983                                                 handle->attr_list,
984                                                 &oidc_ticket_issue_cb,
985                                                 handle);
986 }
987
988
989 /**
990  * Collects all attributes for an ego if in scope parameter
991  */
992 static void
993 oidc_attest_collect (void *cls,
994                      const struct GNUNET_CRYPTO_EcdsaPublicKey *identity,
995                      const struct GNUNET_RECLAIM_Attestation *attest)
996 {
997   struct RequestHandle *handle = cls;
998   struct GNUNET_RECLAIM_AttributeListEntry *le;
999
1000   for (le = handle->attr_list->list_head; NULL != le; le = le->next)
1001   {
1002     if (GNUNET_NO == GNUNET_RECLAIM_id_is_equal (&le->attribute->attestation,
1003                                                  &attest->id))
1004     {
1005       struct GNUNET_RECLAIM_AttestationListEntry *ale;
1006       ale = GNUNET_new (struct GNUNET_RECLAIM_AttestationListEntry);
1007       ale->attestation = GNUNET_RECLAIM_attestation_new (attest->name,
1008                                                          attest->type,
1009                                                          attest->data,
1010                                                          attest->data_size);
1011       GNUNET_CONTAINER_DLL_insert (handle->attests_list->list_head,
1012                                    handle->attests_list->list_tail,
1013                                    ale);
1014     }
1015   }
1016   GNUNET_RECLAIM_get_attestations_next (handle->attest_it);
1017 }
1018
1019
1020 static void
1021 oidc_attr_collect_finished_cb (void *cls)
1022 {
1023   struct RequestHandle *handle = cls;
1024
1025   handle->attr_it = NULL;
1026   handle->ticket_it = NULL;
1027   if (NULL == handle->attr_list->list_head)
1028   {
1029     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_SCOPE);
1030     handle->edesc = GNUNET_strdup ("The requested scope is not available.");
1031     GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1032     return;
1033   }
1034   handle->attests_list = GNUNET_new (struct GNUNET_RECLAIM_AttestationList);
1035   handle->attest_it =
1036     GNUNET_RECLAIM_get_attestations_start (handle->idp,
1037                                            &handle->priv_key,
1038                                            &oidc_iteration_error,
1039                                            handle,
1040                                            &oidc_attest_collect,
1041                                            handle,
1042                                            &oidc_attest_collect_finished_cb,
1043                                            handle);
1044
1045   handle->idp_op = GNUNET_RECLAIM_ticket_issue (handle->idp,
1046                                                 &handle->priv_key,
1047                                                 &handle->oidc->client_pkey,
1048                                                 handle->attr_list,
1049                                                 &oidc_ticket_issue_cb,
1050                                                 handle);
1051 }
1052
1053
1054 /**
1055  * Collects all attributes for an ego if in scope parameter
1056  */
1057 static void
1058 oidc_attr_collect (void *cls,
1059                    const struct GNUNET_CRYPTO_EcdsaPublicKey *identity,
1060                    const struct GNUNET_RECLAIM_Attribute *attr)
1061 {
1062   struct RequestHandle *handle = cls;
1063   struct GNUNET_RECLAIM_AttributeListEntry *le;
1064   char *scope_variables;
1065   char *scope_variable;
1066   char delimiter[] = " ";
1067
1068   scope_variables = GNUNET_strdup (handle->oidc->scope);
1069   scope_variable = strtok (scope_variables, delimiter);
1070   while (NULL != scope_variable)
1071   {
1072     if (0 == strcmp (attr->name, scope_variable))
1073       break;
1074     scope_variable = strtok (NULL, delimiter);
1075   }
1076   if (NULL == scope_variable)
1077   {
1078     GNUNET_RECLAIM_get_attributes_next (handle->attr_it);
1079     GNUNET_free (scope_variables);
1080     // We can ignore this
1081     return;
1082   }
1083   GNUNET_free (scope_variables);
1084   le = GNUNET_new (struct GNUNET_RECLAIM_AttributeListEntry);
1085   le->attribute = GNUNET_RECLAIM_attribute_new (attr->name,
1086                                                 &attr->attestation,
1087                                                 attr->type,
1088                                                 attr->data,
1089                                                 attr->data_size);
1090   le->attribute->id = attr->id;
1091   le->attribute->flag = attr->flag;
1092   le->attribute->attestation = attr->attestation;
1093   GNUNET_CONTAINER_DLL_insert (handle->attr_list->list_head,
1094                                handle->attr_list->list_tail,
1095                                le);
1096   GNUNET_RECLAIM_get_attributes_next (handle->attr_it);
1097 }
1098
1099
1100 /**
1101  * Checks time and cookie and redirects accordingly
1102  */
1103 static void
1104 code_redirect (void *cls)
1105 {
1106   struct RequestHandle *handle = cls;
1107   struct GNUNET_TIME_Absolute current_time;
1108   struct GNUNET_TIME_Absolute *relog_time;
1109   struct GNUNET_CRYPTO_EcdsaPublicKey pubkey;
1110   struct GNUNET_CRYPTO_EcdsaPublicKey ego_pkey;
1111   struct GNUNET_HashCode cache_key;
1112   char *identity_cookie;
1113
1114   GNUNET_asprintf (&identity_cookie,
1115                    "Identity=%s",
1116                    handle->oidc->login_identity);
1117   GNUNET_CRYPTO_hash (identity_cookie, strlen (identity_cookie), &cache_key);
1118   GNUNET_free (identity_cookie);
1119   // No login time for identity -> redirect to login
1120   if (GNUNET_YES ==
1121       GNUNET_CONTAINER_multihashmap_contains (OIDC_cookie_jar_map, &cache_key))
1122   {
1123     relog_time =
1124       GNUNET_CONTAINER_multihashmap_get (OIDC_cookie_jar_map, &cache_key);
1125     current_time = GNUNET_TIME_absolute_get ();
1126     // 30 min after old login -> redirect to login
1127     if (current_time.abs_value_us <= relog_time->abs_value_us)
1128     {
1129       if (GNUNET_OK !=
1130           GNUNET_CRYPTO_ecdsa_public_key_from_string (handle->oidc
1131                                                       ->login_identity,
1132                                                       strlen (
1133                                                         handle->oidc
1134                                                         ->login_identity),
1135                                                       &pubkey))
1136       {
1137         handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_COOKIE);
1138         handle->edesc =
1139           GNUNET_strdup ("The cookie of a login identity is not valid");
1140         GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1141         return;
1142       }
1143       // iterate over egos and compare their public key
1144       for (handle->ego_entry = handle->ego_head; NULL != handle->ego_entry;
1145            handle->ego_entry = handle->ego_entry->next)
1146       {
1147         GNUNET_IDENTITY_ego_get_public_key (handle->ego_entry->ego, &ego_pkey);
1148         if (0 == GNUNET_memcmp (&ego_pkey, &pubkey))
1149         {
1150           handle->priv_key =
1151             *GNUNET_IDENTITY_ego_get_private_key (handle->ego_entry->ego);
1152           handle->idp = GNUNET_RECLAIM_connect (cfg);
1153           handle->attr_list =
1154             GNUNET_new (struct GNUNET_RECLAIM_AttributeList);
1155           handle->attr_it =
1156             GNUNET_RECLAIM_get_attributes_start (handle->idp,
1157                                                  &handle->priv_key,
1158                                                  &oidc_iteration_error,
1159                                                  handle,
1160                                                  &oidc_attr_collect,
1161                                                  handle,
1162                                                  &oidc_attr_collect_finished_cb,
1163                                                  handle);
1164           return;
1165         }
1166       }
1167       GNUNET_SCHEDULER_add_now (&login_redirect, handle);
1168       return;
1169     }
1170   }
1171 }
1172
1173
1174 static void
1175 build_redirect (void *cls)
1176 {
1177   struct RequestHandle *handle = cls;
1178   struct MHD_Response *resp;
1179   char *redirect_uri;
1180
1181   if (GNUNET_YES == handle->oidc->user_cancelled)
1182   {
1183     if ((NULL != handle->redirect_prefix) &&
1184         (NULL != handle->redirect_suffix) && (NULL != handle->tld))
1185     {
1186       GNUNET_asprintf (&redirect_uri,
1187                        "%s.%s/%s?error=%s&error_description=%s&state=%s",
1188                        handle->redirect_prefix,
1189                        handle->tld,
1190                        handle->redirect_suffix,
1191                        "access_denied",
1192                        "User denied access",
1193                        handle->oidc->state);
1194     }
1195     else
1196     {
1197       GNUNET_asprintf (&redirect_uri,
1198                        "%s?error=%s&error_description=%s&state=%s",
1199                        handle->oidc->redirect_uri,
1200                        "access_denied",
1201                        "User denied access",
1202                        handle->oidc->state);
1203     }
1204     resp = GNUNET_REST_create_response ("");
1205     MHD_add_response_header (resp, "Location", redirect_uri);
1206     handle->proc (handle->proc_cls, resp, MHD_HTTP_FOUND);
1207     GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
1208     GNUNET_free (redirect_uri);
1209     return;
1210   }
1211   GNUNET_SCHEDULER_add_now (&code_redirect, handle);
1212 }
1213
1214
1215 static void
1216 lookup_redirect_uri_result (void *cls,
1217                             uint32_t rd_count,
1218                             const struct GNUNET_GNSRECORD_Data *rd)
1219 {
1220   struct RequestHandle *handle = cls;
1221   char *tmp;
1222   char *tmp_key_str;
1223   char *pos;
1224   struct GNUNET_CRYPTO_EcdsaPublicKey redirect_zone;
1225
1226   handle->gns_op = NULL;
1227   if (0 == rd_count)
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     return;
1234   }
1235   for (int i = 0; i < rd_count; i++)
1236   {
1237     if (GNUNET_GNSRECORD_TYPE_RECLAIM_OIDC_REDIRECT != rd[i].record_type)
1238       continue;
1239     if (0 != strncmp (rd[i].data, handle->oidc->redirect_uri, rd[i].data_size))
1240       continue;
1241     tmp = GNUNET_strndup (rd[i].data, rd[i].data_size);
1242     if (NULL == strstr (tmp, handle->oidc->client_id))
1243     {
1244       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1245                   "Redirect uri %s does not contain client_id %s\n",
1246                   tmp,
1247                   handle->oidc->client_id);
1248     }
1249     else
1250     {
1251       pos = strrchr (tmp, (unsigned char) '.');
1252       if (NULL == pos)
1253       {
1254         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1255                     "Redirect uri %s contains client_id but is malformed\n",
1256                     tmp);
1257         GNUNET_free (tmp);
1258         continue;
1259       }
1260       *pos = '\0';
1261       handle->redirect_prefix = GNUNET_strdup (tmp);
1262       tmp_key_str = pos + 1;
1263       pos = strchr (tmp_key_str, (unsigned char) '/');
1264       if (NULL == pos)
1265       {
1266         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1267                     "Redirect uri %s contains client_id but is malformed\n",
1268                     tmp);
1269         GNUNET_free (tmp);
1270         continue;
1271       }
1272       *pos = '\0';
1273       handle->redirect_suffix = GNUNET_strdup (pos + 1);
1274
1275       GNUNET_STRINGS_string_to_data (tmp_key_str,
1276                                      strlen (tmp_key_str),
1277                                      &redirect_zone,
1278                                      sizeof(redirect_zone));
1279     }
1280     GNUNET_SCHEDULER_add_now (&build_redirect, handle);
1281     GNUNET_free (tmp);
1282     return;
1283   }
1284   handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
1285   handle->edesc =
1286     GNUNET_strdup ("Server cannot generate ticket, redirect uri not found.");
1287   GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1288 }
1289
1290
1291 /**
1292  * Initiate redirect back to client.
1293  */
1294 static void
1295 client_redirect (void *cls)
1296 {
1297   struct RequestHandle *handle = cls;
1298
1299   /* Lookup client redirect uri to verify request */
1300   handle->gns_op =
1301     GNUNET_GNS_lookup (handle->gns_handle,
1302                        GNUNET_GNS_EMPTY_LABEL_AT,
1303                        &handle->oidc->client_pkey,
1304                        GNUNET_GNSRECORD_TYPE_RECLAIM_OIDC_REDIRECT,
1305                        GNUNET_GNS_LO_DEFAULT,
1306                        &lookup_redirect_uri_result,
1307                        handle);
1308 }
1309
1310
1311 static char *
1312 get_url_parameter_copy (const struct RequestHandle *handle, const char *key)
1313 {
1314   struct GNUNET_HashCode hc;
1315   char *value;
1316
1317   GNUNET_CRYPTO_hash (key, strlen (key), &hc);
1318   if (GNUNET_YES != GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
1319                                                             ->url_param_map,
1320                                                             &hc))
1321     return NULL;
1322   value =
1323     GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map, &hc);
1324   if (NULL == value)
1325     return NULL;
1326   return GNUNET_strdup (value);
1327 }
1328
1329
1330 /**
1331  * Iteration over all results finished, build final
1332  * response.
1333  *
1334  * @param cls the `struct RequestHandle`
1335  */
1336 static void
1337 build_authz_response (void *cls)
1338 {
1339   struct RequestHandle *handle = cls;
1340   struct GNUNET_HashCode cache_key;
1341
1342   char *expected_scope;
1343   char delimiter[] = " ";
1344   int number_of_ignored_parameter, iterator;
1345
1346
1347   // REQUIRED value: redirect_uri
1348   handle->oidc->redirect_uri =
1349     get_url_parameter_copy (handle, OIDC_REDIRECT_URI_KEY);
1350   if (NULL == handle->oidc->redirect_uri)
1351   {
1352     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1353     handle->edesc = GNUNET_strdup ("missing parameter redirect_uri");
1354     GNUNET_SCHEDULER_add_now (&do_error, handle);
1355     return;
1356   }
1357
1358   // REQUIRED value: response_type
1359   handle->oidc->response_type =
1360     get_url_parameter_copy (handle, OIDC_RESPONSE_TYPE_KEY);
1361   if (NULL == handle->oidc->response_type)
1362   {
1363     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1364     handle->edesc = GNUNET_strdup ("missing parameter response_type");
1365     GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1366     return;
1367   }
1368
1369   // REQUIRED value: scope
1370   handle->oidc->scope = get_url_parameter_copy (handle, OIDC_SCOPE_KEY);
1371   if (NULL == handle->oidc->scope)
1372   {
1373     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_SCOPE);
1374     handle->edesc = GNUNET_strdup ("missing parameter scope");
1375     GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1376     return;
1377   }
1378
1379   // OPTIONAL value: nonce
1380   handle->oidc->nonce = get_url_parameter_copy (handle, OIDC_NONCE_KEY);
1381
1382   // OPTIONAL value: claims
1383   handle->oidc->claims = get_url_parameter_copy (handle, OIDC_CLAIMS_KEY);
1384
1385   // TODO check other values if needed
1386   number_of_ignored_parameter =
1387     sizeof(OIDC_ignored_parameter_array) / sizeof(char *);
1388   for (iterator = 0; iterator < number_of_ignored_parameter; iterator++)
1389   {
1390     GNUNET_CRYPTO_hash (OIDC_ignored_parameter_array[iterator],
1391                         strlen (OIDC_ignored_parameter_array[iterator]),
1392                         &cache_key);
1393     if (GNUNET_YES ==
1394         GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
1395                                                 ->url_param_map,
1396                                                 &cache_key))
1397     {
1398       handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_ACCESS_DENIED);
1399       GNUNET_asprintf (&handle->edesc,
1400                        "Server will not handle parameter: %s",
1401                        OIDC_ignored_parameter_array[iterator]);
1402       GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1403       return;
1404     }
1405   }
1406
1407   // We only support authorization code flows.
1408   if (0 != strcmp (handle->oidc->response_type,
1409                    OIDC_EXPECTED_AUTHORIZATION_RESPONSE_TYPE))
1410   {
1411     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_UNSUPPORTED_RESPONSE_TYPE);
1412     handle->edesc = GNUNET_strdup ("The authorization server does not support "
1413                                    "obtaining this authorization code.");
1414     GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1415     return;
1416   }
1417
1418   // Checks if scope contains 'openid'
1419   expected_scope = GNUNET_strdup (handle->oidc->scope);
1420   char *test;
1421   test = strtok (expected_scope, delimiter);
1422   while (NULL != test)
1423   {
1424     if (0 == strcmp (OIDC_EXPECTED_AUTHORIZATION_SCOPE, expected_scope))
1425       break;
1426     test = strtok (NULL, delimiter);
1427   }
1428   if (NULL == test)
1429   {
1430     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_SCOPE);
1431     handle->edesc =
1432       GNUNET_strdup ("The requested scope is invalid, unknown, or malformed.");
1433     GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1434     GNUNET_free (expected_scope);
1435     return;
1436   }
1437
1438   GNUNET_free (expected_scope);
1439   if ((NULL == handle->oidc->login_identity) &&
1440       (GNUNET_NO == handle->oidc->user_cancelled))
1441     GNUNET_SCHEDULER_add_now (&login_redirect, handle);
1442   else
1443     GNUNET_SCHEDULER_add_now (&client_redirect, handle);
1444 }
1445
1446
1447 /**
1448  * Iterate over tlds in config
1449  */
1450 static void
1451 tld_iter (void *cls, const char *section, const char *option, const char *value)
1452 {
1453   struct RequestHandle *handle = cls;
1454   struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
1455
1456   if (GNUNET_OK !=
1457       GNUNET_CRYPTO_ecdsa_public_key_from_string (value, strlen (value), &pkey))
1458   {
1459     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Skipping non key %s\n", value);
1460     return;
1461   }
1462   if (0 == GNUNET_memcmp (&pkey, &handle->oidc->client_pkey))
1463     handle->tld = GNUNET_strdup (option + 1);
1464 }
1465
1466
1467 /**
1468  * Responds to authorization GET and url-encoded POST request
1469  *
1470  * @param con_handle the connection handle
1471  * @param url the url
1472  * @param cls the RequestHandle
1473  */
1474 static void
1475 authorize_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
1476                     const char *url,
1477                     void *cls)
1478 {
1479   struct RequestHandle *handle = cls;
1480   struct EgoEntry *tmp_ego;
1481   const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key;
1482   struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
1483
1484   cookie_identity_interpretation (handle);
1485
1486   // RECOMMENDED value: state - REQUIRED for answers
1487   handle->oidc->state = get_url_parameter_copy (handle, OIDC_STATE_KEY);
1488
1489   // REQUIRED value: client_id
1490   handle->oidc->client_id = get_url_parameter_copy (handle, OIDC_CLIENT_ID_KEY);
1491   if (NULL == handle->oidc->client_id)
1492   {
1493     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1494     handle->edesc = GNUNET_strdup ("missing parameter client_id");
1495     handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1496     GNUNET_SCHEDULER_add_now (&do_error, handle);
1497     return;
1498   }
1499
1500   // OPTIONAL value: code_challenge
1501   handle->oidc->code_challenge = get_url_parameter_copy (handle,
1502                                                          OIDC_CODE_CHALLENGE_KEY);
1503   if (NULL == handle->oidc->code_challenge)
1504   {
1505     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1506                 "OAuth authorization request does not contain PKCE parameters!\n");
1507   }
1508
1509   if (GNUNET_OK !=
1510       GNUNET_CRYPTO_ecdsa_public_key_from_string (handle->oidc->client_id,
1511                                                   strlen (
1512                                                     handle->oidc->client_id),
1513                                                   &handle->oidc->client_pkey))
1514   {
1515     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_UNAUTHORIZED_CLIENT);
1516     handle->edesc = GNUNET_strdup ("The client is not authorized to request an "
1517                                    "authorization code using this method.");
1518     handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1519     GNUNET_SCHEDULER_add_now (&do_error, handle);
1520     return;
1521   }
1522
1523   // If we know this identity, translated the corresponding TLD
1524   // TODO: We might want to have a reverse lookup functionality for TLDs?
1525   for (tmp_ego = handle->ego_head; NULL != tmp_ego; tmp_ego = tmp_ego->next)
1526   {
1527     priv_key = GNUNET_IDENTITY_ego_get_private_key (tmp_ego->ego);
1528     GNUNET_CRYPTO_ecdsa_key_get_public (priv_key, &pkey);
1529     if (0 == GNUNET_memcmp (&pkey, &handle->oidc->client_pkey))
1530     {
1531       handle->tld = GNUNET_strdup (tmp_ego->identifier);
1532       handle->ego_entry = handle->ego_tail;
1533     }
1534   }
1535   handle->oidc->scope = get_url_parameter_copy (handle, OIDC_SCOPE_KEY);
1536   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Scope: %s\n",GNUNET_strdup (
1537                 handle->oidc->scope));
1538   if (NULL == handle->tld)
1539     GNUNET_CONFIGURATION_iterate_section_values (cfg, "gns", tld_iter, handle);
1540   if (NULL == handle->tld)
1541     handle->tld = GNUNET_strdup (handle->oidc->client_id);
1542   GNUNET_SCHEDULER_add_now (&build_authz_response, handle);
1543 }
1544
1545
1546 /**
1547  * Combines an identity with a login time and responds OK to login request
1548  *
1549  * @param con_handle the connection handle
1550  * @param url the url
1551  * @param cls the RequestHandle
1552  */
1553 static void
1554 login_cont (struct GNUNET_REST_RequestHandle *con_handle,
1555             const char *url,
1556             void *cls)
1557 {
1558   struct MHD_Response *resp = GNUNET_REST_create_response ("");
1559   struct RequestHandle *handle = cls;
1560   struct GNUNET_HashCode cache_key;
1561   struct GNUNET_TIME_Absolute *current_time;
1562   struct GNUNET_TIME_Absolute *last_time;
1563   char *cookie;
1564   char *header_val;
1565   json_t *root;
1566   json_error_t error;
1567   json_t *identity;
1568   char term_data[handle->rest_handle->data_size + 1];
1569
1570   term_data[handle->rest_handle->data_size] = '\0';
1571   GNUNET_memcpy (term_data,
1572                  handle->rest_handle->data,
1573                  handle->rest_handle->data_size);
1574   root = json_loads (term_data, JSON_DECODE_ANY, &error);
1575   identity = json_object_get (root, "identity");
1576   if (! json_is_string (identity))
1577   {
1578     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1579                 "Error parsing json string from %s\n",
1580                 term_data);
1581     handle->proc (handle->proc_cls, resp, MHD_HTTP_BAD_REQUEST);
1582     json_decref (root);
1583     GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
1584     return;
1585   }
1586   GNUNET_asprintf (&cookie, "Identity=%s", json_string_value (identity));
1587   GNUNET_asprintf (&header_val,
1588                    "%s;Max-Age=%d",
1589                    cookie,
1590                    OIDC_COOKIE_EXPIRATION);
1591   MHD_add_response_header (resp, "Set-Cookie", header_val);
1592   MHD_add_response_header (resp, "Access-Control-Allow-Methods", "POST");
1593   GNUNET_CRYPTO_hash (cookie, strlen (cookie), &cache_key);
1594
1595   if (0 != strcmp (json_string_value (identity), "Denied"))
1596   {
1597     current_time = GNUNET_new (struct GNUNET_TIME_Absolute);
1598     *current_time = GNUNET_TIME_relative_to_absolute (
1599       GNUNET_TIME_relative_multiply (GNUNET_TIME_relative_get_second_ (),
1600                                      OIDC_COOKIE_EXPIRATION));
1601     last_time =
1602       GNUNET_CONTAINER_multihashmap_get (OIDC_cookie_jar_map, &cache_key);
1603     GNUNET_free_non_null (last_time);
1604     GNUNET_CONTAINER_multihashmap_put (OIDC_cookie_jar_map,
1605                                        &cache_key,
1606                                        current_time,
1607                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
1608   }
1609   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
1610   GNUNET_free (cookie);
1611   GNUNET_free (header_val);
1612   json_decref (root);
1613   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
1614 }
1615
1616
1617 static int
1618 check_authorization (struct RequestHandle *handle,
1619                      struct GNUNET_CRYPTO_EcdsaPublicKey *cid)
1620 {
1621   struct GNUNET_HashCode cache_key;
1622   char *authorization;
1623   char *credentials;
1624   char *basic_authorization;
1625   char *client_id;
1626   char *pass;
1627   char *expected_pass;
1628
1629   GNUNET_CRYPTO_hash (OIDC_AUTHORIZATION_HEADER_KEY,
1630                       strlen (OIDC_AUTHORIZATION_HEADER_KEY),
1631                       &cache_key);
1632   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
1633                                                            ->header_param_map,
1634                                                            &cache_key))
1635   {
1636     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1637     handle->edesc = GNUNET_strdup ("missing authorization");
1638     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1639     return GNUNET_SYSERR;
1640   }
1641   authorization =
1642     GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->header_param_map,
1643                                        &cache_key);
1644
1645   // split header in "Basic" and [content]
1646   credentials = strtok (authorization, " ");
1647   if ((NULL == credentials) || (0 != strcmp ("Basic", credentials)))
1648   {
1649     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1650     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1651     return GNUNET_SYSERR;
1652   }
1653   credentials = strtok (NULL, " ");
1654   if (NULL == credentials)
1655   {
1656     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1657     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1658     return GNUNET_SYSERR;
1659   }
1660   GNUNET_STRINGS_base64_decode (credentials,
1661                                 strlen (credentials),
1662                                 (void **) &basic_authorization);
1663
1664   if (NULL == basic_authorization)
1665   {
1666     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1667     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1668     return GNUNET_SYSERR;
1669   }
1670   client_id = strtok (basic_authorization, ":");
1671   if (NULL == client_id)
1672   {
1673     GNUNET_free_non_null (basic_authorization);
1674     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1675     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1676     return GNUNET_SYSERR;
1677   }
1678   pass = strtok (NULL, ":");
1679   if (NULL == pass)
1680   {
1681     GNUNET_free_non_null (basic_authorization);
1682     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1683     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1684     return GNUNET_SYSERR;
1685   }
1686
1687   // check client password
1688   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg,
1689                                                           "reclaim-rest-plugin",
1690                                                           "OIDC_CLIENT_SECRET",
1691                                                           &expected_pass))
1692   {
1693     if (0 != strcmp (expected_pass, pass))
1694     {
1695       GNUNET_free_non_null (basic_authorization);
1696       GNUNET_free (expected_pass);
1697       handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1698       handle->response_code = MHD_HTTP_UNAUTHORIZED;
1699       return GNUNET_SYSERR;
1700     }
1701     GNUNET_free (expected_pass);
1702   }
1703   else
1704   {
1705     GNUNET_free_non_null (basic_authorization);
1706     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
1707     handle->edesc = GNUNET_strdup ("gnunet configuration failed");
1708     handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1709     return GNUNET_SYSERR;
1710   }
1711
1712   // check client_id
1713   for (handle->ego_entry = handle->ego_head; NULL != handle->ego_entry;
1714        handle->ego_entry = handle->ego_entry->next)
1715   {
1716     if (0 == strcmp (handle->ego_entry->keystring, client_id))
1717       break;
1718   }
1719   if (NULL == handle->ego_entry)
1720   {
1721     GNUNET_free_non_null (basic_authorization);
1722     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_CLIENT);
1723     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1724     return GNUNET_SYSERR;
1725   }
1726   GNUNET_STRINGS_string_to_data (client_id,
1727                                  strlen (client_id),
1728                                  cid,
1729                                  sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey));
1730
1731   GNUNET_free (basic_authorization);
1732   return GNUNET_OK;
1733 }
1734
1735
1736 const struct EgoEntry *
1737 find_ego (struct RequestHandle *handle,
1738           struct GNUNET_CRYPTO_EcdsaPublicKey *test_key)
1739 {
1740   struct EgoEntry *ego_entry;
1741   struct GNUNET_CRYPTO_EcdsaPublicKey pub_key;
1742
1743   for (ego_entry = handle->ego_head; NULL != ego_entry;
1744        ego_entry = ego_entry->next)
1745   {
1746     GNUNET_IDENTITY_ego_get_public_key (ego_entry->ego, &pub_key);
1747     if (0 == GNUNET_memcmp (&pub_key, test_key))
1748       return ego_entry;
1749   }
1750   return NULL;
1751 }
1752
1753
1754 static void
1755 persist_access_token (const struct RequestHandle *handle,
1756                       const char *access_token,
1757                       const struct GNUNET_RECLAIM_Ticket *ticket)
1758 {
1759   struct GNUNET_HashCode hc;
1760   struct GNUNET_RECLAIM_Ticket *ticketbuf;
1761
1762   GNUNET_CRYPTO_hash (access_token, strlen (access_token), &hc);
1763   ticketbuf = GNUNET_new (struct GNUNET_RECLAIM_Ticket);
1764   *ticketbuf = *ticket;
1765   GNUNET_assert (GNUNET_SYSERR !=
1766                  GNUNET_CONTAINER_multihashmap_put (
1767                    OIDC_access_token_map,
1768                    &hc,
1769                    ticketbuf,
1770                    GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1771 }
1772
1773
1774 /**
1775  * Responds to token url-encoded POST request
1776  *
1777  * @param con_handle the connection handle
1778  * @param url the url
1779  * @param cls the RequestHandle
1780  */
1781 static void
1782 token_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
1783                 const char *url,
1784                 void *cls)
1785 {
1786   struct RequestHandle *handle = cls;
1787   const struct EgoEntry *ego_entry;
1788   struct GNUNET_TIME_Relative expiration_time;
1789   struct GNUNET_RECLAIM_AttributeList *cl = NULL;
1790   struct GNUNET_RECLAIM_AttestationList *al = NULL;
1791   struct GNUNET_RECLAIM_Ticket ticket;
1792   struct GNUNET_CRYPTO_EcdsaPublicKey cid;
1793   const struct GNUNET_CRYPTO_EcdsaPrivateKey *privkey;
1794   struct GNUNET_HashCode cache_key;
1795   struct MHD_Response *resp;
1796   char *grant_type;
1797   char *code;
1798   char *json_response;
1799   char *id_token;
1800   char *access_token;
1801   char *jwt_secret;
1802   char *nonce;
1803   char *code_verifier;
1804
1805   /*
1806    * Check Authorization
1807    */
1808   if (GNUNET_SYSERR == check_authorization (handle, &cid))
1809   {
1810     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1811                 "OIDC authorization for token endpoint failed\n");
1812     GNUNET_SCHEDULER_add_now (&do_error, handle);
1813     return;
1814   }
1815
1816   /*
1817    * Check parameter
1818    */
1819
1820   // TODO Do not allow multiple equal parameter names
1821   // REQUIRED grant_type
1822   GNUNET_CRYPTO_hash (OIDC_GRANT_TYPE_KEY,
1823                       strlen (OIDC_GRANT_TYPE_KEY),
1824                       &cache_key);
1825   grant_type = get_url_parameter_copy (handle, OIDC_GRANT_TYPE_KEY);
1826   if (NULL == grant_type)
1827   {
1828     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1829     handle->edesc = GNUNET_strdup ("missing parameter grant_type");
1830     handle->response_code = MHD_HTTP_BAD_REQUEST;
1831     GNUNET_SCHEDULER_add_now (&do_error, handle);
1832     return;
1833   }
1834
1835   // Check parameter grant_type == "authorization_code"
1836   if (0 != strcmp (OIDC_GRANT_TYPE_VALUE, grant_type))
1837   {
1838     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_UNSUPPORTED_GRANT_TYPE);
1839     handle->response_code = MHD_HTTP_BAD_REQUEST;
1840     GNUNET_free (grant_type);
1841     GNUNET_SCHEDULER_add_now (&do_error, handle);
1842     return;
1843   }
1844   GNUNET_free (grant_type);
1845   // REQUIRED code
1846   code = get_url_parameter_copy (handle, OIDC_CODE_KEY);
1847   if (NULL == code)
1848   {
1849     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1850     handle->edesc = GNUNET_strdup ("missing parameter code");
1851     handle->response_code = MHD_HTTP_BAD_REQUEST;
1852     GNUNET_SCHEDULER_add_now (&do_error, handle);
1853     return;
1854   }
1855   ego_entry = find_ego (handle, &cid);
1856   if (NULL == ego_entry)
1857   {
1858     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1859     handle->edesc = GNUNET_strdup ("Unknown client");
1860     handle->response_code = MHD_HTTP_BAD_REQUEST;
1861     GNUNET_free (code);
1862     GNUNET_SCHEDULER_add_now (&do_error, handle);
1863     return;
1864   }
1865   privkey = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
1866
1867   // REQUIRED code verifier
1868   code_verifier = get_url_parameter_copy (handle, OIDC_CODE_VERIFIER_KEY);
1869   if (NULL == code_verifier)
1870   {
1871     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1872                 "OAuth authorization request does not contain PKCE parameters!\n");
1873
1874   }
1875
1876   // decode code
1877   if (GNUNET_OK != OIDC_parse_authz_code (privkey, code, code_verifier, &ticket,
1878                                           &cl, &al, &nonce))
1879   {
1880     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1881     handle->edesc = GNUNET_strdup ("invalid code");
1882     handle->response_code = MHD_HTTP_BAD_REQUEST;
1883     GNUNET_free (code);
1884     GNUNET_SCHEDULER_add_now (&do_error, handle);
1885     return;
1886   }
1887   GNUNET_free (code);
1888
1889   // create jwt
1890   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (cfg,
1891                                                         "reclaim-rest-plugin",
1892                                                         "expiration_time",
1893                                                         &expiration_time))
1894   {
1895     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_SERVER_ERROR);
1896     handle->edesc = GNUNET_strdup ("gnunet configuration failed");
1897     handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1898     GNUNET_SCHEDULER_add_now (&do_error, handle);
1899     return;
1900   }
1901
1902
1903   // TODO OPTIONAL acr,amr,azp
1904   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg,
1905                                                           "reclaim-rest-plugin",
1906                                                           "jwt_secret",
1907                                                           &jwt_secret))
1908   {
1909     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_REQUEST);
1910     handle->edesc = GNUNET_strdup ("No signing secret configured!");
1911     handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1912     GNUNET_SCHEDULER_add_now (&do_error, handle);
1913     return;
1914   }
1915   id_token = OIDC_id_token_new (&ticket.audience,
1916                                 &ticket.identity,
1917                                 cl,
1918                                 al,
1919                                 &expiration_time,
1920                                 (NULL != nonce) ? nonce : NULL,
1921                                 jwt_secret);
1922   access_token = OIDC_access_token_new ();
1923   OIDC_build_token_response (access_token,
1924                              id_token,
1925                              &expiration_time,
1926                              &json_response);
1927
1928   persist_access_token (handle, access_token, &ticket);
1929   resp = GNUNET_REST_create_response (json_response);
1930   MHD_add_response_header (resp, "Cache-Control", "no-store");
1931   MHD_add_response_header (resp, "Pragma", "no-cache");
1932   MHD_add_response_header (resp, "Content-Type", "application/json");
1933   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
1934   GNUNET_RECLAIM_attribute_list_destroy (cl);
1935   GNUNET_RECLAIM_attestation_list_destroy (al);
1936   GNUNET_free (access_token);
1937   GNUNET_free (json_response);
1938   GNUNET_free (id_token);
1939   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
1940 }
1941
1942
1943 /**
1944  * Collects claims and stores them in handle
1945  */
1946 static void
1947 consume_ticket (void *cls,
1948                 const struct GNUNET_CRYPTO_EcdsaPublicKey *identity,
1949                 const struct GNUNET_RECLAIM_Attribute *attr,
1950                 const struct GNUNET_RECLAIM_Attestation *attest)
1951 {
1952   struct RequestHandle *handle = cls;
1953   handle->idp_op = NULL;
1954
1955   if (NULL == identity)
1956   {
1957     GNUNET_SCHEDULER_add_now (&return_userinfo_response, handle);
1958     return;
1959   }
1960   if (GNUNET_YES == GNUNET_RECLAIM_id_is_zero (&attr->attestation))
1961   {
1962     char *tmp_value;
1963     json_t *value;
1964     tmp_value = GNUNET_RECLAIM_attribute_value_to_string (attr->type,
1965                                                           attr->data,
1966                                                           attr->data_size);
1967     value = json_string (tmp_value);
1968     json_object_set_new (handle->oidc->response, attr->name, value);
1969     GNUNET_free (tmp_value);
1970     return;
1971   }
1972   json_t *claim_sources;
1973   json_t *claim_sources_jwt;
1974   json_t *claim_names;
1975   char *attest_val_str;
1976   claim_sources = json_object_get (handle->oidc->response,"_claim_sources");
1977   claim_names = json_object_get (handle->oidc->response,"_claim_names");
1978   attest_val_str =
1979     GNUNET_RECLAIM_attestation_value_to_string (attest->type,
1980                                                 attest->data,
1981                                                 attest->data_size);
1982   if ((NULL == claim_sources) && (NULL == claim_names) )
1983   {
1984     claim_sources = json_object ();
1985     claim_names = json_object ();
1986   }
1987   char *source_name;
1988   int i = 0;
1989   GNUNET_asprintf (&source_name, "src%d", i);
1990   while (NULL != (claim_sources_jwt = json_object_get (claim_sources,
1991                                                        source_name)))
1992   {
1993     if (0 == strcmp (json_string_value (json_object_get (claim_sources_jwt,
1994                                                          "JWT")),
1995                      attest_val_str))
1996     {
1997       // Adapt only the claim names
1998       json_object_set_new (claim_names, attr->data,
1999                            json_string (source_name));
2000       json_object_set (handle->oidc->response,
2001                        "_claim_names", claim_names);
2002       break;
2003     }
2004     i++;
2005     GNUNET_free (source_name);
2006     GNUNET_asprintf (&source_name, "src%d", i);
2007   }
2008
2009   // Create new one
2010   if (NULL == claim_sources_jwt)
2011   {
2012     claim_sources_jwt = json_object ();
2013     // Set the JWT for names
2014     json_object_set_new (claim_names, attr->data,
2015                          json_string (source_name));
2016     // Set the JWT for the inner source
2017     json_object_set_new (claim_sources_jwt, "JWT",
2018                          json_string (attest_val_str));
2019     // Set the JWT for the source
2020     json_object_set_new (claim_sources, source_name, claim_sources_jwt);
2021     // Set as claims
2022     json_object_set (handle->oidc->response, "_claim_names", claim_names);
2023     json_object_set (handle->oidc->response, "_claim_sources",claim_sources);
2024   }
2025
2026   json_decref (claim_sources);
2027   json_decref (claim_names);
2028   json_decref (claim_sources_jwt);
2029   GNUNET_free (attest_val_str);
2030 }
2031
2032
2033 /**
2034  * Responds to userinfo GET and url-encoded POST request
2035  *
2036  * @param con_handle the connection handle
2037  * @param url the url
2038  * @param cls the RequestHandle
2039  */
2040 static void
2041 userinfo_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
2042                    const char *url,
2043                    void *cls)
2044 {
2045   // TODO expiration time
2046   struct RequestHandle *handle = cls;
2047   char delimiter[] = " ";
2048   struct GNUNET_HashCode cache_key;
2049   char *authorization;
2050   char *authorization_type;
2051   char *authorization_access_token;
2052   struct GNUNET_RECLAIM_Ticket *ticket;
2053   const struct EgoEntry *ego_entry;
2054   const struct GNUNET_CRYPTO_EcdsaPrivateKey *privkey;
2055
2056   GNUNET_CRYPTO_hash (OIDC_AUTHORIZATION_HEADER_KEY,
2057                       strlen (OIDC_AUTHORIZATION_HEADER_KEY),
2058                       &cache_key);
2059   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle
2060                                                            ->header_param_map,
2061                                                            &cache_key))
2062   {
2063     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
2064     handle->edesc = GNUNET_strdup ("No Access Token");
2065     handle->response_code = MHD_HTTP_UNAUTHORIZED;
2066     GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2067     return;
2068   }
2069   authorization =
2070     GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->header_param_map,
2071                                        &cache_key);
2072
2073   // split header in "Bearer" and access_token
2074   authorization = GNUNET_strdup (authorization);
2075   authorization_type = strtok (authorization, delimiter);
2076   if ((NULL == authorization_type) ||
2077       (0 != strcmp ("Bearer", authorization_type)))
2078   {
2079     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
2080     handle->edesc = GNUNET_strdup ("No Access Token");
2081     handle->response_code = MHD_HTTP_UNAUTHORIZED;
2082     GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2083     GNUNET_free (authorization);
2084     return;
2085   }
2086   authorization_access_token = strtok (NULL, delimiter);
2087   if (NULL == authorization_access_token)
2088   {
2089     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
2090     handle->edesc = GNUNET_strdup ("Access token missing");
2091     handle->response_code = MHD_HTTP_UNAUTHORIZED;
2092     GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2093     GNUNET_free (authorization);
2094     return;
2095   }
2096
2097   GNUNET_CRYPTO_hash (authorization_access_token,
2098                       strlen (authorization_access_token),
2099                       &cache_key);
2100   if (GNUNET_NO ==
2101       GNUNET_CONTAINER_multihashmap_contains (OIDC_access_token_map,
2102                                               &cache_key))
2103   {
2104     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
2105     handle->edesc = GNUNET_strdup ("The access token expired");
2106     handle->response_code = MHD_HTTP_UNAUTHORIZED;
2107     GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2108     GNUNET_free (authorization);
2109     return;
2110   }
2111   ticket =
2112     GNUNET_CONTAINER_multihashmap_get (OIDC_access_token_map, &cache_key);
2113   GNUNET_assert (NULL != ticket);
2114   ego_entry = find_ego (handle, &ticket->audience);
2115   if (NULL == ego_entry)
2116   {
2117     handle->emsg = GNUNET_strdup (OIDC_ERROR_KEY_INVALID_TOKEN);
2118     handle->edesc = GNUNET_strdup ("The access token expired");
2119     handle->response_code = MHD_HTTP_UNAUTHORIZED;
2120     GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
2121     GNUNET_free (authorization);
2122     return;
2123   }
2124
2125   handle->idp = GNUNET_RECLAIM_connect (cfg);
2126   handle->oidc->response = json_object ();
2127   json_object_set_new (handle->oidc->response,
2128                        "sub",
2129                        json_string (ego_entry->keystring));
2130   privkey = GNUNET_IDENTITY_ego_get_private_key (ego_entry->ego);
2131   handle->idp_op = GNUNET_RECLAIM_ticket_consume (handle->idp,
2132                                                   privkey,
2133                                                   ticket,
2134                                                   consume_ticket,
2135                                                   handle);
2136   GNUNET_free (authorization);
2137 }
2138
2139
2140 /**
2141  * Handle rest request
2142  *
2143  * @param handle the request handle
2144  */
2145 static void
2146 init_cont (struct RequestHandle *handle)
2147 {
2148   struct GNUNET_REST_RequestHandlerError err;
2149   static const struct GNUNET_REST_RequestHandler handlers[] =
2150   { { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_AUTHORIZE, &authorize_endpoint },
2151     { MHD_HTTP_METHOD_POST,
2152       GNUNET_REST_API_NS_AUTHORIZE,
2153       &authorize_endpoint },   // url-encoded
2154     { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_LOGIN, &login_cont },
2155     { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_TOKEN, &token_endpoint },
2156     { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_USERINFO, &userinfo_endpoint },
2157     { MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_USERINFO, &userinfo_endpoint },
2158     { MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_OIDC, &options_cont },
2159     GNUNET_REST_HANDLER_END };
2160
2161   if (GNUNET_NO ==
2162       GNUNET_REST_handle_request (handle->rest_handle, handlers, &err, handle))
2163   {
2164     handle->response_code = err.error_code;
2165     GNUNET_SCHEDULER_add_now (&do_error, handle);
2166   }
2167 }
2168
2169
2170 /**
2171  * If listing is enabled, prints information about the egos.
2172  *
2173  * This function is initially called for all egos and then again
2174  * whenever a ego's identifier changes or if it is deleted.  At the
2175  * end of the initial pass over all egos, the function is once called
2176  * with 'NULL' for 'ego'. That does NOT mean that the callback won't
2177  * be invoked in the future or that there was an error.
2178  *
2179  * When used with 'GNUNET_IDENTITY_create' or 'GNUNET_IDENTITY_get',
2180  * this function is only called ONCE, and 'NULL' being passed in
2181  * 'ego' does indicate an error (i.e. name is taken or no default
2182  * value is known).  If 'ego' is non-NULL and if '*ctx'
2183  * is set in those callbacks, the value WILL be passed to a subsequent
2184  * call to the identity callback of 'GNUNET_IDENTITY_connect' (if
2185  * that one was not NULL).
2186  *
2187  * When an identity is renamed, this function is called with the
2188  * (known) ego but the NEW identifier.
2189  *
2190  * When an identity is deleted, this function is called with the
2191  * (known) ego and "NULL" for the 'identifier'.  In this case,
2192  * the 'ego' is henceforth invalid (and the 'ctx' should also be
2193  * cleaned up).
2194  *
2195  * @param cls closure
2196  * @param ego ego handle
2197  * @param ctx context for application to store data for this ego
2198  *                 (during the lifetime of this process, initially NULL)
2199  * @param identifier identifier assigned by the user for this ego,
2200  *                   NULL if the user just deleted the ego and it
2201  *                   must thus no longer be used
2202  */
2203 static void
2204 list_ego (void *cls,
2205           struct GNUNET_IDENTITY_Ego *ego,
2206           void **ctx,
2207           const char *identifier)
2208 {
2209   struct RequestHandle *handle = cls;
2210   struct EgoEntry *ego_entry;
2211   struct GNUNET_CRYPTO_EcdsaPublicKey pk;
2212
2213   if ((NULL == ego) && (ID_REST_STATE_INIT == handle->state))
2214   {
2215     handle->state = ID_REST_STATE_POST_INIT;
2216     init_cont (handle);
2217     return;
2218   }
2219   GNUNET_assert (NULL != ego);
2220   if (ID_REST_STATE_INIT == handle->state)
2221
2222   {
2223     ego_entry = GNUNET_new (struct EgoEntry);
2224     GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
2225     ego_entry->keystring = GNUNET_CRYPTO_ecdsa_public_key_to_string (&pk);
2226     ego_entry->ego = ego;
2227     ego_entry->identifier = GNUNET_strdup (identifier);
2228     GNUNET_CONTAINER_DLL_insert_tail (handle->ego_head,
2229                                       handle->ego_tail,
2230                                       ego_entry);
2231     return;
2232   }
2233   /* Ego renamed or added */
2234   if (identifier != NULL)
2235   {
2236     for (ego_entry = handle->ego_head; NULL != ego_entry;
2237          ego_entry = ego_entry->next)
2238     {
2239       if (ego_entry->ego == ego)
2240       {
2241         /* Rename */
2242         GNUNET_free (ego_entry->identifier);
2243         ego_entry->identifier = GNUNET_strdup (identifier);
2244         break;
2245       }
2246     }
2247     if (NULL == ego_entry)
2248     {
2249       /* Add */
2250       ego_entry = GNUNET_new (struct EgoEntry);
2251       GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
2252       ego_entry->keystring = GNUNET_CRYPTO_ecdsa_public_key_to_string (&pk);
2253       ego_entry->ego = ego;
2254       ego_entry->identifier = GNUNET_strdup (identifier);
2255       GNUNET_CONTAINER_DLL_insert_tail (handle->ego_head,
2256                                         handle->ego_tail,
2257                                         ego_entry);
2258     }
2259   }
2260   else
2261   {
2262     /* Delete */
2263     for (ego_entry = handle->ego_head; NULL != ego_entry;
2264          ego_entry = ego_entry->next)
2265     {
2266       if (ego_entry->ego != ego)
2267         continue;
2268       GNUNET_CONTAINER_DLL_remove (handle->ego_head,
2269                                    handle->ego_tail,
2270                                    ego_entry);
2271       GNUNET_free (ego_entry->identifier);
2272       GNUNET_free (ego_entry->keystring);
2273       GNUNET_free (ego_entry);
2274       return;
2275     }
2276   }
2277 }
2278
2279
2280 static void
2281 rest_identity_process_request (struct GNUNET_REST_RequestHandle *rest_handle,
2282                                GNUNET_REST_ResultProcessor proc,
2283                                void *proc_cls)
2284 {
2285   struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
2286
2287   handle->oidc = GNUNET_new (struct OIDC_Variables);
2288   if (NULL == OIDC_cookie_jar_map)
2289     OIDC_cookie_jar_map = GNUNET_CONTAINER_multihashmap_create (10,
2290                                                                 GNUNET_NO);
2291   if (NULL == OIDC_access_token_map)
2292     OIDC_access_token_map =
2293       GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
2294   handle->response_code = 0;
2295   handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
2296   handle->proc_cls = proc_cls;
2297   handle->proc = proc;
2298   handle->state = ID_REST_STATE_INIT;
2299   handle->rest_handle = rest_handle;
2300
2301   handle->url = GNUNET_strdup (rest_handle->url);
2302   if (handle->url[strlen (handle->url) - 1] == '/')
2303     handle->url[strlen (handle->url) - 1] = '\0';
2304   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting...\n");
2305   handle->identity_handle = GNUNET_IDENTITY_connect (cfg, &list_ego, handle);
2306   handle->gns_handle = GNUNET_GNS_connect (cfg);
2307   handle->namestore_handle = GNUNET_NAMESTORE_connect (cfg);
2308   handle->timeout_task =
2309     GNUNET_SCHEDULER_add_delayed (handle->timeout, &do_timeout, handle);
2310   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connected\n");
2311 }
2312
2313
2314 /**
2315    * Entry point for the plugin.
2316    *
2317    * @param cls Config info
2318    * @return NULL on error, otherwise the plugin context
2319    */
2320 void *
2321 libgnunet_plugin_rest_openid_connect_init (void *cls)
2322 {
2323   static struct Plugin plugin;
2324   struct GNUNET_REST_Plugin *api;
2325
2326   cfg = cls;
2327   if (NULL != plugin.cfg)
2328     return NULL;     /* can only initialize once! */
2329   memset (&plugin, 0, sizeof(struct Plugin));
2330   plugin.cfg = cfg;
2331   api = GNUNET_new (struct GNUNET_REST_Plugin);
2332   api->cls = &plugin;
2333   api->name = GNUNET_REST_API_NS_OIDC;
2334   api->process_request = &rest_identity_process_request;
2335   GNUNET_asprintf (&allow_methods,
2336                    "%s, %s, %s, %s, %s",
2337                    MHD_HTTP_METHOD_GET,
2338                    MHD_HTTP_METHOD_POST,
2339                    MHD_HTTP_METHOD_PUT,
2340                    MHD_HTTP_METHOD_DELETE,
2341                    MHD_HTTP_METHOD_OPTIONS);
2342
2343   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2344               _ ("OpenID Connect REST API initialized\n"));
2345   return api;
2346 }
2347
2348
2349 /**
2350    * Exit point from the plugin.
2351    *
2352    * @param cls the plugin context (as returned by "init")
2353    * @return always NULL
2354    */
2355 void *
2356 libgnunet_plugin_rest_openid_connect_done (void *cls)
2357 {
2358   struct GNUNET_REST_Plugin *api = cls;
2359   struct Plugin *plugin = api->cls;
2360
2361   plugin->cfg = NULL;
2362
2363   struct GNUNET_CONTAINER_MultiHashMapIterator *hashmap_it;
2364   void *value = NULL;
2365   hashmap_it =
2366     GNUNET_CONTAINER_multihashmap_iterator_create (OIDC_cookie_jar_map);
2367   while (GNUNET_YES ==
2368          GNUNET_CONTAINER_multihashmap_iterator_next (hashmap_it, NULL,
2369                                                       value))
2370     GNUNET_free_non_null (value);
2371   GNUNET_CONTAINER_multihashmap_iterator_destroy (hashmap_it);
2372   GNUNET_CONTAINER_multihashmap_destroy (OIDC_cookie_jar_map);
2373
2374   hashmap_it =
2375     GNUNET_CONTAINER_multihashmap_iterator_create (OIDC_access_token_map);
2376   while (GNUNET_YES ==
2377          GNUNET_CONTAINER_multihashmap_iterator_next (hashmap_it, NULL,
2378                                                       value))
2379     GNUNET_free_non_null (value);
2380   GNUNET_CONTAINER_multihashmap_destroy (OIDC_access_token_map);
2381   GNUNET_CONTAINER_multihashmap_iterator_destroy (hashmap_it);
2382   GNUNET_free_non_null (allow_methods);
2383   GNUNET_free (api);
2384   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2385               "OpenID Connect REST plugin is finished\n");
2386   return NULL;
2387 }
2388
2389
2390 /* end of plugin_rest_openid_connect.c */