challenger

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

commit 206ebd255645426585bf21d005306bfc1a88430e
parent f2f46a1df0cdb5dfb5beaba68296c309fde6a85d
Author: Christian Grothoff <christian@grothoff.org>
Date:   Mon,  8 May 2023 20:47:51 +0200

implement OAuth2 error codes (rfc6749 - section-5.2) as found by schanzen

Diffstat:
Msrc/challenger/challenger-httpd_common.c | 33+++++++++++++++++++++++++++++++++
Msrc/challenger/challenger-httpd_common.h | 19+++++++++++++++++++
Msrc/challenger/challenger-httpd_token.c | 163++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
3 files changed, 147 insertions(+), 68 deletions(-)

diff --git a/src/challenger/challenger-httpd_common.c b/src/challenger/challenger-httpd_common.c @@ -118,3 +118,36 @@ CH_code_to_nonce (const char *code, } return GNUNET_OK; } + + +MHD_RESULT +TALER_MHD_reply_with_oauth_error ( + struct MHD_Connection *connection, + unsigned int http_status, + const char *oauth_error, + enum TALER_ErrorCode ec, + const char *detail) +{ + struct MHD_Response *resp; + MHD_RESULT mret; + + resp = TALER_MHD_make_json ( + GNUNET_JSON_PACK ( + TALER_MHD_PACK_EC (ec), + GNUNET_JSON_pack_string ("error", + oauth_error), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_string ("detail", + detail)))); + if (MHD_HTTP_UNAUTHORIZED == http_status) + GNUNET_break (MHD_YES == + MHD_add_response_header ( + resp, + "WWW-Authenticate", + "Bearer, error=\"invalid_token\"")); + mret = MHD_queue_response (connection, + http_status, + resp); + MHD_destroy_response (resp); + return mret; +} diff --git a/src/challenger/challenger-httpd_common.h b/src/challenger/challenger-httpd_common.h @@ -66,4 +66,23 @@ CH_code_to_nonce (const char *code, struct CHALLENGER_ValidationNonceP *nonce); +/** + * Send a OAuth 2.0 response indicating an error following + * section 5.2 of RFC 6749. + * + * @param connection the MHD connection to use + * @param ec error code uniquely identifying the error + * @param oauth_error error as of the OAuth 2.0 protocol + * @param http_status HTTP status code to use + * @param detail additional optional detail about the error + * @return a MHD result code + */ +MHD_RESULT +TALER_MHD_reply_with_oauth_error (struct MHD_Connection *connection, + unsigned int http_status, + const char *oauth_error, + enum TALER_ErrorCode ec, + const char *detail); + + #endif diff --git a/src/challenger/challenger-httpd_token.c b/src/challenger/challenger-httpd_token.c @@ -224,43 +224,53 @@ CH_handler_token (struct CH_HandlerContext *hc, "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"); + return TALER_MHD_reply_with_oauth_error ( + hc->connection, + MHD_HTTP_BAD_REQUEST, + "unsupported_grant_type", + 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"); + return TALER_MHD_reply_with_oauth_error ( + hc->connection, + MHD_HTTP_BAD_REQUEST, + "invalid_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"); + return TALER_MHD_reply_with_oauth_error ( + hc->connection, + MHD_HTTP_BAD_REQUEST, + "invalid_client", + 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"); + return TALER_MHD_reply_with_oauth_error ( + hc->connection, + MHD_HTTP_BAD_REQUEST, + "invalid_client", + 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"); + return TALER_MHD_reply_with_oauth_error ( + hc->connection, + MHD_HTTP_BAD_REQUEST, + "invalid_request", + TALER_EC_GENERIC_PARAMETER_MISSING, + "redirect_uri"); } /* Check this client is authorized to access the service */ @@ -276,10 +286,12 @@ CH_handler_token (struct CH_HandlerContext *hc, &dummy)) { GNUNET_break_op (0); - return TALER_MHD_reply_with_error (hc->connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "client_id"); + return TALER_MHD_reply_with_oauth_error ( + hc->connection, + MHD_HTTP_BAD_REQUEST, + "invalid_client", + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "client_id"); } qs = CH_db->client_check (CH_db->cls, @@ -291,19 +303,22 @@ CH_handler_token (struct CH_HandlerContext *hc, { 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"); + 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); + return TALER_MHD_reply_with_oauth_error ( + hc->connection, + MHD_HTTP_UNAUTHORIZED, + "invalid_client", + TALER_EC_CHALLENGER_GENERIC_CLIENT_UNKNOWN, + NULL); case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: break; } @@ -312,10 +327,12 @@ CH_handler_token (struct CH_HandlerContext *hc, 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); + return TALER_MHD_reply_with_oauth_error ( + hc->connection, + MHD_HTTP_UNAUTHORIZED, + "invalid_client", + TALER_EC_CHALLENGER_GENERIC_CLIENT_FORBIDDEN_BAD_REDIRECT_URI, + NULL); } GNUNET_free (client_url); } @@ -325,10 +342,12 @@ CH_handler_token (struct CH_HandlerContext *hc, &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); + return TALER_MHD_reply_with_oauth_error ( + hc->connection, + MHD_HTTP_UNAUTHORIZED, + "invalid_grant", + TALER_EC_CHALLENGER_CLIENT_FORBIDDEN_BAD_CODE, + NULL); } /* Check code is valid */ @@ -352,19 +371,22 @@ CH_handler_token (struct CH_HandlerContext *hc, { 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"); + 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"); + return TALER_MHD_reply_with_oauth_error ( + hc->connection, + MHD_HTTP_UNAUTHORIZED, + "invalid_grant", + TALER_EC_CHALLENGER_GENERIC_VALIDATION_UNKNOWN, + "validation_get"); case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: break; } @@ -383,46 +405,51 @@ CH_handler_token (struct CH_HandlerContext *hc, { 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"); + return TALER_MHD_reply_with_oauth_error ( + hc->connection, + MHD_HTTP_UNAUTHORIZED, + "invalid_grant", + TALER_EC_CHALLENGER_CLIENT_FORBIDDEN_BAD_CODE, + "code"); } GNUNET_free (code); } { - struct CHALLENGER_AccessTokenP grant; + struct CHALLENGER_AccessTokenP token; enum GNUNET_DB_QueryStatus qs; /* FIXME: do not hard-code 1h? */ - struct GNUNET_TIME_Relative grant_expiration + struct GNUNET_TIME_Relative token_expiration = GNUNET_TIME_UNIT_HOURS; GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, - &grant, - sizeof (grant)); + &token, + sizeof (token)); qs = CH_db->token_add_grant (CH_db->cls, &bc->nonce, - &grant, - grant_expiration, + &token, + token_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"); + 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"); + return TALER_MHD_reply_with_oauth_error ( + hc->connection, + MHD_HTTP_UNAUTHORIZED, + "invalid_grant", + TALER_EC_CHALLENGER_GRANT_UNKNOWN, + "token_add_grant"); case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: break; } @@ -432,11 +459,11 @@ CH_handler_token (struct CH_HandlerContext *hc, hc->connection, MHD_HTTP_OK, GNUNET_JSON_pack_data_auto ("access_token", - &grant), + &token), GNUNET_JSON_pack_string ("token_type", "Bearer"), GNUNET_JSON_pack_uint64 ("expires_in", - grant_expiration.rel_value_us + token_expiration.rel_value_us / GNUNET_TIME_UNIT_SECONDS.rel_value_us)); } }