challenger

OAuth 2.0-based authentication service that validates user can receive messages at a certain address
Log | Files | Refs | Submodules | README | LICENSE

commit 514c54aed35ae3e4b046abc3e059f46e67083e5f
parent cc8ff79d4dd67c09849efcbcc0f9c29a7c61f1eb
Author: Christian Grothoff <christian@grothoff.org>
Date:   Mon,  8 May 2023 17:03:22 +0200

rename /auth to /token

Diffstat:
Msrc/challenger/Makefile.am | 4++--
Msrc/challenger/challenger-httpd.c | 6+++---
Dsrc/challenger/challenger-httpd_auth.c | 442-------------------------------------------------------------------------------
Dsrc/challenger/challenger-httpd_auth.h | 41-----------------------------------------
Asrc/challenger/challenger-httpd_token.c | 442+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/challenger/challenger-httpd_token.h | 41+++++++++++++++++++++++++++++++++++++++++
Msrc/challenger/test-challenger.sh | 2+-
Msrc/challengerdb/Makefile.am | 2+-
Dsrc/challengerdb/pg_auth_add_grant.c | 62--------------------------------------------------------------
Dsrc/challengerdb/pg_auth_add_grant.h | 48------------------------------------------------
Asrc/challengerdb/pg_token_add_grant.c | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/challengerdb/pg_token_add_grant.h | 48++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/challengerdb/plugin_challengerdb_postgres.c | 6+++---
Msrc/include/challenger_database_plugin.h | 10+++++-----
14 files changed, 608 insertions(+), 608 deletions(-)

diff --git a/src/challenger/Makefile.am b/src/challenger/Makefile.am @@ -37,7 +37,6 @@ challenger_admin_LDADD = \ challenger_httpd_SOURCES = \ challenger-httpd.c challenger-httpd.h \ - challenger-httpd_auth.c challenger-httpd_auth.h \ challenger-httpd_challenge.c challenger-httpd_challenge.h \ challenger-httpd_common.c challenger-httpd_common.h \ challenger-httpd_config.c challenger-httpd_config.h \ @@ -45,7 +44,8 @@ challenger_httpd_SOURCES = \ challenger-httpd_login.c challenger-httpd_login.h \ challenger-httpd_mhd.c challenger-httpd_mhd.h \ challenger-httpd_setup.c challenger-httpd_setup.h \ - challenger-httpd_solve.c challenger-httpd_solve.h + challenger-httpd_solve.c challenger-httpd_solve.h \ + challenger-httpd_token.c challenger-httpd_token.h challenger_httpd_LDADD = \ $(top_builddir)/src/util/libchallengerutil.la \ $(top_builddir)/src/challengerdb/libchallengerdb.la \ diff --git a/src/challenger/challenger-httpd.c b/src/challenger/challenger-httpd.c @@ -24,13 +24,13 @@ #include <gnunet/gnunet_curl_lib.h> #include "challenger_util.h" #include "challenger-httpd.h" -#include "challenger-httpd_auth.h" #include "challenger-httpd_challenge.h" #include "challenger-httpd_info.h" #include "challenger-httpd_setup.h" #include "challenger-httpd_login.h" #include "challenger-httpd_mhd.h" #include "challenger-httpd_solve.h" +#include "challenger-httpd_token.h" #include "challenger_database_lib.h" #include "challenger-httpd_config.h" #include <taler/taler_templating_lib.h> @@ -197,9 +197,9 @@ url_handler (void *cls, .handler = &CH_handler_solve }, { - .url = "/auth", + .url = "/token", .method = MHD_HTTP_METHOD_POST, - .handler = &CH_handler_auth + .handler = &CH_handler_token }, { .url = "/info", diff --git a/src/challenger/challenger-httpd_auth.c b/src/challenger/challenger-httpd_auth.c @@ -1,442 +0,0 @@ -/* - This file is part of Challenger - Copyright (C) 2023 Taler Systems SA - - Challenger 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 - Foundation; either version 3, or (at your option) any later version. - - Challenger is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License along with - Challenger; see the file COPYING. If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file challenger-httpd_auth.c - * @brief functions to handle incoming /auth requests - * @author Christian Grothoff - */ -#include "platform.h" -#include "challenger-httpd.h" -#include <gnunet/gnunet_util_lib.h> -#include "challenger-httpd_auth.h" -#include "challenger-httpd_common.h" -#include <taler/taler_json_lib.h> -#include <taler/taler_merchant_service.h> -#include <taler/taler_signatures.h> - - -/** - * Context for a /auth operation. - */ -struct AuthContext -{ - - /** - * Nonce of the validation process the request is about. - */ - struct CHALLENGER_ValidationNonceP nonce; - - /** - * Handle for processing uploaded data. - */ - struct MHD_PostProcessor *pp; - - /** - * Uploaded 'client_id' field from POST data. - */ - char *client_id; - - /** - * Uploaded 'client_id' field from POST data. - */ - char *redirect_uri; - - /** - * Uploaded 'client_secret' field from POST data. - */ - char *client_secret; - - /** - * Uploaded 'code' field from POST data. - */ - char *code; - - /** - * Uploaded 'grant_type' field from POST data. - */ - char *grant_type; -}; - - -/** - * Function called to clean up a backup context. - * - * @param cls a `struct AuthContext` - */ -static void -cleanup_ctx (void *cls) -{ - struct AuthContext *bc = cls; - - if (NULL != bc->pp) - { - GNUNET_break_op (MHD_YES == - MHD_destroy_post_processor (bc->pp)); - } - GNUNET_free (bc->client_id); - GNUNET_free (bc->redirect_uri); - GNUNET_free (bc->client_secret); - GNUNET_free (bc->code); - GNUNET_free (bc->grant_type); - GNUNET_free (bc); -} - - -/** - * Iterator over key-value pairs where the value may be made available - * in increments and/or may not be zero-terminated. Used for - * processing POST data. - * - * @param cls a `struct AuthContext *` - * @param kind type of the value, always #MHD_POSTDATA_KIND when called from MHD - * @param key 0-terminated key for the value - * @param filename name of the uploaded file, NULL if not known - * @param content_type mime-type of the data, NULL if not known - * @param transfer_encoding encoding of the data, NULL if not known - * @param data pointer to @a size bytes of data at the - * specified offset - * @param off offset of data in the overall value - * @param size number of bytes in @a data available - * @return #MHD_YES to continue iterating, - * #MHD_NO to abort the iteration - */ -static enum MHD_Result -post_iter (void *cls, - enum MHD_ValueKind kind, - const char *key, - const char *filename, - const char *content_type, - const char *transfer_encoding, - const char *data, - uint64_t off, - size_t size) -{ - struct AuthContext *bc = cls; - struct Map - { - const char *name; - char **ptr; - } map[] = { - { - .name = "client_id", - .ptr = &bc->client_id - }, - { - .name = "redirect_uri", - .ptr = &bc->redirect_uri - }, - { - .name = "client_secret", - .ptr = &bc->client_secret - }, - { - .name = "code", - .ptr = &bc->code - }, - { - .name = "grant_type", - .ptr = &bc->grant_type - }, - { - .name = NULL, - .ptr = NULL - }, - }; - char **ptr = NULL; - size_t slen; - - (void) kind; - (void) filename; - (void) content_type; - (void) transfer_encoding; - (void) off; - for (unsigned int i = 0; NULL != map[i].name; i++) - if (0 == strcmp (key, - map[i].name)) - ptr = map[i].ptr; - if (NULL == ptr) - return MHD_YES; /* ignore */ - if (NULL == *ptr) - slen = 0; - else - slen = strlen (*ptr); - if (NULL == *ptr) - *ptr = GNUNET_malloc (size + 1); - else - *ptr = GNUNET_realloc (*ptr, - slen + size + 1); - memcpy (&(*ptr)[slen], - data, - size); - return MHD_YES; -} - - -MHD_RESULT -CH_handler_auth (struct CH_HandlerContext *hc, - const char *upload_data, - size_t *upload_data_size) -{ - struct AuthContext *bc = hc->ctx; - - if (NULL == bc) - { - /* first call, setup internals */ - bc = GNUNET_new (struct AuthContext); - hc->cc = &cleanup_ctx; - hc->ctx = bc; - bc->pp = MHD_create_post_processor (hc->connection, - 2 * 1024, - &post_iter, - bc); - TALER_MHD_check_content_length (hc->connection, - 2 * 1024); - return MHD_YES; - } - /* handle upload */ - if (0 != *upload_data_size) - { - enum MHD_Result res; - - res = MHD_post_process (bc->pp, - upload_data, - *upload_data_size); - *upload_data_size = 0; - if (MHD_YES == res) - return MHD_YES; - return MHD_NO; - } - if ( (NULL == bc->grant_type) || - (0 != strcmp (bc->grant_type, - "authorization_code")) ) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (hc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "authorization_code"); - } - - if (NULL == bc->code) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (hc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MISSING, - "code"); - } - if (NULL == bc->client_secret) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (hc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MISSING, - "client_secret"); - } - if (NULL == bc->client_id) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (hc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MISSING, - "client_id"); - } - if (NULL == bc->redirect_uri) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (hc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MISSING, - "redirect_uri"); - } - - /* Check this client is authorized to access the service */ - { - enum GNUNET_DB_QueryStatus qs; - char *client_url = NULL; - unsigned long long client_id; - char dummy; - - if (1 != sscanf (bc->client_id, - "%llu%c", - &client_id, - &dummy)) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (hc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "client_id"); - } - - qs = CH_db->client_check (CH_db->cls, - client_id, - bc->client_secret, - 0, /* do not increment */ - &client_url); - switch (qs) - { - case GNUNET_DB_STATUS_HARD_ERROR: - GNUNET_break (0); - return TALER_MHD_reply_with_error (hc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - "client_check"); - case GNUNET_DB_STATUS_SOFT_ERROR: - GNUNET_break (0); - return MHD_NO; - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (hc->connection, - MHD_HTTP_NOT_FOUND, - TALER_EC_CHALLENGER_GENERIC_CLIENT_UNKNOWN, - NULL); - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - break; - } - if ( (NULL != client_url) && - (0 != strcmp (client_url, - bc->redirect_uri)) ) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (hc->connection, - MHD_HTTP_FORBIDDEN, - TALER_EC_CHALLENGER_GENERIC_CLIENT_FORBIDDEN_BAD_REDIRECT_URI, - NULL); - } - GNUNET_free (client_url); - } - - if (GNUNET_OK != - CH_code_to_nonce (bc->code, - &bc->nonce)) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (hc->connection, - MHD_HTTP_FORBIDDEN, - TALER_EC_CHALLENGER_CLIENT_FORBIDDEN_BAD_CODE, - NULL); - } - - /* Check code is valid */ - { - char *client_secret; - char *address; - char *client_scope; - char *client_state; - char *client_redirect_url; - enum GNUNET_DB_QueryStatus qs; - char *code; - - qs = CH_db->validation_get (CH_db->cls, - &bc->nonce, - &client_secret, - &address, - &client_scope, - &client_state, - &client_redirect_url); - switch (qs) - { - case GNUNET_DB_STATUS_HARD_ERROR: - GNUNET_break (0); - return TALER_MHD_reply_with_error (hc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - "validation_get"); - case GNUNET_DB_STATUS_SOFT_ERROR: - GNUNET_break (0); - return MHD_NO; - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (hc->connection, - MHD_HTTP_NOT_FOUND, - TALER_EC_CHALLENGER_GENERIC_VALIDATION_UNKNOWN, - "validation_get"); - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - break; - } - code = CH_compute_code (&bc->nonce, - client_secret, - client_scope, - address, - client_redirect_url); - GNUNET_free (address); - GNUNET_free (client_scope); - GNUNET_free (client_secret); - GNUNET_free (client_redirect_url); - GNUNET_free (client_state); - if (0 != strcmp (code, - bc->code)) - { - GNUNET_break_op (0); - GNUNET_free (code); - return TALER_MHD_reply_with_error (hc->connection, - MHD_HTTP_FORBIDDEN, - TALER_EC_CHALLENGER_CLIENT_FORBIDDEN_BAD_CODE, - "code"); - } - GNUNET_free (code); - } - - { - struct CHALLENGER_AccessTokenP grant; - enum GNUNET_DB_QueryStatus qs; - /* FIXME: do not hard-code 1h? */ - struct GNUNET_TIME_Relative grant_expiration - = GNUNET_TIME_UNIT_HOURS; - - GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, - &grant, - sizeof (grant)); - qs = CH_db->auth_add_grant (CH_db->cls, - &bc->nonce, - &grant, - grant_expiration, - CH_validation_expiration); - switch (qs) - { - case GNUNET_DB_STATUS_HARD_ERROR: - GNUNET_break (0); - return TALER_MHD_reply_with_error (hc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_STORE_FAILED, - "auth_add_grant"); - case GNUNET_DB_STATUS_SOFT_ERROR: - GNUNET_break (0); - return MHD_NO; - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - GNUNET_break (0); - return TALER_MHD_reply_with_error (hc->connection, - MHD_HTTP_NOT_FOUND, - TALER_EC_CHALLENGER_GRANT_UNKNOWN, - "auth_add_grant"); - case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: - break; - } - - - return TALER_MHD_REPLY_JSON_PACK ( - hc->connection, - MHD_HTTP_OK, - GNUNET_JSON_pack_data_auto ("access_token", - &grant), - GNUNET_JSON_pack_string ("token_type", - "Bearer"), - GNUNET_JSON_pack_uint64 ("expires_in", - grant_expiration.rel_value_us - / GNUNET_TIME_UNIT_SECONDS.rel_value_us)); - } -} diff --git a/src/challenger/challenger-httpd_auth.h b/src/challenger/challenger-httpd_auth.h @@ -1,41 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 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 - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License along with - TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file challenger-httpd_auth.h - * @brief functions to handle incoming requests on /auth - * @author Christian Grothoff - */ -#ifndef AUTHR_HTTPD_AUTH_H -#define AUTHR_HTTPD_AUTH_H - -#include <microhttpd.h> - - -/** - * Handle a client POSTing a /auth request - * - * @param hc context of the connection - * @param upload_data upload data, if any - * @param[in,out] upload_data_size remaining data in @a upload_data, to be updated - * @return MHD result code - */ -MHD_RESULT -CH_handler_auth (struct CH_HandlerContext *hc, - const char *upload_data, - size_t *upload_data_size); - - -#endif diff --git a/src/challenger/challenger-httpd_token.c b/src/challenger/challenger-httpd_token.c @@ -0,0 +1,442 @@ +/* + This file is part of Challenger + Copyright (C) 2023 Taler Systems SA + + Challenger 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 + Foundation; either version 3, or (at your option) any later version. + + Challenger is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + Challenger; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file challenger-httpd_token.c + * @brief functions to handle incoming /token requests + * @author Christian Grothoff + */ +#include "platform.h" +#include "challenger-httpd.h" +#include <gnunet/gnunet_util_lib.h> +#include "challenger-httpd_token.h" +#include "challenger-httpd_common.h" +#include <taler/taler_json_lib.h> +#include <taler/taler_merchant_service.h> +#include <taler/taler_signatures.h> + + +/** + * Context for a /token operation. + */ +struct TokenContext +{ + + /** + * Nonce of the validation process the request is about. + */ + struct CHALLENGER_ValidationNonceP nonce; + + /** + * Handle for processing uploaded data. + */ + struct MHD_PostProcessor *pp; + + /** + * Uploaded 'client_id' field from POST data. + */ + char *client_id; + + /** + * Uploaded 'client_id' field from POST data. + */ + char *redirect_uri; + + /** + * Uploaded 'client_secret' field from POST data. + */ + char *client_secret; + + /** + * Uploaded 'code' field from POST data. + */ + char *code; + + /** + * Uploaded 'grant_type' field from POST data. + */ + char *grant_type; +}; + + +/** + * Function called to clean up a backup context. + * + * @param cls a `struct TokenContext` + */ +static void +cleanup_ctx (void *cls) +{ + struct TokenContext *bc = cls; + + if (NULL != bc->pp) + { + GNUNET_break_op (MHD_YES == + MHD_destroy_post_processor (bc->pp)); + } + GNUNET_free (bc->client_id); + GNUNET_free (bc->redirect_uri); + GNUNET_free (bc->client_secret); + GNUNET_free (bc->code); + GNUNET_free (bc->grant_type); + GNUNET_free (bc); +} + + +/** + * Iterator over key-value pairs where the value may be made available + * in increments and/or may not be zero-terminated. Used for + * processing POST data. + * + * @param cls a `struct TokenContext *` + * @param kind type of the value, always #MHD_POSTDATA_KIND when called from MHD + * @param key 0-terminated key for the value + * @param filename name of the uploaded file, NULL if not known + * @param content_type mime-type of the data, NULL if not known + * @param transfer_encoding encoding of the data, NULL if not known + * @param data pointer to @a size bytes of data at the + * specified offset + * @param off offset of data in the overall value + * @param size number of bytes in @a data available + * @return #MHD_YES to continue iterating, + * #MHD_NO to abort the iteration + */ +static enum MHD_Result +post_iter (void *cls, + enum MHD_ValueKind kind, + const char *key, + const char *filename, + const char *content_type, + const char *transfer_encoding, + const char *data, + uint64_t off, + size_t size) +{ + struct TokenContext *bc = cls; + struct Map + { + const char *name; + char **ptr; + } map[] = { + { + .name = "client_id", + .ptr = &bc->client_id + }, + { + .name = "redirect_uri", + .ptr = &bc->redirect_uri + }, + { + .name = "client_secret", + .ptr = &bc->client_secret + }, + { + .name = "code", + .ptr = &bc->code + }, + { + .name = "grant_type", + .ptr = &bc->grant_type + }, + { + .name = NULL, + .ptr = NULL + }, + }; + char **ptr = NULL; + size_t slen; + + (void) kind; + (void) filename; + (void) content_type; + (void) transfer_encoding; + (void) off; + for (unsigned int i = 0; NULL != map[i].name; i++) + if (0 == strcmp (key, + map[i].name)) + ptr = map[i].ptr; + if (NULL == ptr) + return MHD_YES; /* ignore */ + if (NULL == *ptr) + slen = 0; + else + slen = strlen (*ptr); + if (NULL == *ptr) + *ptr = GNUNET_malloc (size + 1); + else + *ptr = GNUNET_realloc (*ptr, + slen + size + 1); + memcpy (&(*ptr)[slen], + data, + size); + return MHD_YES; +} + + +MHD_RESULT +CH_handler_token (struct CH_HandlerContext *hc, + const char *upload_data, + size_t *upload_data_size) +{ + struct TokenContext *bc = hc->ctx; + + if (NULL == bc) + { + /* first call, setup internals */ + bc = GNUNET_new (struct TokenContext); + hc->cc = &cleanup_ctx; + hc->ctx = bc; + bc->pp = MHD_create_post_processor (hc->connection, + 2 * 1024, + &post_iter, + bc); + TALER_MHD_check_content_length (hc->connection, + 2 * 1024); + return MHD_YES; + } + /* handle upload */ + if (0 != *upload_data_size) + { + enum MHD_Result res; + + res = MHD_post_process (bc->pp, + upload_data, + *upload_data_size); + *upload_data_size = 0; + if (MHD_YES == res) + return MHD_YES; + return MHD_NO; + } + if ( (NULL == bc->grant_type) || + (0 != strcmp (bc->grant_type, + "authorization_code")) ) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (hc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "authorization_code"); + } + + if (NULL == bc->code) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (hc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MISSING, + "code"); + } + if (NULL == bc->client_secret) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (hc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MISSING, + "client_secret"); + } + if (NULL == bc->client_id) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (hc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MISSING, + "client_id"); + } + if (NULL == bc->redirect_uri) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (hc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MISSING, + "redirect_uri"); + } + + /* Check this client is authorized to access the service */ + { + enum GNUNET_DB_QueryStatus qs; + char *client_url = NULL; + unsigned long long client_id; + char dummy; + + if (1 != sscanf (bc->client_id, + "%llu%c", + &client_id, + &dummy)) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (hc->connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "client_id"); + } + + qs = CH_db->client_check (CH_db->cls, + client_id, + bc->client_secret, + 0, /* do not increment */ + &client_url); + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + GNUNET_break (0); + return TALER_MHD_reply_with_error (hc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + "client_check"); + case GNUNET_DB_STATUS_SOFT_ERROR: + GNUNET_break (0); + return MHD_NO; + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (hc->connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_CHALLENGER_GENERIC_CLIENT_UNKNOWN, + NULL); + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + break; + } + if ( (NULL != client_url) && + (0 != strcmp (client_url, + bc->redirect_uri)) ) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (hc->connection, + MHD_HTTP_FORBIDDEN, + TALER_EC_CHALLENGER_GENERIC_CLIENT_FORBIDDEN_BAD_REDIRECT_URI, + NULL); + } + GNUNET_free (client_url); + } + + if (GNUNET_OK != + CH_code_to_nonce (bc->code, + &bc->nonce)) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (hc->connection, + MHD_HTTP_FORBIDDEN, + TALER_EC_CHALLENGER_CLIENT_FORBIDDEN_BAD_CODE, + NULL); + } + + /* Check code is valid */ + { + char *client_secret; + char *address; + char *client_scope; + char *client_state; + char *client_redirect_url; + enum GNUNET_DB_QueryStatus qs; + char *code; + + qs = CH_db->validation_get (CH_db->cls, + &bc->nonce, + &client_secret, + &address, + &client_scope, + &client_state, + &client_redirect_url); + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + GNUNET_break (0); + return TALER_MHD_reply_with_error (hc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + "validation_get"); + case GNUNET_DB_STATUS_SOFT_ERROR: + GNUNET_break (0); + return MHD_NO; + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (hc->connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_CHALLENGER_GENERIC_VALIDATION_UNKNOWN, + "validation_get"); + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + break; + } + code = CH_compute_code (&bc->nonce, + client_secret, + client_scope, + address, + client_redirect_url); + GNUNET_free (address); + GNUNET_free (client_scope); + GNUNET_free (client_secret); + GNUNET_free (client_redirect_url); + GNUNET_free (client_state); + if (0 != strcmp (code, + bc->code)) + { + GNUNET_break_op (0); + GNUNET_free (code); + return TALER_MHD_reply_with_error (hc->connection, + MHD_HTTP_FORBIDDEN, + TALER_EC_CHALLENGER_CLIENT_FORBIDDEN_BAD_CODE, + "code"); + } + GNUNET_free (code); + } + + { + struct CHALLENGER_AccessTokenP grant; + enum GNUNET_DB_QueryStatus qs; + /* FIXME: do not hard-code 1h? */ + struct GNUNET_TIME_Relative grant_expiration + = GNUNET_TIME_UNIT_HOURS; + + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, + &grant, + sizeof (grant)); + qs = CH_db->token_add_grant (CH_db->cls, + &bc->nonce, + &grant, + grant_expiration, + CH_validation_expiration); + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + GNUNET_break (0); + return TALER_MHD_reply_with_error (hc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_STORE_FAILED, + "token_add_grant"); + case GNUNET_DB_STATUS_SOFT_ERROR: + GNUNET_break (0); + return MHD_NO; + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + GNUNET_break (0); + return TALER_MHD_reply_with_error (hc->connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_CHALLENGER_GRANT_UNKNOWN, + "token_add_grant"); + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + break; + } + + + return TALER_MHD_REPLY_JSON_PACK ( + hc->connection, + MHD_HTTP_OK, + GNUNET_JSON_pack_data_auto ("access_token", + &grant), + GNUNET_JSON_pack_string ("token_type", + "Bearer"), + GNUNET_JSON_pack_uint64 ("expires_in", + grant_expiration.rel_value_us + / GNUNET_TIME_UNIT_SECONDS.rel_value_us)); + } +} diff --git a/src/challenger/challenger-httpd_token.h b/src/challenger/challenger-httpd_token.h @@ -0,0 +1,41 @@ +/* + This file is part of TALER + Copyright (C) 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file challenger-httpd_token.h + * @brief functions to handle incoming requests on /token + * @author Christian Grothoff + */ +#ifndef AUTHR_HTTPD_TOKEN_H +#define AUTHR_HTTPD_TOKEN_H + +#include <microhttpd.h> + + +/** + * Handle a client POSTing a /token request + * + * @param hc context of the connection + * @param upload_data upload data, if any + * @param[in,out] upload_data_size remaining data in @a upload_data, to be updated + * @return MHD result code + */ +MHD_RESULT +CH_handler_token (struct CH_HandlerContext *hc, + const char *upload_data, + size_t *upload_data_size); + + +#endif diff --git a/src/challenger/test-challenger.sh b/src/challenger/test-challenger.sh @@ -156,7 +156,7 @@ fi echo "OK" echo -n "Requesting authorization for client ..." -STATUS=$(curl "${BURL}/auth" \ +STATUS=$(curl "${BURL}/token" \ -X POST \ --data-urlencode "client_id=${CLIENT_ID}" \ --data-urlencode "redirect_uri=${REDIRECT_URI}" \ diff --git a/src/challengerdb/Makefile.am b/src/challengerdb/Makefile.am @@ -61,7 +61,7 @@ libchallenger_plugin_db_postgres_la_SOURCES = \ pg_client_delete.h pg_client_delete.c \ pg_client_check.h pg_client_check.c \ pg_info_get_grant.h pg_info_get_grant.c \ - pg_auth_add_grant.h pg_auth_add_grant.c \ + pg_token_add_grant.h pg_token_add_grant.c \ pg_setup_nonce.h pg_setup_nonce.c \ pg_login_start.h pg_login_start.c \ pg_challenge_set_address_and_pin.h pg_challenge_set_address_and_pin.c \ diff --git a/src/challengerdb/pg_auth_add_grant.c b/src/challengerdb/pg_auth_add_grant.c @@ -1,62 +0,0 @@ -/* - This file is part of Challenger - Copyright (C) 2023 Taler Systems SA - - Challenger is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - Challenger is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - Challenger; see the file COPYING. If not, see <http://www.gnu.org/licenses/> - */ -/** - * @file challengerdb/pg_auth_add_grant.c - * @brief Implementation of the auth_add_grant function for Postgres - * @author Christian Grothoff - */ -#include "platform.h" -#include <taler/taler_error_codes.h> -#include <taler/taler_dbevents.h> -#include <taler/taler_pq_lib.h> -#include "pg_auth_add_grant.h" -#include "pg_helper.h" - - -enum GNUNET_DB_QueryStatus -CH_PG_auth_add_grant ( - void *cls, - const struct CHALLENGER_ValidationNonceP *nonce, - const struct CHALLENGER_AccessTokenP *grant, - struct GNUNET_TIME_Relative grant_expiration, - struct GNUNET_TIME_Relative address_expiration) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_TIME_Absolute ge - = GNUNET_TIME_relative_to_absolute (grant_expiration); - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (nonce), - GNUNET_PQ_query_param_auto_from_type (grant), - GNUNET_PQ_query_param_absolute_time (&ge), - GNUNET_PQ_query_param_relative_time (&address_expiration), - GNUNET_PQ_query_param_end - }; - - PREPARE (pg, - "auth_add_grant", - "INSERT INTO grants" - " (access_token" - " ,address" - " ,grant_expiration_time" - " ,address_expiration_time" - ") SELECT" - " $2, address, $3, $4 + last_tx_time" - " FROM validations" - " WHERE nonce=$1;"); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "auth_add_grant", - params); -} diff --git a/src/challengerdb/pg_auth_add_grant.h b/src/challengerdb/pg_auth_add_grant.h @@ -1,48 +0,0 @@ -/* - This file is part of Challenger - Copyright (C) 2023 Taler Systems SA - - Challenger is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - Challenger is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - Challenger; see the file COPYING. If not, see <http://www.gnu.org/licenses/> - */ -/** - * @file challengerdb/pg_auth_add_grant.h - * @brief implementation of the auth_add_grant function for Postgres - * @author Christian Grothoff - */ -#ifndef PG_AUTH_ADD_GRANT_H -#define PG_AUTH_ADD_GRANT_H - -#include <taler/taler_util.h> -#include <taler/taler_json_lib.h> -#include "challenger_database_plugin.h" - - -/** - * Add access @a grant to address under @a nonce. - * - * @param cls closure - * @param nonce validation process to grant access to - * @param grant grant token that grants access - * @param grant_expiration for how long should the grant be valid - * @param address_expiration for how long after validation do we consider addresses to be valid - * @return transaction status - */ -enum GNUNET_DB_QueryStatus -CH_PG_auth_add_grant ( - void *cls, - const struct CHALLENGER_ValidationNonceP *nonce, - const struct CHALLENGER_AccessTokenP *grant, - struct GNUNET_TIME_Relative grant_expiration, - struct GNUNET_TIME_Relative address_expiration); - - -#endif diff --git a/src/challengerdb/pg_token_add_grant.c b/src/challengerdb/pg_token_add_grant.c @@ -0,0 +1,62 @@ +/* + This file is part of Challenger + Copyright (C) 2023 Taler Systems SA + + Challenger is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Challenger is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + Challenger; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ +/** + * @file challengerdb/pg_token_add_grant.c + * @brief Implementation of the token_add_grant function for Postgres + * @author Christian Grothoff + */ +#include "platform.h" +#include <taler/taler_error_codes.h> +#include <taler/taler_dbevents.h> +#include <taler/taler_pq_lib.h> +#include "pg_token_add_grant.h" +#include "pg_helper.h" + + +enum GNUNET_DB_QueryStatus +CH_PG_token_add_grant ( + void *cls, + const struct CHALLENGER_ValidationNonceP *nonce, + const struct CHALLENGER_AccessTokenP *grant, + struct GNUNET_TIME_Relative grant_expiration, + struct GNUNET_TIME_Relative address_expiration) +{ + struct PostgresClosure *pg = cls; + struct GNUNET_TIME_Absolute ge + = GNUNET_TIME_relative_to_absolute (grant_expiration); + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (nonce), + GNUNET_PQ_query_param_auto_from_type (grant), + GNUNET_PQ_query_param_absolute_time (&ge), + GNUNET_PQ_query_param_relative_time (&address_expiration), + GNUNET_PQ_query_param_end + }; + + PREPARE (pg, + "token_add_grant", + "INSERT INTO grants" + " (access_token" + " ,address" + " ,grant_expiration_time" + " ,address_expiration_time" + ") SELECT" + " $2, address, $3, $4 + last_tx_time" + " FROM validations" + " WHERE nonce=$1;"); + return GNUNET_PQ_eval_prepared_non_select (pg->conn, + "token_add_grant", + params); +} diff --git a/src/challengerdb/pg_token_add_grant.h b/src/challengerdb/pg_token_add_grant.h @@ -0,0 +1,48 @@ +/* + This file is part of Challenger + Copyright (C) 2023 Taler Systems SA + + Challenger is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + Challenger is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + Challenger; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ +/** + * @file challengerdb/pg_token_add_grant.h + * @brief implementation of the token_add_grant function for Postgres + * @author Christian Grothoff + */ +#ifndef PG_AUTH_ADD_GRANT_H +#define PG_AUTH_ADD_GRANT_H + +#include <taler/taler_util.h> +#include <taler/taler_json_lib.h> +#include "challenger_database_plugin.h" + + +/** + * Add access @a grant to address under @a nonce. + * + * @param cls closure + * @param nonce validation process to grant access to + * @param grant grant token that grants access + * @param grant_expiration for how long should the grant be valid + * @param address_expiration for how long after validation do we consider addresses to be valid + * @return transaction status + */ +enum GNUNET_DB_QueryStatus +CH_PG_token_add_grant ( + void *cls, + const struct CHALLENGER_ValidationNonceP *nonce, + const struct CHALLENGER_AccessTokenP *grant, + struct GNUNET_TIME_Relative grant_expiration, + struct GNUNET_TIME_Relative address_expiration); + + +#endif diff --git a/src/challengerdb/plugin_challengerdb_postgres.c b/src/challengerdb/plugin_challengerdb_postgres.c @@ -29,7 +29,7 @@ #include "pg_client_add.h" #include "pg_client_delete.h" #include "pg_info_get_grant.h" -#include "pg_auth_add_grant.h" +#include "pg_token_add_grant.h" #include "pg_client_check.h" #include "pg_setup_nonce.h" #include "pg_login_start.h" @@ -403,8 +403,8 @@ libchallenger_plugin_db_postgres_init (void *cls) = &CH_PG_validation_get; plugin->info_get_grant = &CH_PG_info_get_grant; - plugin->auth_add_grant - = &CH_PG_auth_add_grant; + plugin->token_add_grant + = &CH_PG_token_add_grant; return plugin; } diff --git a/src/include/challenger_database_plugin.h b/src/include/challenger_database_plugin.h @@ -335,11 +335,11 @@ struct CHALLENGER_DatabasePlugin * @return transaction status */ enum GNUNET_DB_QueryStatus - (*auth_add_grant)(void *cls, - const struct CHALLENGER_ValidationNonceP *nonce, - const struct CHALLENGER_AccessTokenP *grant, - struct GNUNET_TIME_Relative grant_expiration, - struct GNUNET_TIME_Relative address_expiration); + (*token_add_grant)(void *cls, + const struct CHALLENGER_ValidationNonceP *nonce, + const struct CHALLENGER_AccessTokenP *grant, + struct GNUNET_TIME_Relative grant_expiration, + struct GNUNET_TIME_Relative address_expiration); /**