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