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