diff --git a/doc/Jamfile b/doc/Jamfile
index 042a45d..1337b4a 100644
--- a/doc/Jamfile
+++ b/doc/Jamfile
@@ -34,6 +34,7 @@ make xml/index.xml
../include/async_mqtt5/error.hpp
../include/async_mqtt5/types.hpp
../include/async_mqtt5/mqtt_client.hpp
+ ../include/async_mqtt5/detail/any_authenticator.hpp
:
@call-doxygen
;
diff --git a/doc/qbk/00_main.qbk b/doc/qbk/00_main.qbk
index b6d0391..f1e1125 100644
--- a/doc/qbk/00_main.qbk
+++ b/doc/qbk/00_main.qbk
@@ -30,9 +30,10 @@
[template mqttlink[id text][@https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc[id] [text]]]
[def __CompletionToken__ [@boost:/doc/html/boost_asio/reference/asynchronous_operations.html#boost_asio.reference.asynchronous_operations.completion_tokens_and_handlers CompletionToken]]
-[def __ExecutionContext__ [reflink2 ExecutionContext ExecutionContext]]
-[def __StreamType__ [reflink2 StreamType StreamType]]
-[def __TlsContext__ [reflink2 TlsContext TlsContext]]
+[def __ExecutionContext__ [reflink ExecutionContext]]
+[def __StreamType__ [reflink StreamType]]
+[def __TlsContext__ [reflink TlsContext]]
+[def __is_authenticator__ [reflink is_authenticator]]
[def __Boost__ [@https://www.boost.org/ Boost]]
[def __Asio__ [@boost:/libs/asio/index.html Boost.Asio]]
@@ -55,6 +56,7 @@
[def __QOS__ [mqttlink 3901234 `QoS`]]
[def __RETAIN__ [mqttlink 3901104 `RETAIN`]]
[def __SUBSCRIBE_OPTIONS__ [mqttlink 3901169 `Subscribe Options`]]
+[def __ENHANCED_AUTH__ [mqttlink 3901256 `Enhanced Authentication`]]
[def __CONNECT__ [mqttlink 3901033 `CONNECT`]]
[def __CONNACK__ [mqttlink 3901074 `CONNACK`]]
diff --git a/doc/qbk/reference/concepts/is_authenticator.qbk b/doc/qbk/reference/concepts/is_authenticator.qbk
new file mode 100644
index 0000000..7b1e652
--- /dev/null
+++ b/doc/qbk/reference/concepts/is_authenticator.qbk
@@ -0,0 +1,25 @@
+[/
+ Copyright (c) 2023 Mireo
+
+ 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)
+]
+
+[section:is_authenticator is_authenticator concept]
+
+`is_authenticator` represents authenticator object that needs to have following functions:
+
+ void async_auth(
+ ``[reflink2 auth_step_e async_mqtt5::auth_step_e]`` auth_step, // authentication stage
+ std::string server_data, // server authentication data
+ ``[asioreflink any_completion_handler any_completion_handler]``<
+ void(
+ __ERROR_CODE__ ec, // non-trivial error code aborts authentication
+ std::string auth_data // client authentication data
+ )
+ >
+ );
+ std::string_view method(); // returns authentication method
+
+
+[endsect]
diff --git a/doc/qbk/reference/quickref.xml b/doc/qbk/reference/quickref.xml
index e1c18c2..2836112 100644
--- a/doc/qbk/reference/quickref.xml
+++ b/doc/qbk/reference/quickref.xml
@@ -27,6 +27,7 @@
ExecutionContext
StreamType
TlsContext
+ is_authenticator
@@ -36,6 +37,7 @@
Enumerations
+ auth_step_e
client_error
disconnect_rc_e
qos_e
diff --git a/doc/reference.dox b/doc/reference.dox
index a524653..4f090f8 100644
--- a/doc/reference.dox
+++ b/doc/reference.dox
@@ -71,7 +71,8 @@ WARN_LOGFILE =
#---------------------------------------------------------------------------
INPUT = ../include/async_mqtt5/error.hpp \
../include/async_mqtt5/types.hpp \
- ../include/async_mqtt5/mqtt_client.hpp
+ ../include/async_mqtt5/mqtt_client.hpp \
+ ../include/async_mqtt5/detail/any_authenticator.hpp
FILE_PATTERNS =
RECURSIVE = NO
EXCLUDE =
diff --git a/doc/reference.xsl b/doc/reference.xsl
index 3ea5940..dad8434 100644
--- a/doc/reference.xsl
+++ b/doc/reference.xsl
@@ -40,6 +40,7 @@
[include concepts/ExecutionContext.qbk]
[include concepts/StreamType.qbk]
[include concepts/TlsContext.qbk]
+[include concepts/is_authenticator.qbk]
[include reason_codes/Reason_codes.qbk]
[include properties/will_props.qbk]
[include properties/connect_props.qbk]
@@ -1481,6 +1482,7 @@
ExecutionContext
StreamType
TlsContext
+ is_authenticator
@@ -1531,7 +1533,8 @@
+ or contains(type, 'TlsContext') or contains(type, 'StreamType')
+ or contains(type, 'is_authenticator')">
diff --git a/include/async_mqtt5/detail/any_authenticator.hpp b/include/async_mqtt5/detail/any_authenticator.hpp
new file mode 100644
index 0000000..0774d4e
--- /dev/null
+++ b/include/async_mqtt5/detail/any_authenticator.hpp
@@ -0,0 +1,108 @@
+#ifndef ASYNC_MQTT5_ANY_AUTHENTICATOR
+#define ASYNC_MQTT5_ANY_AUTHENTICATOR
+
+#include
+#include
+
+#include
+
+namespace async_mqtt5 {
+
+namespace asio = boost::asio;
+using error_code = boost::system::error_code;
+
+namespace detail {
+
+using auth_handler_type = asio::any_completion_handler<
+ void(error_code ec, std::string auth_data)
+>;
+
+template
+concept is_authenticator = requires (T a) {
+ {
+ a.async_auth(auth_step_e {}, std::string {}, auth_handler_type {})
+ } -> std::same_as;
+ { a.method() } -> std::same_as;
+};
+
+class auth_fun_base {
+ using auth_func = void(*)(
+ auth_step_e, std::string, auth_handler_type, auth_fun_base*
+ );
+ auth_func _auth_func;
+
+public:
+ auth_fun_base(auth_func f) : _auth_func(f) {}
+ ~auth_fun_base() = default;
+
+ void async_auth(
+ auth_step_e step, std::string data, auth_handler_type auth_handler
+ ) {
+ _auth_func(step, std::move(data), std::move(auth_handler), this);
+ }
+};
+
+template
+class auth_fun : public auth_fun_base {
+ Authenticator _authenticator;
+
+public:
+ auth_fun(Authenticator authenticator) :
+ auth_fun_base(&async_auth),
+ _authenticator(std::forward(authenticator))
+ {}
+
+ static void async_auth(
+ auth_step_e step, std::string data, auth_handler_type auth_handler,
+ auth_fun_base* base_ptr
+ ) {
+ auto auth_fun_ptr = static_cast(base_ptr);
+ auth_fun_ptr->_authenticator.async_auth(
+ step, std::move(data), std::move(auth_handler)
+ );
+ }
+};
+
+} // end namespace detail
+
+class any_authenticator {
+ std::string _method;
+ std::unique_ptr _auth_fun;
+
+public:
+ any_authenticator() = default;
+
+ template
+ any_authenticator(Authenticator&& a) :
+ _method(a.method()),
+ _auth_fun(
+ new detail::auth_fun(
+ std::forward(a)
+ )
+ )
+ {}
+
+ std::string_view method() const {
+ return _method;
+ }
+
+ template
+ decltype(auto) async_auth(
+ auth_step_e step, std::string data,
+ CompletionToken&& token
+ ) {
+ using Signature = void(error_code, std::string);
+
+ auto initiate = [this](auto handler, auth_step_e step, std::string data) {
+ _auth_fun->async_auth(step, std::move(data), std::move(handler));
+ };
+
+ return asio::async_initiate(
+ initiate, token, step, std::move(data)
+ );
+ }
+};
+
+} // end namespace async_mqtt5
+
+#endif // !ASYNC_MQTT5_ANY_AUTHENTICATOR
diff --git a/include/async_mqtt5/detail/internal_types.hpp b/include/async_mqtt5/detail/internal_types.hpp
index 9355601..5406a00 100644
--- a/include/async_mqtt5/detail/internal_types.hpp
+++ b/include/async_mqtt5/detail/internal_types.hpp
@@ -4,6 +4,8 @@
#include
#include
+#include
+
#include
#include
@@ -38,6 +40,7 @@ struct mqtt_context {
std::optional will;
connect_props co_props;
connack_props ca_props;
+ any_authenticator authenticator;
};
struct disconnect_context {
diff --git a/include/async_mqtt5/error.hpp b/include/async_mqtt5/error.hpp
index bd6b9a8..306bb88 100644
--- a/include/async_mqtt5/error.hpp
+++ b/include/async_mqtt5/error.hpp
@@ -486,6 +486,16 @@ requires (cat == connack) {
return std::make_pair(valid_codes, len);
}
+template
+inline std::pair valid_codes()
+requires (cat == auth) {
+ static reason_code valid_codes[] = {
+ success, continue_authentication
+ };
+ static size_t len = sizeof(valid_codes) / sizeof(reason_code);
+ return std::make_pair(valid_codes, len);
+}
+
template
inline std::pair valid_codes()
requires (cat == puback || cat == pubrec) {
diff --git a/include/async_mqtt5/impl/assemble_op.hpp b/include/async_mqtt5/impl/assemble_op.hpp
index 310a0fb..4f4e892 100644
--- a/include/async_mqtt5/impl/assemble_op.hpp
+++ b/include/async_mqtt5/impl/assemble_op.hpp
@@ -95,6 +95,9 @@ public:
};
if (cc(error_code {}, 0) == 0 && _data_span.size()) {
+ /* TODO clear read buffer on reconnect
+ * OR use dispatch instead of post here
+ */
return asio::post(
asio::prepend(
std::move(*this), on_read {}, error_code {},
@@ -128,16 +131,16 @@ public:
}
if (ec)
- return complete(ec, 0, 0, {}, {});
+ return complete(ec, 0, {}, {});
_data_span.expand_suffix(bytes_read);
assert(_data_span.size());
- auto control_code = uint8_t(*_data_span.first());
+ auto control_byte = uint8_t(*_data_span.first());
- if ((control_code & 0b11110000) == 0)
+ if ((control_byte & 0b11110000) == 0)
// close the connection, cancel
- return complete(client::error::malformed_packet, 0, 0, {}, {});
+ return complete(client::error::malformed_packet, 0, {}, {});
auto first = _data_span.first() + 1;
auto varlen = decoders::type_parse(
@@ -147,12 +150,12 @@ public:
if (!varlen) {
if (_data_span.size() < 5)
return perform(wait_for, asio::transfer_at_least(1));
- return complete(client::error::malformed_packet, 0, 0, {}, {});
+ return complete(client::error::malformed_packet, 0, {}, {});
}
// TODO: respect max packet size which could be dinamically set by the broker
if (*varlen > max_packet_size - std::distance(_data_span.first(), first))
- return complete(client::error::malformed_packet, 0, 0, {}, {});
+ return complete(client::error::malformed_packet, 0, {}, {});
if (std::distance(first, _data_span.last()) < *varlen)
return perform(wait_for, asio::transfer_at_least(1));
@@ -161,7 +164,7 @@ public:
std::distance(_data_span.first(), first) + *varlen
);
- dispatch(wait_for, control_code, first, first + *varlen);
+ dispatch(wait_for, control_byte, first, first + *varlen);
}
private:
@@ -179,51 +182,39 @@ private:
return res == 0b00000000;
}
- static bool contains_packet_id(control_code_e code) {
- using enum control_code_e;
-
- return code == puback || code == pubrec
- || code == pubrel || code == pubcomp
- || code == subscribe || code == suback
- || code == unsubscribe || code == unsuback;
- }
-
void dispatch(
duration wait_for,
- uint8_t control_code, byte_citer first, byte_citer last
+ uint8_t control_byte, byte_citer first, byte_citer last
) {
using namespace decoders;
using enum control_code_e;
- if (!valid_header(control_code))
- return complete(client::error::malformed_packet, 0, 0, {}, {});
+ if (!valid_header(control_byte))
+ return complete(client::error::malformed_packet, 0, {}, {});
- auto code = control_code_e(control_code & 0b11110000);
+ auto code = control_code_e(control_byte & 0b11110000);
if (code == pingresp)
return perform(wait_for, asio::transfer_at_least(0));
- uint16_t packet_id = 0;
- if (contains_packet_id(code))
- packet_id = decoders::decode_packet_id(first).value();
-
bool is_reply = code != publish && code != auth && code != disconnect;
if (is_reply) {
+ auto packet_id = decoders::decode_packet_id(first).value();
_svc._replies.dispatch(error_code {}, code, packet_id, first, last);
return perform(wait_for, asio::transfer_at_least(0));
}
- complete(error_code {}, packet_id, control_code, first, last);
+ complete(error_code {}, control_byte, first, last);
}
void complete(
- error_code ec, uint16_t packet_id, uint8_t control_code,
+ error_code ec, uint8_t control_code,
byte_citer first, byte_citer last
) {
asio::dispatch(
get_executor(),
asio::prepend(
- std::move(_handler), ec, packet_id, control_code,
+ std::move(_handler), ec, control_code,
first, last
)
);
diff --git a/include/async_mqtt5/impl/async_sender.hpp b/include/async_mqtt5/impl/async_sender.hpp
index 2939ace..1a5360c 100644
--- a/include/async_mqtt5/impl/async_sender.hpp
+++ b/include/async_mqtt5/impl/async_sender.hpp
@@ -30,7 +30,7 @@ public:
_handler(std::move(handler)) {}
static serial_num_t next_serial_num(serial_num_t last) {
- return ++last;
+ return last + 1;
}
asio::const_buffer buffer() const { return _buffer; }
diff --git a/include/async_mqtt5/impl/client_service.hpp b/include/async_mqtt5/impl/client_service.hpp
index 0345d5e..6ed9949 100644
--- a/include/async_mqtt5/impl/client_service.hpp
+++ b/include/async_mqtt5/impl/client_service.hpp
@@ -57,6 +57,13 @@ public:
std::move(username), std::move(password)
};
}
+
+ template
+ void authenticator(Authenticator&& authenticator) {
+ _mqtt_context.authenticator = any_authenticator(
+ std::forward(authenticator)
+ );
+ }
};
template
@@ -88,6 +95,13 @@ public:
std::move(username), std::move(password)
};
}
+
+ template
+ void authenticator(Authenticator&& authenticator) {
+ _mqtt_context.authenticator = any_authenticator(
+ std::forward(authenticator)
+ );
+ }
};
template <
@@ -177,6 +191,13 @@ public:
_stream.brokers(std::move(hosts), default_port);
}
+ template
+ void authenticator(Authenticator&& authenticator) {
+ _stream_context.authenticator(
+ std::forward(authenticator)
+ );
+ }
+
template
auto connack_prop(Prop p) {
return _stream_context.connack_prop(p);
@@ -243,10 +264,10 @@ public:
}.perform(wait_for, asio::transfer_at_least(0));
};
- using signature = void (
- error_code, uint16_t, uint8_t, byte_citer, byte_citer
+ using Signature = void (
+ error_code, uint8_t, byte_citer, byte_citer
);
- return asio::async_initiate (
+ return asio::async_initiate (
std::move(initiation), token, wait_for
);
}
diff --git a/include/async_mqtt5/impl/connect_op.hpp b/include/async_mqtt5/impl/connect_op.hpp
index 692b26f..c379d2b 100644
--- a/include/async_mqtt5/impl/connect_op.hpp
+++ b/include/async_mqtt5/impl/connect_op.hpp
@@ -28,12 +28,18 @@ template <
typename Stream, typename Handler
>
class connect_op {
+ static constexpr size_t min_packet_sz = 5;
+
struct on_connect {};
struct on_tls_handshake {};
struct on_ws_handshake {};
struct on_send_connect {};
struct on_fixed_header {};
- struct on_read_connack {};
+ struct on_read_packet {};
+ struct on_init_auth_data {};
+ struct on_auth_data {};
+ struct on_send_auth {};
+ struct on_complete_auth {};
Stream& _stream;
mqtt_context& _ctx;
@@ -152,13 +158,30 @@ public:
);
}
else
- send_connect();
+ (*this)(on_ws_handshake {}, error_code {});
}
void operator()(on_ws_handshake, error_code ec) {
if (ec)
return complete(ec);
+ auto auth_method = _ctx.authenticator.method();
+ if (!auth_method.empty()) {
+ _ctx.co_props[prop::authentication_method] = auth_method;
+ return _ctx.authenticator.async_auth(
+ auth_step_e::client_initial, "",
+ asio::prepend(std::move(*this), on_init_auth_data {})
+ );
+ }
+
+ send_connect();
+ }
+
+ void operator()(on_init_auth_data, error_code ec, std::string data) {
+ if (ec)
+ return complete(asio::error::try_again);
+
+ _ctx.co_props[prop::authentication_data] = std::move(data);
send_connect();
}
@@ -186,10 +209,9 @@ public:
if (ec)
return complete(ec);
- constexpr size_t min_connack_sz = 5;
- _buffer_ptr = std::make_unique(min_connack_sz, 0);
+ _buffer_ptr = std::make_unique(min_packet_sz, 0);
- auto buff = asio::buffer(_buffer_ptr->data(), min_connack_sz);
+ auto buff = asio::buffer(_buffer_ptr->data(), min_packet_sz);
asio::async_read(
_stream, buff,
asio::prepend(std::move(*this), on_fixed_header {})
@@ -202,8 +224,9 @@ public:
if (ec)
return complete(ec);
- auto control_byte = (*_buffer_ptr)[0];
- if (control_byte != 0b00100000)
+ auto code = control_code_e((*_buffer_ptr)[0] & 0b11110000);
+
+ if (code != control_code_e::auth && code != control_code_e::connack)
return complete(asio::error::try_again);
auto varlen_ptr = _buffer_ptr->cbegin() + 1;
@@ -217,7 +240,8 @@ public:
auto remain_len = *varlen -
std::distance(varlen_ptr, _buffer_ptr->cbegin() + num_read);
- _buffer_ptr->resize(_buffer_ptr->size() + remain_len);
+ if (num_read + remain_len > _buffer_ptr->size())
+ _buffer_ptr->resize(num_read + remain_len);
auto buff = asio::buffer(_buffer_ptr->data() + num_read, remain_len);
auto first = _buffer_ptr->cbegin() + varlen_sz + 1;
@@ -227,21 +251,33 @@ public:
_stream, buff,
asio::prepend(
asio::append(
- std::move(*this), uint8_t(control_byte), first, last
- ), on_read_connack {}
+ std::move(*this), code, first, last
+ ), on_read_packet {}
)
);
}
void operator()(
- on_read_connack, error_code ec, size_t, uint8_t control_code,
+ on_read_packet, error_code ec, size_t, control_code_e code,
byte_citer first, byte_citer last
) {
if (ec)
return complete(ec);
+ if (code == control_code_e::connack)
+ return on_connack(first, last);
+
+ if (!_ctx.co_props[prop::authentication_method].has_value())
+ return complete(client::error::malformed_packet);
+
+ on_auth(first, last);
+ }
+
+ void on_connack(byte_citer first, byte_citer last) {
auto packet_length = std::distance(first, last);
auto rv = decoders::decode_connack(packet_length, first);
+ if (!rv.has_value())
+ return complete(client::error::malformed_packet);
const auto& [session_present, reason_code, ca_props] = *rv;
_ctx.ca_props = ca_props;
@@ -257,7 +293,84 @@ public:
if (!rc.has_value()) // reason code not allowed in CONNACK
return complete(client::error::malformed_packet);
- complete(to_asio_error(*rc));
+ auto ec = to_asio_error(*rc);
+ if (ec)
+ return complete(ec);
+
+ if (_ctx.co_props[prop::authentication_method].has_value())
+ return _ctx.authenticator.async_auth(
+ auth_step_e::server_final,
+ ca_props[prop::authentication_data].value_or(""),
+ asio::prepend(std::move(*this), on_complete_auth {})
+ );
+
+ complete(error_code {});
+ }
+
+ void on_auth(byte_citer first, byte_citer last) {
+ auto packet_length = std::distance(first, last);
+ auto rv = decoders::decode_auth(packet_length, first);
+ if (!rv.has_value())
+ return complete(client::error::malformed_packet);
+ const auto& [reason_code, auth_props] = *rv;
+
+ auto rc = to_reason_code(reason_code);
+ if (
+ !rc.has_value() ||
+ auth_props[prop::authentication_method]
+ != _ctx.co_props[prop::authentication_method]
+ )
+ return complete(client::error::malformed_packet);
+
+ _ctx.authenticator.async_auth(
+ auth_step_e::server_challenge,
+ auth_props[prop::authentication_data].value_or(""),
+ asio::prepend(std::move(*this), on_auth_data {})
+ );
+ }
+
+ void operator()(on_auth_data, error_code ec, std::string data) {
+ if (ec)
+ return complete(asio::error::try_again);
+
+ auth_props props;
+ props[prop::authentication_method] =
+ _ctx.co_props[prop::authentication_method];
+ props[prop::authentication_data] = std::move(data);
+
+ auto packet = control_packet::of(
+ no_pid, get_allocator(),
+ encoders::encode_auth,
+ reason_codes::continue_authentication.value(), props
+ );
+
+ const auto& wire_data = packet.wire_data();
+
+ async_mqtt5::detail::async_write(
+ _stream, asio::buffer(wire_data),
+ asio::consign(
+ asio::prepend(std::move(*this), on_send_auth{}),
+ std::move(packet)
+ )
+ );
+ }
+
+ void operator()(on_send_auth, error_code ec, size_t) {
+ if (ec)
+ return complete(ec);
+
+ auto buff = asio::buffer(_buffer_ptr->data(), min_packet_sz);
+ asio::async_read(
+ _stream, buff,
+ asio::prepend(std::move(*this), on_fixed_header {})
+ );
+ }
+
+ void operator()(on_complete_auth, error_code ec, std::string) {
+ if (ec)
+ return complete(asio::error::try_again);
+
+ complete(error_code {});
}
private:
diff --git a/include/async_mqtt5/impl/read_message_op.hpp b/include/async_mqtt5/impl/read_message_op.hpp
index fddb544..3fccb2d 100644
--- a/include/async_mqtt5/impl/read_message_op.hpp
+++ b/include/async_mqtt5/impl/read_message_op.hpp
@@ -55,7 +55,7 @@ public:
void operator()(
on_message, error_code ec,
- uint16_t packet_id, uint8_t control_code,
+ uint8_t control_code,
byte_citer first, byte_citer last
) {
if (ec == client::error::malformed_packet)
@@ -69,19 +69,17 @@ public:
)
return;
- dispatch(ec, packet_id, control_code, first, last);
+ dispatch(control_code, first, last);
}
void operator()(on_disconnect, error_code ec) {
- if (!ec || ec == asio::error::try_again)
+ if (!ec)
perform();
}
private:
-
- // TODO: ec & packet_id are not used here
void dispatch(
- error_code ec, uint16_t packet_id, uint8_t control_byte,
+ uint8_t control_byte,
byte_citer first, byte_citer last
) {
using enum control_code_e;
diff --git a/include/async_mqtt5/impl/sentry_op.hpp b/include/async_mqtt5/impl/sentry_op.hpp
index 9881d2f..94a307f 100644
--- a/include/async_mqtt5/impl/sentry_op.hpp
+++ b/include/async_mqtt5/impl/sentry_op.hpp
@@ -83,7 +83,7 @@ public:
void operator()(on_disconnect, error_code ec) {
get_cancellation_slot().clear();
- if (!ec || ec == asio::error::try_again)
+ if (!ec)
perform();
}
};
diff --git a/include/async_mqtt5/mqtt_client.hpp b/include/async_mqtt5/mqtt_client.hpp
index 1c926b4..dc576b3 100644
--- a/include/async_mqtt5/mqtt_client.hpp
+++ b/include/async_mqtt5/mqtt_client.hpp
@@ -55,7 +55,7 @@ public:
explicit mqtt_client(
const executor_type& ex,
const std::string& cnf,
- tls_context_type tls_context = {}
+ TlsContext tls_context = {}
) :
_svc_ptr(std::make_shared(
ex, cnf, std::move(tls_context)
@@ -216,6 +216,39 @@ public:
return *this;
}
+ /**
+ * \brief Assign an authenticator that the Client will use for
+ * \__ENHANCED_AUTH\__ on every connect to a Broker.
+ * Re-authentication can be initiated by calling \ref async_authenticate.
+ *
+ * \param authenticator Object that will be stored (move-constructed or by reference)
+ * and used for authentication. It needs to satisfy \__is_authenticator\__ concept.
+ *
+ */
+ template
+ mqtt_client& authenticator(Authenticator&& authenticator) {
+ _svc_ptr->authenticator(std::forward(authenticator));
+ return *this;
+ }
+
+ /**
+ * \brief Initiates re-authentication.
+ * TODO
+ */
+ template
+ decltype(auto) async_authenticate(CompletionToken&& token) {
+ using Signature = void(error_code);
+
+ auto initiate = [] (
+ auto handler
+ ) {
+ // TODO re-authentication
+ };
+
+ return asio::async_initiate(
+ std::move(initiate), token
+ );
+ }
/**
* \brief Send a \__PUBLISH\__ packet to Broker to transport an
@@ -228,7 +261,7 @@ public:
* \param retain The \ref retain_e flag.
* \param props An instance of \__PUBLISH_PROPS\__.
* \param token Completion token that will be used to produce a
- * completion handler. The handler will be invoked when the operation is completed.
+ * completion handler. The handler will be invoked when the operation completes.
* On immediate completion, invocation of the handler will be performed in a manner
* equivalent to using \__POST\__.
*
@@ -313,7 +346,7 @@ public:
* \param topics A list of \ref subscribe_topic of interest.
* \param props An instance of \__SUBSCRIBE_PROPS\__.
* \param token Completion token that will be used to produce a
- * completion handler. The handler will be invoked when the operation is completed.
+ * completion handler. The handler will be invoked when the operation completes.
* On immediate completion, invocation of the handler will be performed in a manner
* equivalent to using \__POST\__.
*
@@ -373,7 +406,7 @@ public:
* \param topic A \ref subscribe_topic of interest.
* \param props An instance of \__SUBSCRIBE_PROPS\__.
* \param token Completion token that will be used to produce a
- * completion handler. The handler will be invoked when the operation is completed.
+ * completion handler. The handler will be invoked when the operation completes.
* On immediate completion, invocation of the handler will be performed in a manner
* equivalent to using \__POST\__.
*
@@ -421,7 +454,7 @@ public:
* \param topics List of Topics to unsubscribe from.
* \param props An instance of \__UNSUBSCRIBE_PROPS\__.
* \param token Completion token that will be used to produce a
- * completion handler. The handler will be invoked when the operation is completed.
+ * completion handler. The handler will be invoked when the operation completes.
* On immediate completion, invocation of the handler will be performed in a manner
* equivalent to using \__POST\__.
*
@@ -480,7 +513,7 @@ public:
* \param topic Topic to unsubscribe from.
* \param props An instance of \__UNSUBSCRIBE_PROPS\__.
* \param token Completion token that will be used to produce a
- * completion handler. The handler will be invoked when the operation is completed.
+ * completion handler. The handler will be invoked when the operation completes.
* On immediate completion, invocation of the handler will be performed in a manner
* equivalent to using \__POST\__.
*
@@ -531,7 +564,7 @@ public:
* or there is a pending Application Message.
*
* \param token Completion token that will be used to produce a
- * completion handler. The handler will be invoked when the operation is completed.
+ * completion handler. The handler will be invoked when the operation completes.
* On immediate completion, invocation of the handler will be performed in a manner
* equivalent to using \__POST\__.
*
@@ -574,7 +607,7 @@ public:
* the Broker of the reason for disconnection.
* \param props An instance of \__DISCONNECT_PROPS\__.
* \param token Completion token that will be used to produce a
- * completion handler. The handler will be invoked when the operation is completed.
+ * completion handler. The handler will be invoked when the operation completes.
* On immediate completion, invocation of the handler will be performed in a manner
* equivalent to using \__POST\__.
*
@@ -616,7 +649,7 @@ public:
* See \ref mqtt_client::cancel.
*
* \param token Completion token that will be used to produce a
- * completion handler. The handler will be invoked when the operation is completed.
+ * completion handler. The handler will be invoked when the operation completes.
* On immediate completion, invocation of the handler will be performed in a manner
* equivalent to using \__POST\__.
*
diff --git a/include/async_mqtt5/types.hpp b/include/async_mqtt5/types.hpp
index 79c1c82..2584a17 100644
--- a/include/async_mqtt5/types.hpp
+++ b/include/async_mqtt5/types.hpp
@@ -61,6 +61,23 @@ enum class dup_e : std::uint8_t {
yes = 0b1, no = 0b0
};
+/**
+ * \brief Represents the stage of \__ENHANCED_AUTH\__ process.
+ */
+enum class auth_step_e {
+ /** Client needs to send initial authentication data. */
+ client_initial,
+
+ /** Server responded with reason_codes.continue_authentication and possibly
+ * authentication data, client needs to send further authentication data.
+ */
+ server_challenge,
+
+ /** Server responded with reason_codes.success and final
+ * authentication data, which client validates.
+ */
+ server_final
+};
/**
* \brief Represents the \__SUBSCRIBE_OPTIONS\__ associated with each Subscription.