Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/modules/ROOT/nav.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* xref:requests_responses.adoc[]
* xref:cancellation.adoc[]
* xref:serialization.adoc[]
* xref:auth.adoc[]
* xref:logging.adoc[]
* xref:sentinel.adoc[]
* xref:benchmarks.adoc[]
Expand Down
49 changes: 49 additions & 0 deletions doc/modules/ROOT/pages/auth.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//
// Copyright (c) 2026 Marcelo Zimbres Silva (mzimbres@gmail.com),
// Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//

= Authentication

Boost.Redis supports connecting to servers that require authentication.
To achieve it, you must send a
https://redis.io/commands/hello/[HELLO] command with the appropriate
`AUTH` parameters during connection establishment. Boost.Redis allows you
to customize this handshake by using a _setup request_: a request that
is run automatically before any other request,
every time a physical connection to the server is established.

Configuration is done on xref:reference:boost/redis/config.adoc[`config`].
Set `use_setup` to `true` (required for backwards compatibility)
and build the desired setup request in
`config::setup`. By default, the library sends a plain `HELLO 3` (RESP3
without auth). To authenticate, clear the default setup and push a
`HELLO` command that includes your credentials:

[source,cpp]
----
config cfg;
cfg.use_setup = true;
cfg.setup.clear(); // Remove the default HELLO 3
cfg.setup.hello("my_username", "my_password");

co_await conn.async_run(cfg);
----

Authentication is just one use of this mechanism.
For example, to select a particular logical database (see the Redis
https://redis.io/commands/select/[SELECT] command), add a `SELECT`
command after the `HELLO`:

[source,cpp]
----
config cfg;
cfg.use_setup = true;
cfg.setup.push("SELECT", 42); // select the logical database 42 after the default HELLO 3

co_await conn.async_run(cfg);
----

47 changes: 43 additions & 4 deletions include/boost/redis/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ struct config {
*/
std::string unix_socket;

/** @brief Username used for authentication during connection establishment.
/** @brief (Deprecated) Username used for authentication during connection establishment.
*
* If @ref use_setup is false (the default), during connection establishment,
* authentication is performed by sending a `HELLO` command.
Expand All @@ -176,10 +176,20 @@ struct config {
*
* When using Sentinel, this setting applies to masters and replicas.
* Use @ref sentinel_config::setup to configure authorization for Sentinels.
*
* @par Deprecated
* This setting is deprecated and will be removed in a subsequent release.
* Please set @ref setup, instead:
*
* @code
* cfg.use_setup = true;
* cfg.setup.clear();
* cfg.setup.hello("my_username", "my_password");
* @endcode
*/
std::string username = "default";

/** @brief Password used for authentication during connection establishment.
/** @brief (Deprecated) Password used for authentication during connection establishment.
*
* If @ref use_setup is false (the default), during connection establishment,
* authentication is performed by sending a `HELLO` command.
Expand All @@ -191,28 +201,57 @@ struct config {
*
* When using Sentinel, this setting applies to masters and replicas.
* Use @ref sentinel_config::setup to configure authorization for Sentinels.
*
* @par Deprecated
* This setting is deprecated and will be removed in a subsequent release.
* Please set @ref setup, instead:
*
* @code
* cfg.use_setup = true;
* cfg.setup.clear();
* cfg.setup.hello("my_username", "my_password");
* @endcode
*/
std::string password;

/** @brief Client name parameter to use during connection establishment.
/** @brief (Deprecated) Client name parameter to use during connection establishment.
*
* If @ref use_setup is false (the default), during connection establishment,
* a `HELLO` command is sent. If this field is not empty, the `HELLO` command
* will contain a `SETNAME` subcommand containing this value.
*
* When using Sentinel, this setting applies to masters and replicas.
* Use @ref sentinel_config::setup to configure this value for Sentinels.
*
* @par Deprecated
* This setting is deprecated and will be removed in a subsequent release.
* Please set @ref setup, instead:
*
* @code
* cfg.use_setup = true;
* cfg.setup.clear();
* cfg.setup.hello_setname("my_client_name");
* @endcode
*/
std::string clientname = "Boost.Redis";

/** @brief Database index to pass to the `SELECT` command during connection establishment.
/** @brief (Deprecated) Database index to pass to the `SELECT` command during connection establishment.
*
* If @ref use_setup is false (the default), and this field is set to a
* non-empty optional, and its value is different than zero,
* a `SELECT` command will be issued during connection establishment to set the logical
* database index. By default, no `SELECT` command is sent.
*
* When using Sentinel, this setting applies to masters and replicas.
*
* @par Deprecated
* This setting is deprecated and will be removed in a subsequent release.
* Please set @ref setup, instead:
*
* @code
* cfg.use_setup = true;
* cfg.setup.push("SELECT", 4); // select database index 4
* @endcode
*/
std::optional<int> database_index = 0;

Expand Down
30 changes: 27 additions & 3 deletions include/boost/redis/impl/request.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,15 @@ auto has_response(std::string_view cmd) -> bool
request make_hello_request()
{
request req;
req.push("HELLO", "3");
req.hello();
return req;
}

} // namespace boost::redis::detail

void boost::redis::request::append(const request& other)
namespace boost::redis {

void request::append(const request& other)
{
// Remember the old payload size, to update offsets
std::size_t old_offset = payload_.size();
Expand All @@ -55,7 +57,7 @@ void boost::redis::request::append(const request& other)
}
}

void boost::redis::request::add_pubsub_arg(detail::pubsub_change_type type, std::string_view value)
void request::add_pubsub_arg(detail::pubsub_change_type type, std::string_view value)
{
// Add the argument
resp3::add_bulk(payload_, value);
Expand All @@ -65,3 +67,25 @@ void boost::redis::request::add_pubsub_arg(detail::pubsub_change_type type, std:
std::size_t offset = payload_.size() - value.size() - 2u;
pubsub_changes_.push_back({type, offset, value.size()});
}

void request::hello() { push("HELLO", "3"); }

void request::hello(std::string_view username, std::string_view password)
{
push("HELLO", "3", "AUTH", username, password);
}

void request::hello_setname(std::string_view client_name)
{
push("HELLO", "3", "SETNAME", client_name);
}

void request::hello_setname(
std::string_view username,
std::string_view password,
std::string_view client_name)
{
push("HELLO", "3", "AUTH", username, password, "SETNAME", client_name);
}

} // namespace boost::redis
8 changes: 4 additions & 4 deletions include/boost/redis/impl/setup_request_utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,13 @@ inline void compose_setup_request(

// Gather everything we can in a HELLO command
if (send_auth && send_setname)
req.push("HELLO", "3", "AUTH", cfg.username, cfg.password, "SETNAME", cfg.clientname);
req.hello_setname(cfg.username, cfg.password, cfg.clientname);
else if (send_auth)
req.push("HELLO", "3", "AUTH", cfg.username, cfg.password);
req.hello(cfg.username, cfg.password);
else if (send_setname)
req.push("HELLO", "3", "SETNAME", cfg.clientname);
req.hello_setname(cfg.clientname);
else
req.push("HELLO", "3");
req.hello();

// SELECT is independent of HELLO
if (cfg.database_index && cfg.database_index.value() != 0)
Expand Down
45 changes: 45 additions & 0 deletions include/boost/redis/request.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -724,6 +724,51 @@ class request {
patterns_end);
}

/** @brief Appends a HELLO 3 command to the end of the request.
*
* Equivalent to adding the Redis command `HELLO 3`.
*/
void hello();

/** @brief Appends a HELLO 3 command with AUTH to the end of the request.
*
* Equivalent to the adding the following Redis command:
* @code
* HELLO 3 AUTH <username> <password>
* @endcode
*
* @param username The ACL username.
* @param password The password for the user.
*/
void hello(std::string_view username, std::string_view password);

/** @brief Appends a HELLO 3 command with SETNAME to the end of the request.
*
* Equivalent to adding the following Redis command:
* @code
* HELLO 3 SETNAME <client_name>
* @endcode
*
* @param client_name The client name (visible in CLIENT LIST).
*/
void hello_setname(std::string_view client_name);

/** @brief Appends a HELLO 3 command with AUTH and SETNAME to the end of the request.
*
* Equivalent to adding the following Redis command:
* @code
* HELLO 3 AUTH <username> <password> SETNAME <client_name>
* @endcode
*
* @param username The ACL username.
* @param password The password for the user.
* @param client_name The client name (visible in CLIENT LIST).
*/
void hello_setname(
std::string_view username,
std::string_view password,
std::string_view client_name);

private:
void check_cmd(std::string_view cmd)
{
Expand Down
41 changes: 41 additions & 0 deletions test/test_request.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,42 @@ void test_mix_pubsub_regular()
check_pubsub_changes(req, expected_changes);
}

// --- hello ---
void test_hello()
{
request req;
req.hello();
BOOST_TEST_EQ(req.payload(), "*2\r\n$5\r\nHELLO\r\n$1\r\n3\r\n");
}

void test_hello_auth()
{
request req;
req.hello("user", "pass");
BOOST_TEST_EQ(
req.payload(),
"*5\r\n$5\r\nHELLO\r\n$1\r\n3\r\n$4\r\nAUTH\r\n$4\r\nuser\r\n$4\r\npass\r\n");
}

void test_hello_setname()
{
request req;
req.hello_setname("myclient");
BOOST_TEST_EQ(
req.payload(),
"*4\r\n$5\r\nHELLO\r\n$1\r\n3\r\n$7\r\nSETNAME\r\n$8\r\nmyclient\r\n");
}

void test_hello_setname_auth()
{
request req;
req.hello_setname("user", "pass", "myclient");
BOOST_TEST_EQ(
req.payload(),
"*7\r\n$5\r\nHELLO\r\n$1\r\n3\r\n$4\r\nAUTH\r\n$4\r\nuser\r\n$4\r\npass\r\n"
"$7\r\nSETNAME\r\n$8\r\nmyclient\r\n");
}

// --- append ---
void test_append()
{
Expand Down Expand Up @@ -703,6 +739,11 @@ int main()

test_mix_pubsub_regular();

test_hello();
test_hello_auth();
test_hello_setname();
test_hello_setname_auth();

test_append();
test_append_no_response();
test_append_flags();
Expand Down