Send your first message, then decide who else may
Part 2 left you with a Dashboard you can read and a publish/subscribe model that has only ever existed on paper. This part closes both gaps in one sitting. First you send a real MQTT message and watch it come back, using a client that is already inside EMQX — nothing to install and not one line of code. Then you deal with what that first message quietly exposed: until you create an authenticator, an empty username and an empty password are enough to get in, and anything that gets in can read and write your whole topic space.
The broker works. That is a smaller claim than it sounds
EMQX 5.8.9 is installed, the Dashboard opens, and the tour in Part 2 built the mental model: a publisher sends a message on a topic, a subscriber asks for the topics it cares about, and the broker in the middle routes every message to whoever asked for it. All of that is still theory. You have not yet watched a single message travel through your own broker.
Fixing that takes one page of the Dashboard, because EMQX ships a test client inside it. And the moment you use it you will notice something the theory never mentioned: it connects with the Username and Password fields left blank, and it works.
8083, subscribe to testtopic/#, publish to testtopic/1, and watch your own message come back to you.That second point is not a scare story bolted onto a tutorial. Anyone who can reach your 1883 or your 8083 can publish to, and read, your entire topic space. The Woow add-on documentation lists “set up MQTT authentication after your first login” as required, not optional — which is why the first message and the first password belong in the same sitting rather than in two parts a week apart.
Diagnose → WebSocket Client, and the three menu items that are not there
There are several ways to talk to an MQTT broker. EMQX itself points at the desktop client MQTTX, the command-line MQTTX CLI and the browser build MQTTX Web. All three have to be installed, or at least remembered, somewhere outside EMQX. The fourth option does not: the WebSocket Client built into the Dashboard. It speaks MQTT over WebSocket, connects on port 8083 by default, and does all three actions — connect, subscribe and publish — right there in the page. For a first test tool that is the right trade.
You find it under Diagnose in the left menu. That menu holds five entries, and this is the part worth knowing before you go looking: three of them only exist in the paid EMQX Enterprise edition. On Open Source 5.8.9 you have two.
| Entry under Diagnose | Edition | What it is for |
|---|---|---|
| Alarms | Enterprise only | Not present in your Open Source 5.8.9 Dashboard |
| WebSocket Client | Open Source and Enterprise | Connect, subscribe and publish from the browser. This whole part rests on it |
| Topic Monitoring | Enterprise only | Not present in your Open Source 5.8.9 Dashboard |
| Slow Subscriptions | Enterprise only | Not present in your Open Source 5.8.9 Dashboard |
| Log Trace | Open Source and Enterprise | The other one that works here. It traces logs rather than sending messages |
Hunting an Open Source interface for a menu item that was only ever in Enterprise is a rite of passage nobody enjoys. The table above is there to save you that particular afternoon.
What the page gives you
The WebSocket Client covers three stages — connect, subscribe, publish — and keeps two lists underneath: Published, what you sent, and Received, what came back. Click + to open several WebSocket connections at once, each one independent of the others. That is how you simulate two devices: one connection subscribing, another publishing.
One behavior surprises everyone exactly once. Refreshing this page clears every connection and all the sent and received data. It is a quick test tool, not a place that keeps history.
It is a whiteboard, not a notebook. Excellent for working something out in front of you, and wiped the moment somebody walks past with a cloth. If you want the working kept, that is a different object — the desktop MQTTX, which saves connections and history.
| Term | Plain English | In one line |
|---|---|---|
| Publish | send a message | Send a message to a topic |
| Subscribe | ask for a topic | Declare which topics you want messages from |
| Topic | message address | The hierarchical name that classifies a message |
| Payload | message body | The data a publish actually carries |
| QoS | delivery guarantee | How strongly message delivery is guaranteed (0/1/2) |
| Retained | last message kept | The broker remembers the last message and a new subscriber receives it at once |
| WebSocket | browser transport | The transport that lets a browser speak MQTT |
Four moves, and a message that comes back to you
The trick that makes this test so cheap is that you are going to be both ends at once. One connection subscribes to a filter, the same connection publishes to a topic that filter covers, and the broker routes the message back across to you. If the row appears in Received, every link in the chain worked.
-
Step 1
Open the WebSocket client
Log in to the EMQX Dashboard. If you changed the username and password after installing, in Part 1, use your own. In the left menu click Diagnose → WebSocket Client.
-
Step 2
Make the first connection
In the Connection block, leave Host as
localhostand Port as8083. Leave Username and Password empty — no authentication is set up yet, and that is exactly the state we are about to examine. Click Connect. When the status turns to Connected, you are through.Host: localhostPort: 8083If you have the Dashboard open from another device rather than from the machine itself,
localhostmeans that other device and will not connect. Put your Home Assistant host address in the Host field instead. -
Step 3
Subscribe, then publish
In the Subscription block set Topic to
testtopic/#and click Subscribe. Then in the Publish block set Topic totesttopic/1, put{"msg":"Hello"}in Payload, start with QoS0, and click Publish. The same message shows up in the Received area below, because you are the subscriber as well.Subscription topic: testtopic/#Publish topic: testtopic/1Payload: {"msg":"Hello"} -
Step 4
Verify QoS
Change the Publish QoS to
1, send another message, and watch it reach Received just the same. Then try2. Nothing dramatic happens on screen — and that is worth seeing for yourself before the next section explains what the three levels actually promise.
flowchart LR S["Subscription block:
subscribe to testtopic/#"] --> B A["Publish block:
publish to testtopic/1"] --> B["EMQX asks: does any live
subscription filter match
the topic testtopic/1?"] B --> D["Received list, in the very
same tab, because you are
the subscriber as well"]
There is one rule about that pair of topics that catches nearly everybody, and it is worth internalizing before you invent your own names. The Topic in the Publish block cannot carry the + or # wildcards. Only a Subscription topic may use them. A publish names one exact topic; a subscription describes a set.
flowchart TD A["The topic string you are
about to type"] --> B{"Does it contain
a + or a # ?"} B -->|"no"| C["Both blocks take it.
testtopic/1 is fine
on either side"] B -->|"yes"| D{"Which block are
you typing into?"} D -->|"Subscription"| E["Accepted. testtopic/# is
a filter, and it matches
testtopic/1"] D -->|"Publish"| F["Refused. A publish topic
names one exact topic"]
Three delivery guarantees, and none of them is “better”
QoS — Quality of Service — is the MQTT mechanism that controls how strongly the delivery of a single message is guaranteed. EMQX supports all three levels at both ends, and in the WebSocket client you pick one for the subscription and one for the publish.
| QoS | Name | Guarantee | Typical use |
|---|---|---|---|
| 0 | At most once | No guarantee of delivery, and none against duplicates | Periodic sensor readings, where dropping one does no harm |
| 1 | At least once | Delivery guaranteed, but it may arrive twice | State changes where a repeat or two is acceptable |
| 2 | Exactly once | Delivery guaranteed, and never duplicated | Switches and control commands, where a repeat is not allowed |
Read that middle column again, because the shorthand people repeat — “higher is more reliable” — loses the part that actually bites. QoS 1 does not mean “more likely to arrive”; it means arrival is guaranteed and a duplicate is possible. If the thing on the other end opens a garage door, a duplicate is not a lesser problem than a loss.
QoS 0 is shouting a number across the kitchen while somebody is cooking. Usually heard, occasionally not, and you carry on regardless because another reading is coming in thirty seconds. QoS 1 is phoning and repeating yourself until they say yes — they definitely got it, and they may well have written it down twice. QoS 2 is the courier who needs a signature and will not leave a second parcel: it costs the most back-and-forth, and it is the only one you want for “unlock the door.”
The cheapest way to get a feel for it is the loop you already built: publish to a topic you subscribe to yourself. Because the publisher and the subscriber are the same end, the broker still routes the message across, and the Received rows let you confirm with your own eyes whether the same message comes back after a QoS change, and whether it arrives twice.
Retained is a separate switch
While you are in the Publish block, tick Retain and send one more message. The broker now remembers that message as the last one on that topic, and a new subscriber receives it immediately on subscribing instead of waiting for the next publish. Open a second connection with +, subscribe to testtopic/#, and it arrives before you have done anything else.
sequenceDiagram participant A as Connection A participant E as EMQX participant B as Connection B A->>E: publish testtopic/1, Retain ticked E->>E: store it as the retained message B->>E: subscribe to testtopic/#35; E->>B: hand over the retained message at once A->>E: publish testtopic/1 again, Retain ticked E->>B: deliver it as an ordinary message
Password-Based, with the built-in database
Everything so far worked with the Username and Password fields empty. Now we close that. Expand Access Control in the Dashboard's left menu and you get three items: Authentication, Authorization and Banned Clients, the blocklist. This part is the first of those. The second is Part 4.
EMQX keeps the two firmly apart, and the distinction is the single most useful thing in this section. Authentication answers “who are you?” — confirmed by a username and password, a client ID, a JWT or a similar mechanism. Authorization answers “what publishes and subscribes may this identity make?”
It is the badge at the front desk versus the list of rooms your badge opens. Authentication is the badge: the guard is satisfied you are who you say. Authorization is the list: this badge opens the second floor and the store cupboard, and nothing else. A building with badges and no room list is a building where everyone who got through the door can walk into the server room.
| Term | Plain English | In one line |
|---|---|---|
| Authentication | identity check | Confirms who you are |
| Password-Based | password mechanism | Checks a username (or client ID) plus a password |
| Built-in Database | built-in store | EMQX keeps the users and passwords itself |
| Credential | credentials | The data that proves an identity |
| JWT | signed token | A token signed by an issuer that carries claims |
| HTTP Server | HTTP backend | Your own HTTP service returns the authentication verdict |
| LDAP | directory protocol | A company directory that checks a user and password |
What you are choosing between
Creating an authenticator usually takes four steps: choose a Mechanism, choose a Backend that stores or fetches the data, fill in the connection details, and create it. The mechanisms are Password-Based (username and password), JWT (a token) and MQTT 5.0's SCRAM, a stronger, mutual check. The backends are the EMQX built-in database, an external database — MySQL, PostgreSQL, MongoDB or Redis — and an HTTP Server. JWT needs no backend at all.
flowchart LR M["Step 1
Mechanism"] --> P["Password-Based"] M --> J["JWT
(no backend to pick)"] M --> SC["SCRAM (MQTT 5.0)"] P --> B["Step 2
Backend"] B --> B1["Built-in Database"] B --> B2["MySQL / PostgreSQL /
MongoDB / Redis"] B --> B3["HTTP Server"]
For a home add-on install the practical choice is Password-Based plus Built-in Database. There is no second database to maintain, and adding a user directly in the Dashboard is enough to let Home Assistant and Zigbee2MQTT connect with a username and password. That is the route the steps below take.
-
Step 1
Open the Authentication page
In the Dashboard's left menu go to Access Control → Authentication, then click Create at the top right.
-
Step 2
Create Password-Based plus the built-in database
On the Create page pick Password-Based as the mechanism and Built-in Database as the backend. Set whether it matches on Username or ClientID, and which password hash to use, to suit your case. Then click Create. External databases and HTTP Server are left to the concept section below; you do not need them here.
-
Step 3
Add a user
Find the authenticator you just created in the Authenticator List, click User Management, and add a username and a password — for example
ha_brokerwith a password of your own choosing. It is stored in the EMQX built-in database. Write the password down somewhere you will still have it when you point Home Assistant at the broker. -
Step 4
Prove it took effect, then put it back
Go back to the WebSocket client from earlier in this part and connect with the credentials you just created — then try again with a deliberately wrong password and watch it be rejected. That rejection is the proof; a successful connection on its own does not tell you the authenticator is doing anything. If you also want to see what disabling does, turn this authenticator's Enable switch off in the Authenticator List, watch every client connect again, and turn it straight back on when the experiment is over. Delete any user you no longer need from User Management.
Two settings that will bite you later
The built-in database is the backend with the least to look after: usernames and passwords live in EMQX's own database and there is no separate data service to run. You can add and delete accounts by hand in User Management, or download the official template, fill it in, and use Import to create many at once. Two details on that page deserve more attention than they get.
The first is UserID Type: whether an account is identified by its username or by its client ID when it connects. It has to match the field your client actually sends: register an account by username, and a client that identifies itself only by client ID gives the check nothing to compare.
What the Enable switch really does
Each authenticator in the list has an Enable switch. The official documentation is explicit about what happens when you turn it off: after that, “all clients can connect.” It does not shut everyone out. It takes this identity check away.
It is sending the receptionist home for the evening. Nobody is turned away at the desk, because nobody is asked. If you switched it off for a maintenance job, the building is open until you switch it back on — and it looks exactly the same from the outside either way, which is what makes this one easy to leave off by accident.
flowchart TD
A["A client sends CONNECT"] --> B{"Is an authenticator there,
with Enable switched on?"}
B -->|"no authenticator, or Enable off"| C["Connected.
Every client is let in"]
B -->|"yes"| D{"Do the credentials match
the built-in database?"}
D -->|"no"| E["Rejected"]
D -->|"yes"| F["Connected as that identity"]
C --> G["Free to publish and subscribe
anywhere in the topic space,
until authorization is added"]
F --> G
That bottom box is Part 4's job, and it is why finishing this part does not mean you are finished. Authentication is the half that has to come first, because there is nothing to write rules about until identities exist.
The three you should recognize, and probably not build today
The Create page offers more than the route you just took. You do not need any of it for a home install, but you should be able to recognize each one and know when it earns its place — otherwise you will either reach for the wrong one or spend an evening looking for one that is not in your edition.
JWT
JWT (JSON Web Token) is token-based authentication. The client puts a JWT token into the username or password field when it connects, and EMQX only has to check the signature and the claims in the Payload — which is why JWT needs no backend of its own. When you create it you choose Secret (verify with a shared key) or Public Key (verify with a public key), say whether the secret is Base64-encoded, and fill the claims you want checked into Payload.
If you use a JWKS Endpoint, EMQX periodically fetches a set of RSA or ECDSA public keys from the authorization server to verify the JWT, and you set the refresh interval in seconds. JWT fits the case where an existing identity service already issues the tokens and you only want EMQX to accept them too. For a plain self-hosted Home Assistant setup that is usually a bonus rather than a requirement: with no token-issuing service in the picture, a username and password is simpler.
HTTP Server
HTTP Server hands authentication to an external HTTP service that you provide yourself. EMQX sends every connection request to that URL and allows or denies it according to the response. You configure the request method (POST or GET), the request URL — which must include the http or https scheme — the headers, and the data to be checked in the body, usually username and password. It can sit on top of an account system you already have, but you have to maintain that service and make sure the response format is what EMQX expects. A broker whose authentication depends on a second service you wrote is a broker with a second thing that can fail at three in the morning.
LDAP
LDAP (Lightweight Directory Access Protocol) is the protocol for checking a user against a directory server. The important part first: in EMQX, LDAP is only available in the paid Enterprise edition, so you will not find it in your Open Source 5.8.9 Dashboard. Conceptually it borrows an existing LDAP directory as the source of accounts. If reusing an account directory you already run is the goal, your options under Open Source are the built-in database, or an HTTP Server fronting your existing identity service.
| Route | What you have to run yourself | Edition | When it earns its place |
|---|---|---|---|
| Password-Based + Built-in Database | Nothing beyond EMQX | Open Source and Enterprise | A home add-on install. Start here |
| Password-Based + external database | MySQL, PostgreSQL, MongoDB or Redis | Open Source and Enterprise | A large number of accounts, central management, or an identity system you already run |
| Password-Based + HTTP Server | Your own HTTP service, and its response format | Open Source and Enterprise | You want the verdict to come from an account system you already have |
| JWT | Whatever issues and signs the tokens | Open Source and Enterprise | Tokens are already being issued somewhere and you want EMQX to accept them too |
| LDAP | A directory server | Enterprise only | Not an option on Open Source 5.8.9, whatever your directory looks like |
The failures that actually happen, in the order they happen
| Symptom | Likely cause | How to fix it |
|---|---|---|
| Connect does nothing, or keeps failing | Another service — WebRTC, for example — has taken 8083, the same kind of port conflict covered when you installed the add-on. Or you are coming in from another device |
Check what else is holding 8083. If you are connecting from another device, Host has to be the Home Assistant host address, not localhost |
| You subscribed but nothing arrives | The subscription topic does not cover the publish topic | Check that the filter matches with its wildcard — only a subscription to testtopic/# matches testtopic/1. After a successful publish the Received area should hold a row; if there is not a single one, the broker did not actually route the message |
| The same message arrives more than once | You are on QoS 1, which guarantees at-least-once delivery | A duplicate is possible by design, and it is not a broker fault. Choose QoS 2 when you need exactly one copy |
| Everything disappears after you refresh the page | The built-in WebSocket client clears itself by default | Not a bug. If you need connection history or several saved setups, switch to the desktop MQTTX; that is what suits ongoing testing |
| Home Assistant stops connecting once you create an authenticator | The field you matched on, or the password, is wrong | Check whether UserID Type reads the username or the client ID, the case of the account name you added, and its password. Check the authenticator has not been left disabled — if your experiment turned Enable off, switching it back on restores things |
| After you turn Enable off, every client can connect | That is by design. Disabling takes the whole authentication layer away | It does not tighten anything, so do not read a disabled authenticator as protection switched on. To keep out clients you do not recognize you need authorization alongside a working authenticator |
| JWT keeps failing authentication | A mismatch between how the token was signed and how EMQX was told to verify it | Check whether you chose Secret or Public Key, whether the Base64 switch matches on both sides, and whether the Payload claims match the claims inside your JWT. Start with a token you can read on the issuing side and test it in the Dashboard by the shortest path |
| The external database or HTTP backend shows Disconnected | EMQX cannot reach that server, or the query failed | Check the server address, the port, the credentials and whether the response format is what EMQX expects. Once the external side is fixed, go back to the Authenticator List and let it connect again |
How does the built-in WebSocket client differ from MQTTX?
Why does the test use 8083 rather than 1883?
8083. 1883 is plain TCP MQTT, which a browser cannot open directly; to reach 1883 you need a tool such as the desktop MQTTX or the CLI. Both are MQTT — only the transport layer differs.Which of QoS 0, 1 and 2 should I actually choose?
Can the tool hold several connections at once?
Aren't authentication and authorization the same thing?
Which suits home use, the built-in database or MySQL/Redis?
Where does JWT fit in a smart home?
Why can't I find LDAP?
Where to go from here
Your broker now knows who is knocking. It still does not care what they do.
A message has travelled through your own EMQX and come back, and a wrong password is refused. Part 4 takes the other half: authorization, which decides which topics an identity may publish to and subscribe to, and then listeners and TLS — the ports themselves, and how to stop the password you just set from crossing the network in the clear.
Open the full guidePart 3 of the WoowTech EMQX Complete Guide series on the Apporo blog.
Adapted from the WoowTech EMQX Complete Guide, produced by WoowTech and released under CC BY 4.0. This adaptation is published by Apporo under the same licence.
Light · Air · Water · Control · apporo