Skip to content

Commit e56f1b8

Browse files
committed
feat[auth, client-id]: support username/password authentication; support
custom mqtt client ids
1 parent b4c79d4 commit e56f1b8

File tree

4 files changed

+52
-15
lines changed

4 files changed

+52
-15
lines changed

examples/mqtt_client_example/src/mqtt_client_example.erl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,10 @@ start() ->
2727
%% Start the MQTT client.
2828
%%
2929
Config = #{
30-
url => "mqtt://mqtt.eclipseprojects.io",
30+
url => "mqtt://broker.hivemq.com",
31+
% username => "some-user", % (optional)
32+
% password => "some-password", % (optional)
33+
% client_id => "some-client", % (optional - defaults to: atomvm-<DEVICE-MAC-ADDRESS>)
3134
connected_handler => fun handle_connected/1
3235
},
3336
{ok, _MQTT} = mqtt_client:start(Config),

markdown/mqtt_client.md

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ The input parameter to the `start/1` function is an Erlang `map` structure, cont
6363

6464
%% erlang
6565
Config = #{
66-
url => "mqtt://mqtt.eclipseprojects.io",
66+
url => "mqtt://broker.hivemq.com",
6767
connected_handler => fun handle_connected/1,
6868
disconnected_handler => fun handle_disconnected/1,
6969
error_handler => handle_error/2
@@ -113,7 +113,7 @@ You can publish a message using the `publish/4`
113113
%% erlang
114114
Topic = <<"atomvm/topic0">>,
115115
Message = <<"Hello!">>,
116-
MsgId = mqtt_client:publish(MTQQ, Topic, Message).
116+
MsgId = mqtt_client:publish(MQTT, Topic, Message).
117117

118118
The above function call will publish a message to the specified topic using the MQTT QoS `at_most_once`. Note that messages sent with `at_most_once` QoS are not subject to notification.
119119

@@ -151,7 +151,7 @@ Subscribe to an MQTT topic by using the `subscribe/3` function. Specify a topic
151151
subscribed_handler = fun handle_subscribed/2,
152152
data_handler = fun handle_data/3
153153
},
154-
ok = mqtt_client:subscribe(MTQQ, Topic, SubscribeOptions).
154+
ok = mqtt_client:subscribe(MQTT, Topic, SubscribeOptions).
155155

156156
The `subscribe/3` function will return `{error, already_subscribed}` if the client application is already subscribed to the specified topic.
157157

@@ -173,14 +173,14 @@ The `data_handler` will be passed the MQTT client instance, topic on which the m
173173

174174
### Unsubscribing from an MQTT topic
175175

176-
Use the `unscibscribe/3` function to unsubscribe from a topic.
176+
Use the `unsubscribe/3` function to unsubscribe from a topic.
177177

178178
%% erlang
179179
Topic = <<"atomvm/topic0">>,
180180
UnSubscribeOptions = #{
181181
unsubscribed_handler = fun handle_unsubscribed/2
182182
},
183-
ok = mqtt_client:unsubscribe(MTQQ, Topic, UnSubscribeOptions).
183+
ok = mqtt_client:unsubscribe(MQTT, Topic, UnSubscribeOptions).
184184

185185
The `unsubscribe/3` function will return `{error, not_subscribed}` if the client application is not yet subscribed to the specified topic.
186186

@@ -199,6 +199,21 @@ TODO
199199

200200
#### Username/Password authentication
201201

202+
The username and password can be specified either as parameters to `mqtt_client:start/1` in the configuration map or directly in the broker URL (if supported by the broker).
203+
204+
%% erlang
205+
Config = #{
206+
url => "mqtts://some-broker.io:8883"
207+
username => <<"test">>,
208+
password => <<"milkstout">>,
209+
...
210+
}
211+
% or
212+
Config = #{
213+
url => "mqtts://test:[email protected]:8883",
214+
...
215+
}
216+
202217
#### Connecting via TLS
203218

204219
##### Client TLS Authentication

ports/atomvm_mqtt_client.c

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ static const char *const unsubscribe_failed_atom = ATOM_STR("\x12", "unsu
6161
static const char *const unsubscribed_atom = ATOM_STR("\xC", "unsubscribed");
6262
static const char *const url_atom = ATOM_STR("\x3", "url");
6363
static const char *const username_atom = ATOM_STR("\x8", "username");
64+
static const char *const client_id_atom = ATOM_STR("\x9", "client_id");
6465

6566
// error codes
6667
static const char *const bad_username_atom = ATOM_STR("\x0C", "bad_username");
@@ -700,12 +701,11 @@ void atomvm_mqtt_client_init(GlobalContext *global)
700701
esp_log_level_set("MQTT_CLIENT", ESP_LOG_VERBOSE);
701702
}
702703

703-
// NB. Caller assumes ownership of returned string
704-
static char *maybe_get_string(term kv, AtomString key, GlobalContext *global)
704+
static char* maybe_get_string_or_default(term kv, AtomString key, char *default_value, GlobalContext *global)
705705
{
706706
term value_term = interop_kv_get_value(kv, key, global);
707707
if (!term_is_string(value_term) && !term_is_binary(value_term)) {
708-
return NULL;
708+
return default_value;
709709
}
710710

711711
int ok;
@@ -717,6 +717,12 @@ static char *maybe_get_string(term kv, AtomString key, GlobalContext *global)
717717
return value_str;
718718
}
719719

720+
// NB. Caller assumes ownership of returned string
721+
static char *maybe_get_string(term kv, AtomString key, GlobalContext *global)
722+
{
723+
return maybe_get_string_or_default(kv, key, NULL, global);
724+
}
725+
720726
// NB. Caller assumes ownership of returned string
721727
// static char *get_string_default(term kv, AtomString key, AtomString default_value, GlobalContext *global)
722728
// {
@@ -785,17 +791,26 @@ Context *atomvm_mqtt_client_create_port(GlobalContext *global, term opts)
785791
UNUSED(port);
786792
char *username_str = maybe_get_string(opts, username_atom, global);
787793
char *password_str = maybe_get_string(opts, password_atom, global);
794+
char *client_id_str = maybe_get_string_or_default(opts, client_id_atom, get_default_client_id(), global);
795+
// todo: implement cert support
788796
// char *cert_str = maybe_get_string(opts, cert_atom, global);
789797

790798
// Note that char * values passed into this struct are copied into the MQTT state
791-
const char *client_id = get_default_client_id();
792799
esp_mqtt_client_config_t mqtt_cfg = {
793800
#if ESP_IDF_VERSION_MAJOR >= 5
794801
.broker.address.uri = url_str,
795-
.credentials.client_id = client_id
802+
.credentials = {
803+
.username = username_str,
804+
.client_id = client_id_str,
805+
.authentication = {
806+
.password = password_str
807+
}
808+
},
796809
#else
797810
.uri = url_str,
798-
.client_id = client_id,
811+
.username = username_str,
812+
.password = password_str
813+
.client_id = client_id_str,
799814
.user_context = (void *) ctx
800815
#endif
801816
};
@@ -805,6 +820,7 @@ Context *atomvm_mqtt_client_create_port(GlobalContext *global, term opts)
805820
free(host_str);
806821
free(username_str);
807822
free(password_str);
823+
free(client_id_str);
808824

809825
if (UNLIKELY(IS_NULL_PTR(client))) {
810826
ESP_LOGE(TAG, "Error: Unable to initialize MQTT client.\n");

src/mqtt_client.erl

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,7 @@
9292
error_handler => fun((mqtt(), error()) -> any()),
9393
username => binary_or_string(),
9494
password => binary_or_string(),
95-
client_id => binary_or_string(),
96-
trusted_cert => binary_or_string()
95+
client_id => binary_or_string()
9796
}.
9897

9998
-type error_type() :: esp_tls | connection_refused | undefined.
@@ -494,7 +493,11 @@ init(Config) ->
494493
try
495494
Self = self(),
496495
Port = erlang:open_port({spawn, "atomvm_mqtt_client"}, [
497-
{receiver, Self}, {url, maps:get(url, Config)}
496+
{receiver, Self},
497+
{url, maps:get(url, Config)},
498+
{username, maps:get(username, Config)},
499+
{password, maps:get(password, Config)},
500+
{client_id, maps:get(client_id, Config)}
498501
]),
499502
{ok, #state{
500503
port = Port,

0 commit comments

Comments
 (0)