diff options
author | Christian Grothoff <christian@grothoff.org> | 2023-05-13 17:06:21 +0200 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2023-05-13 17:06:21 +0200 |
commit | c9ed524bc32beb7f5e8be5eeed077e54b81cf4df (patch) | |
tree | a4bae09f5c33904c6354c8e7500d9a6d799e69b5 /src/kyclogic/plugin_kyclogic_oauth2.c | |
parent | ff1a28319fe31741958a0b1cfa761fd44878db45 (diff) |
add logic to oauth2 plugin to use /setup endpoint when configured
Diffstat (limited to 'src/kyclogic/plugin_kyclogic_oauth2.c')
-rw-r--r-- | src/kyclogic/plugin_kyclogic_oauth2.c | 229 |
1 files changed, 203 insertions, 26 deletions
diff --git a/src/kyclogic/plugin_kyclogic_oauth2.c b/src/kyclogic/plugin_kyclogic_oauth2.c index c72b04b7c..e7350f01c 100644 --- a/src/kyclogic/plugin_kyclogic_oauth2.c +++ b/src/kyclogic/plugin_kyclogic_oauth2.c @@ -1,6 +1,6 @@ /* This file is part of GNU Taler - Copyright (C) 2022 Taler Systems SA + Copyright (C) 2022-2023 Taler Systems SA Taler is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software @@ -75,15 +75,21 @@ struct TALER_KYCLOGIC_ProviderDetails char *section; /** + * URL of the Challenger ``/setup`` endpoint for + * approving address validations. NULL if not used. + */ + char *setup_url; + + /** * URL of the OAuth2.0 endpoint for KYC checks. - * (token/auth) */ - char *auth_url; + char *authorize_url; /** * URL of the OAuth2.0 endpoint for KYC checks. + * (token/auth) */ - char *login_url; + char *token_url; /** * URL of the user info access endpoint. @@ -148,6 +154,11 @@ struct TALER_KYCLOGIC_InitiateHandle struct GNUNET_SCHEDULER_Task *task; /** + * Handle for the OAuth 2.0 setup request. + */ + struct GNUNET_CURL_Job *job; + + /** * Continuation to call. */ TALER_KYCLOGIC_InitiateCallback cb; @@ -283,8 +294,9 @@ static void oauth2_unload_configuration (struct TALER_KYCLOGIC_ProviderDetails *pd) { GNUNET_free (pd->section); - GNUNET_free (pd->auth_url); - GNUNET_free (pd->login_url); + GNUNET_free (pd->token_url); + GNUNET_free (pd->setup_url); + GNUNET_free (pd->authorize_url); GNUNET_free (pd->info_url); GNUNET_free (pd->client_id); GNUNET_free (pd->client_secret); @@ -327,12 +339,12 @@ oauth2_load_configuration (void *cls, if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (ps->cfg, provider_section_name, - "KYC_OAUTH2_AUTH_URL", + "KYC_OAUTH2_TOKEN_URL", &s)) { GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, provider_section_name, - "KYC_OAUTH2_AUTH_URL"); + "KYC_OAUTH2_TOKEN_URL"); oauth2_unload_configuration (pd); return NULL; } @@ -346,23 +358,23 @@ oauth2_load_configuration (void *cls, { GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, provider_section_name, - "KYC_OAUTH2_AUTH_URL", + "KYC_OAUTH2_TOKEN_URL", "not a valid URL"); GNUNET_free (s); oauth2_unload_configuration (pd); return NULL; } - pd->auth_url = s; + pd->token_url = s; if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (ps->cfg, provider_section_name, - "KYC_OAUTH2_LOGIN_URL", + "KYC_OAUTH2_AUTHORIZE_URL", &s)) { GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, provider_section_name, - "KYC_OAUTH2_LOGIN_URL"); + "KYC_OAUTH2_AUTHORIZE_URL"); oauth2_unload_configuration (pd); return NULL; } @@ -376,13 +388,41 @@ oauth2_load_configuration (void *cls, { GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, provider_section_name, - "KYC_OAUTH2_LOGIN_URL", + "KYC_OAUTH2_AUTHORIZE_URL", "not a valid URL"); oauth2_unload_configuration (pd); GNUNET_free (s); return NULL; } - pd->login_url = s; + if (NULL != strchr (s, '#')) + { + const char *extra = strchr (s, '#'); + const char *slash = strrchr (s, '/'); + + if ( (0 != strcmp (extra, + "#setup")) || + (NULL == slash) ) + { + GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, + provider_section_name, + "KYC_OAUTH2_AUTHORIZE_URL", + "not a valid authorze URL (bad fragment)"); + oauth2_unload_configuration (pd); + GNUNET_free (s); + return NULL; + } + pd->authorize_url = GNUNET_strndup (s, + extra - s); + GNUNET_asprintf (&pd->setup_url, + "%.*s/setup", + (int) (slash - s), + s); + GNUNET_free (s); + } + else + { + pd->authorize_url = s; + } if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (ps->cfg, @@ -480,19 +520,20 @@ oauth2_load_configuration (void *cls, * how to begin the OAuth2.0 checking process to * the client. * - * @param cls a `struct TALER_KYCLOGIC_InitiateHandle *` + * @param ih process to redirect for + * @param authorize_url authorization URL to use */ static void -initiate_task (void *cls) +initiate_with_url (struct TALER_KYCLOGIC_InitiateHandle *ih, + const char *authorize_url) { - struct TALER_KYCLOGIC_InitiateHandle *ih = cls; + const struct TALER_KYCLOGIC_ProviderDetails *pd = ih->pd; struct PluginState *ps = pd->ps; char *hps; char *url; char legi_s[42]; - ih->task = NULL; GNUNET_snprintf (legi_s, sizeof (legi_s), "%llu", @@ -515,16 +556,11 @@ initiate_task (void *cls) } GNUNET_asprintf (&url, "%s?response_type=code&client_id=%s&redirect_uri=%s", - pd->login_url, + authorize_url, pd->client_id, redirect_uri_encoded); GNUNET_free (redirect_uri_encoded); } - /* FIXME-API: why do we *redirect* the client here, - instead of making the HTTP request *ourselves* - and forwarding the response? This prevents us - from using authentication on initiation, - (which is desirable for challenger!) */ ih->cb (ih->cb_cls, TALER_EC_NONE, url, @@ -538,6 +574,142 @@ initiate_task (void *cls) /** + * After we are done with the CURL interaction we + * need to update our database state with the information + * retrieved. + * + * @param cls a `struct TALER_KYCLOGIC_InitiateHandle *` + * @param response_code HTTP response code from server, 0 on hard error + * @param response in JSON, NULL if response was not in JSON format + */ +static void +handle_curl_setup_finished (void *cls, + long response_code, + const void *response) +{ + struct TALER_KYCLOGIC_InitiateHandle *ih = cls; + const struct TALER_KYCLOGIC_ProviderDetails *pd = ih->pd; + const json_t *j = response; + + ih->job = NULL; + switch (response_code) + { + case MHD_HTTP_OK: + { + const char *nonce; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("nonce", + &nonce), + GNUNET_JSON_spec_end () + }; + enum GNUNET_GenericReturnValue res; + const char *emsg; + unsigned int line; + char *url; + + res = GNUNET_JSON_parse (j, + spec, + &emsg, + &line); + if (GNUNET_OK != res) + { + GNUNET_break_op (0); + json_dumpf (j, + stderr, + JSON_INDENT (2)); + ih->cb (ih->cb_cls, + TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE, + NULL, + NULL, + NULL, + "Unexpected response from KYC gateway: setup must return a nonce"); + GNUNET_free (ih); + return; + } + GNUNET_asprintf (&url, + "%s/%s", + pd->setup_url, + nonce); + initiate_with_url (ih, + url); + GNUNET_free (url); + return; + } + break; + default: + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "/setup URL returned HTTP status %u\n", + (unsigned int) response_code); + ih->cb (ih->cb_cls, + TALER_EC_EXCHANGE_KYC_PROOF_BACKEND_INVALID_RESPONSE, + NULL, + NULL, + NULL, + "/setup request to OAuth 2.0 backend returned unexpected HTTP status code"); + GNUNET_free (ih); + return; + } +} + + +/** + * Logic to asynchronously return the response for how to begin the OAuth2.0 + * checking process to the client. May first request a dynamic URL via + * ``/setup`` if configured to use a client-authenticated setup process. + * + * @param cls a `struct TALER_KYCLOGIC_InitiateHandle *` + */ +static void +initiate_task (void *cls) +{ + struct TALER_KYCLOGIC_InitiateHandle *ih = cls; + const struct TALER_KYCLOGIC_ProviderDetails *pd = ih->pd; + struct PluginState *ps = pd->ps; + char *hdr; + struct curl_slist *slist; + CURL *eh; + + ih->task = NULL; + if (NULL == pd->setup_url) + { + initiate_with_url (ih, + pd->authorize_url); + return; + } + eh = curl_easy_init (); + if (NULL == eh) + { + GNUNET_break (0); + ih->cb (ih->cb_cls, + TALER_EC_GENERIC_ALLOCATION_FAILURE, + NULL, + NULL, + NULL, + "curl_easy_init() failed"); + GNUNET_free (ih); + return; + } + GNUNET_assert (CURLE_OK == + curl_easy_setopt (eh, + CURLOPT_URL, + pd->setup_url)); + GNUNET_asprintf (&hdr, + "%s: Bearer %s", + MHD_HTTP_HEADER_AUTHORIZATION, + pd->client_secret); + slist = curl_slist_append (NULL, + hdr); + ih->job = GNUNET_CURL_job_add2 (ps->curl_ctx, + eh, + slist, + &handle_curl_setup_finished, + ih); + curl_slist_free_all (slist); + GNUNET_free (hdr); +} + + +/** * Initiate KYC check. * * @param cls the @e cls of this struct with the plugin-specific state @@ -584,6 +756,11 @@ oauth2_initiate_cancel (struct TALER_KYCLOGIC_InitiateHandle *ih) GNUNET_SCHEDULER_cancel (ih->task); ih->task = NULL; } + if (NULL != ih->job) + { + GNUNET_CURL_job_cancel (ih->job); + ih->job = NULL; + } GNUNET_free (ih); } @@ -1002,7 +1179,7 @@ handle_curl_login_finished (void *cls, eh = curl_easy_init (); if (NULL == eh) { - GNUNET_break_op (0); + GNUNET_break (0); ph->response = TALER_MHD_make_error ( TALER_EC_GENERIC_ALLOCATION_FAILURE, @@ -1129,7 +1306,7 @@ oauth2_proof (void *cls, GNUNET_assert (CURLE_OK == curl_easy_setopt (ph->eh, CURLOPT_URL, - pd->auth_url)); + pd->token_url)); GNUNET_assert (CURLE_OK == curl_easy_setopt (ph->eh, CURLOPT_POST, |