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