add logic to oauth2 plugin to use /setup endpoint when configured

This commit is contained in:
Christian Grothoff 2023-05-13 17:06:21 +02:00
parent ff1a28319f
commit c9ed524bc3
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC
4 changed files with 210 additions and 33 deletions

@ -1 +1 @@
Subproject commit 85736484cb0da26aded705ebb1e944e8bb1b8504 Subproject commit e50e37672fae7983fb5e934cd1d381b92648f7b6

View File

@ -13,11 +13,11 @@ PROVIDED_CHECKS = EXAMPLE_DO_NOT_USE
KYC_OAUTH2_VALIDITY = forever KYC_OAUTH2_VALIDITY = forever
# URL where we initiate the user's login process # URL where we initiate the user's login process
KYC_OAUTH2_LOGIN_URL = http://kyc.example.com/login KYC_OAUTH2_AUTHORIZE_URL = https://kyc.example.com/authorize
# URL where we send the user's authentication information # URL where we send the user's authentication information
KYC_OAUTH2_AUTH_URL = http://kyc.example.com/auth KYC_OAUTH2_TOKEN_URL = https://kyc.example.com/token
# URL of the user info access point. # URL of the user info access point.
KYC_OAUTH2_INFO_URL = http://kyc.example.com/info KYC_OAUTH2_INFO_URL = https://kyc.example.com/info
# Where does the client get redirected upon completion? # Where does the client get redirected upon completion?
KYC_OAUTH2_POST_URL = http://example.com/thank-you KYC_OAUTH2_POST_URL = http://example.com/thank-you

View File

@ -1,6 +1,6 @@
/* /*
This file is part of GNU Taler 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 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 terms of the GNU Affero General Public License as published by the Free Software
@ -75,15 +75,21 @@ struct TALER_KYCLOGIC_ProviderDetails
char *section; char *section;
/** /**
* URL of the OAuth2.0 endpoint for KYC checks. * URL of the Challenger ``/setup`` endpoint for
* (token/auth) * approving address validations. NULL if not used.
*/ */
char *auth_url; char *setup_url;
/** /**
* URL of the OAuth2.0 endpoint for KYC checks. * URL of the OAuth2.0 endpoint for KYC checks.
*/ */
char *login_url; char *authorize_url;
/**
* URL of the OAuth2.0 endpoint for KYC checks.
* (token/auth)
*/
char *token_url;
/** /**
* URL of the user info access endpoint. * URL of the user info access endpoint.
@ -147,6 +153,11 @@ struct TALER_KYCLOGIC_InitiateHandle
*/ */
struct GNUNET_SCHEDULER_Task *task; struct GNUNET_SCHEDULER_Task *task;
/**
* Handle for the OAuth 2.0 setup request.
*/
struct GNUNET_CURL_Job *job;
/** /**
* Continuation to call. * Continuation to call.
*/ */
@ -283,8 +294,9 @@ static void
oauth2_unload_configuration (struct TALER_KYCLOGIC_ProviderDetails *pd) oauth2_unload_configuration (struct TALER_KYCLOGIC_ProviderDetails *pd)
{ {
GNUNET_free (pd->section); GNUNET_free (pd->section);
GNUNET_free (pd->auth_url); GNUNET_free (pd->token_url);
GNUNET_free (pd->login_url); GNUNET_free (pd->setup_url);
GNUNET_free (pd->authorize_url);
GNUNET_free (pd->info_url); GNUNET_free (pd->info_url);
GNUNET_free (pd->client_id); GNUNET_free (pd->client_id);
GNUNET_free (pd->client_secret); GNUNET_free (pd->client_secret);
@ -327,12 +339,12 @@ oauth2_load_configuration (void *cls,
if (GNUNET_OK != if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (ps->cfg, GNUNET_CONFIGURATION_get_value_string (ps->cfg,
provider_section_name, provider_section_name,
"KYC_OAUTH2_AUTH_URL", "KYC_OAUTH2_TOKEN_URL",
&s)) &s))
{ {
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
provider_section_name, provider_section_name,
"KYC_OAUTH2_AUTH_URL"); "KYC_OAUTH2_TOKEN_URL");
oauth2_unload_configuration (pd); oauth2_unload_configuration (pd);
return NULL; return NULL;
} }
@ -346,23 +358,23 @@ oauth2_load_configuration (void *cls,
{ {
GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
provider_section_name, provider_section_name,
"KYC_OAUTH2_AUTH_URL", "KYC_OAUTH2_TOKEN_URL",
"not a valid URL"); "not a valid URL");
GNUNET_free (s); GNUNET_free (s);
oauth2_unload_configuration (pd); oauth2_unload_configuration (pd);
return NULL; return NULL;
} }
pd->auth_url = s; pd->token_url = s;
if (GNUNET_OK != if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (ps->cfg, GNUNET_CONFIGURATION_get_value_string (ps->cfg,
provider_section_name, provider_section_name,
"KYC_OAUTH2_LOGIN_URL", "KYC_OAUTH2_AUTHORIZE_URL",
&s)) &s))
{ {
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
provider_section_name, provider_section_name,
"KYC_OAUTH2_LOGIN_URL"); "KYC_OAUTH2_AUTHORIZE_URL");
oauth2_unload_configuration (pd); oauth2_unload_configuration (pd);
return NULL; return NULL;
} }
@ -376,13 +388,41 @@ oauth2_load_configuration (void *cls,
{ {
GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
provider_section_name, provider_section_name,
"KYC_OAUTH2_LOGIN_URL", "KYC_OAUTH2_AUTHORIZE_URL",
"not a valid URL"); "not a valid URL");
oauth2_unload_configuration (pd); oauth2_unload_configuration (pd);
GNUNET_free (s); GNUNET_free (s);
return NULL; 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 != if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_string (ps->cfg, 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 * how to begin the OAuth2.0 checking process to
* the client. * the client.
* *
* @param cls a `struct TALER_KYCLOGIC_InitiateHandle *` * @param ih process to redirect for
* @param authorize_url authorization URL to use
*/ */
static void 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; const struct TALER_KYCLOGIC_ProviderDetails *pd = ih->pd;
struct PluginState *ps = pd->ps; struct PluginState *ps = pd->ps;
char *hps; char *hps;
char *url; char *url;
char legi_s[42]; char legi_s[42];
ih->task = NULL;
GNUNET_snprintf (legi_s, GNUNET_snprintf (legi_s,
sizeof (legi_s), sizeof (legi_s),
"%llu", "%llu",
@ -515,16 +556,11 @@ initiate_task (void *cls)
} }
GNUNET_asprintf (&url, GNUNET_asprintf (&url,
"%s?response_type=code&client_id=%s&redirect_uri=%s", "%s?response_type=code&client_id=%s&redirect_uri=%s",
pd->login_url, authorize_url,
pd->client_id, pd->client_id,
redirect_uri_encoded); redirect_uri_encoded);
GNUNET_free (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, ih->cb (ih->cb_cls,
TALER_EC_NONE, TALER_EC_NONE,
url, url,
@ -537,6 +573,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. * Initiate KYC check.
* *
@ -584,6 +756,11 @@ oauth2_initiate_cancel (struct TALER_KYCLOGIC_InitiateHandle *ih)
GNUNET_SCHEDULER_cancel (ih->task); GNUNET_SCHEDULER_cancel (ih->task);
ih->task = NULL; ih->task = NULL;
} }
if (NULL != ih->job)
{
GNUNET_CURL_job_cancel (ih->job);
ih->job = NULL;
}
GNUNET_free (ih); GNUNET_free (ih);
} }
@ -1002,7 +1179,7 @@ handle_curl_login_finished (void *cls,
eh = curl_easy_init (); eh = curl_easy_init ();
if (NULL == eh) if (NULL == eh)
{ {
GNUNET_break_op (0); GNUNET_break (0);
ph->response ph->response
= TALER_MHD_make_error ( = TALER_MHD_make_error (
TALER_EC_GENERIC_ALLOCATION_FAILURE, TALER_EC_GENERIC_ALLOCATION_FAILURE,
@ -1129,7 +1306,7 @@ oauth2_proof (void *cls,
GNUNET_assert (CURLE_OK == GNUNET_assert (CURLE_OK ==
curl_easy_setopt (ph->eh, curl_easy_setopt (ph->eh,
CURLOPT_URL, CURLOPT_URL,
pd->auth_url)); pd->token_url));
GNUNET_assert (CURLE_OK == GNUNET_assert (CURLE_OK ==
curl_easy_setopt (ph->eh, curl_easy_setopt (ph->eh,
CURLOPT_POST, CURLOPT_POST,

View File

@ -53,8 +53,8 @@ LOGIC = oauth2
USER_TYPE = INDIVIDUAL USER_TYPE = INDIVIDUAL
PROVIDED_CHECKS = DUMMY PROVIDED_CHECKS = DUMMY
KYC_OAUTH2_VALIDITY = forever KYC_OAUTH2_VALIDITY = forever
KYC_OAUTH2_AUTH_URL = http://localhost:6666/oauth/v2/token KYC_OAUTH2_TOKEN_URL = http://localhost:6666/oauth/v2/token
KYC_OAUTH2_LOGIN_URL = http://localhost:6666/oauth/v2/login KYC_OAUTH2_AUTHORIZE_URL = http://localhost:6666/oauth/v2/login
KYC_OAUTH2_INFO_URL = http://localhost:6666/api/user/me KYC_OAUTH2_INFO_URL = http://localhost:6666/api/user/me
KYC_OAUTH2_CLIENT_ID = taler-exchange KYC_OAUTH2_CLIENT_ID = taler-exchange
KYC_OAUTH2_CLIENT_SECRET = exchange-secret KYC_OAUTH2_CLIENT_SECRET = exchange-secret