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