Authorization and ACL
EMQX Authorization and ACL: authorizer order, the Built-in Database ACL, how topic rules match, what a denial does, and pairing it with Authentication to reach least privilege.
Why this matters
The authentication in Chapter 6 controls who may connect, but not what a client may do to which topics once it is in. For one account you may want it to read only the studio sensors and control its own switches, not read any topic it likes, and certainly not control the devices you would rather it left alone. That is what authorization handles: you keep ACL rules in EMQX's built-in database, match publish/subscribe permissions on who + topic + action, and learn the order in which allow and deny are applied.
By the end you can tighten Home Assistant and each device down to the topics they are supposed to touch, and close the authentication + authorization loop at least privilege.
Core concepts
Authorization controls permissions on MQTT's publish and subscribe operations. EMQX offers these backend types: ACL File (rules in a file), the built-in database (Built-in Database), an external database (MySQL/PostgreSQL/MongoDB/Redis), and HTTP Server. You reach the screen from the Dashboard sidebar at Access Control → Authorization.
Order is the key part. Every time a client wants to publish or subscribe, EMQX walks a chain of authorizers in order. Each authorizer holds its own access control list (ACL) rules, and EMQX starts matching at the first authorizer; the first rule that matches that client decides, and its allow or deny is applied. If nothing in an authorizer matches, EMQX moves to the next one, and when nothing matches anywhere the global default in Global Settings decides (allow or deny).
So authentication (who you are) and authorization (what you may do) are two complementary layers: the built-in database ACL can key off username or client ID to decide who gets which topics and which actions.
Terms at a glance
| Term | Plain English | In one line |
|---|---|---|
| Authorization | what you may do | Decides publish/subscribe permissions |
| ACL | access control list | The set of topic permission rules |
| Authorizer | authorization checker | One unit that runs a rule set against a backend |
| Allow | let it through | Lets this action (and topic) through |
| Deny | block it | Blocks this action (and topic) |
| Action | the operation being checked | publish or subscribe |
| Topic Filter | topic pattern | A rule's topic, which may contain wildcards |
Hands-on
Open the Authorization page
Log in to the Dashboard, go to Access Control → Authorization in the sidebar, and click Create at the top right.
Create a built-in database authorizer
Choose Built-in Database as the backend (the built-in database needs no extra parameters) and click Create to finish. You land back on the Authorization List.
Add an ACL rule
On that authorizer, go to Actions → Permissions to edit the rules: pick the client identity (client ID / username / all users), enter the topic (it may contain
+and#), choose the action publish or subscribe, and set Allow or Deny. Save.Set the global defaults and verify
Back on the Authorization list, click Settings (the global settings) and decide whether "no rule matched" means allow or deny, and whether a denial should ignore (drop the request) or disconnect the client. Finally, run a real publish and subscribe with your test account and confirm that allow and deny behave the way the rules say.
Authorization order and the authorizer chain
Authorization lets you create several authorizers at once. When a client publishes or subscribes, EMQX checks them one by one and the first rule that matches decides — so order matters. On the Authorization List you can drag rows with the mouse, or reorder them from the Actions column; EMQX starts at the first one and works down until an ACL rule matches.
"The first match settles it" is exactly the point of ordering: say you have a very broad deny on device/# in front, and an allow on device/+/state behind it. The over-broad deny matches first, and the allow behind it never gets a turn. So think the narrow, precise rule through before you write it, and then order the rules by weight.
Global Settings decides the final fallback: when no authorizer finds a matching rule, you can have EMQX always allow or always deny, and choose whether a denial means ignore (drop the request silently) or disconnect the client. In a home setup most people set the no-match case to deny and stay on the safe side: if it is not on the allow list, it is blocked.
ACL rule matching and allow/deny
The built-in database ACL describes a permission along three dimensions, client + topic + action, one rule per row saying who may do which action on which topic. You can pick "all users" to set a baseline, then add rules for a specific client ID or username on top.
The topic in a rule can carry wildcards: collect your switch topics at home under living-room/switch/+/state, for example, and one rule covers every living-room switch. Allow lets that action-and-topic pair through, Deny blocks it. The same topic can carry both allow and deny rules, and which one wins is decided together by the authorizer order and the global settings.
The usual advice for a home setup is least privilege: allow only the publish/subscribe topics each account genuinely needs, rather than opening everything first and then patching holes with deny. That way, even if the ordering or the global settings have a gap, the exposure stays small.
Troubleshooting
- The client connects but cannot publish to a topic: usually an authorization rule is blocking that action or that topic. Go to Authorization → Permissions and check whether that account or client has an "action + topic + Allow" rule covering the topic you want, and watch whether the no-match value in Global Settings is deny.
- There is an Allow rule and it is still blocked: check the rule order, because EMQX goes by the first match. If a broader
#deny sits in front and matches first, the Allow behind it never gets a turn; simplify the rules or reorder them. - You want to block a client and nothing happens: change the rule aimed at it to deny, or add a "that topic + subscribe/publish + deny" rule earlier in the order; then confirm in Global Settings that a no-match is not an implicit allow.
- An external database authorizer shows Disconnected: the external database address, credentials or query syntax is wrong, or that database is not ready. The configuration still saves, but it fails at run time; fix the external side, go back to Overview to read its health, and confirm that what you picked really is the built-in database.
FAQ
How do authorization and authentication divide the work
Authentication confirms who you are first; authorization then decides whether you can do it. Authentication cannot stop a client from subscribing to someone else's topics — only authorization rules can allow or deny at topic granularity. Least privilege only starts once both are configured.
Which is better, the built-in database ACL or an ACL file
For a home setup where you configure things in the Dashboard, want every change to stick and want it to feel obvious, pick the built-in database. An ACL file keeps the rules as text, which suits a more engineering-minded owner who wants them under version control, but it is less obvious to edit. For most add-on users the built-in database is enough.
Does a client get disconnected after a deny
That depends on your Global Settings. EMQX allows either "drop the request after a denial (ignore the message)" or "disconnect the client outright (disconnect)". Pick ignore if you want the message blocked quietly, disconnect if you want the connection cut; neither is absolute, and home setups often pick ignore to avoid a run of reconnects.
Why does least privilege keep coming up
The wider the permissions, the larger the area that can be tampered with once an account is compromised or stolen. Open only the publish/subscribe topics each account needs right now and deny the rest — that keeps the damage from one broken device as small as it can be, and containment like this is what matters in home IoT.