do_insert_token.c (3838B)
1 /* 2 This file is part of Challenger 3 Copyright (C) 2023 Taler Systems SA 4 5 Challenger is free software; you can redistribute it and/or modify it under the 6 terms of the GNU General Public License as published by the Free Software 7 Foundation; either version 3, or (at your option) any later version. 8 9 Challenger is distributed in the hope that it will be useful, but WITHOUT ANY 10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 13 You should have received a copy of the GNU General Public License along with 14 Challenger; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file src/challengerdb/do_insert_token.c 18 * @brief Implementation of the do_insert_token function for Postgres 19 * @author Christian Grothoff 20 */ 21 #include "platform.h" 22 #include <taler/taler_error_codes.h> 23 #include <taler/taler_dbevents.h> 24 #include <taler/taler_pq_lib.h> 25 #include "do_insert_token.h" 26 #include "pg_helper.h" 27 28 29 enum GNUNET_DB_QueryStatus 30 CHALLENGERDB_do_insert_token (struct CHALLENGERDB_PostgresContext *ctx, 31 const struct CHALLENGER_ValidationNonceP *nonce, 32 uint64_t client_id, 33 const struct CHALLENGER_AccessTokenP *token, 34 struct GNUNET_TIME_Relative token_expiration, 35 struct GNUNET_TIME_Relative address_expiration) 36 { 37 struct GNUNET_TIME_Absolute now 38 = GNUNET_TIME_absolute_get (); 39 struct GNUNET_TIME_Absolute ge 40 = GNUNET_TIME_relative_to_absolute (token_expiration); 41 struct GNUNET_TIME_Absolute ae 42 = GNUNET_TIME_relative_to_absolute (address_expiration); 43 struct GNUNET_PQ_QueryParam params[] = { 44 GNUNET_PQ_query_param_auto_from_type (nonce), 45 GNUNET_PQ_query_param_auto_from_type (token), 46 GNUNET_PQ_query_param_absolute_time (&ge), 47 GNUNET_PQ_query_param_absolute_time (&ae), 48 GNUNET_PQ_query_param_absolute_time (&now), 49 GNUNET_PQ_query_param_uint64 (&client_id), 50 GNUNET_PQ_query_param_end 51 }; 52 53 /* Redemption must be atomic and one-shot (RFC 6749 4.1.2): consume the 54 validation by deleting it as we mint the token, so the same authorization 55 code cannot be replayed to mint additional tokens. A replay finds no 56 matching validations row and thus inserts no token (NO_RESULTS), which the 57 caller maps to 'invalid_grant'. The 'auth_attempts_left < 0' guard is what 58 restricts us to validations that were actually *solved*: -1 is the sentinel 59 challenger_do_validate_and_solve_pin() writes once the user entered the 60 correct PIN. The 'address IS NOT NULL' guard additionally avoids violating 61 the tokens.address NOT NULL constraint. Expiration and client are checked 62 here as well and not merely by the get_validation_pkce() that preceded us: 63 the two run in separate transactions, so anything we do not repeat is a 64 TOCTOU window. */ 65 PREPARE (ctx, 66 "do_insert_token", 67 "WITH consumed AS (" 68 " DELETE FROM validations" 69 " WHERE nonce=$1" 70 " AND address IS NOT NULL" 71 " AND auth_attempts_left < 0" 72 " AND expiration_time > $5" 73 " AND client_serial_id=$6" 74 " RETURNING address" 75 ") INSERT INTO tokens" 76 " (access_token" 77 " ,address" 78 " ,token_expiration_time" 79 " ,address_expiration_time" 80 ") SELECT" 81 " $2" 82 " ,address" 83 " ,$3" 84 " ,$4" 85 " FROM consumed;"); 86 return GNUNET_PQ_eval_prepared_non_select (ctx->conn, 87 "do_insert_token", 88 params); 89 }