HiveMQ

OAuth authentication + SQL authorization

Hi, I’m using the Enterprise Security Extension as trial. I’ve setup JWT authentication with Okta following this tutorial:

This works, so when using some MQTT client I can publish/subscribe using the bearer token retrieved from Okta as “password”. I can’t get the authorization part to work though, all SQL should be configured correctly, I’ve been following this:

https://www.hivemq.com/docs/ese/4.5/enterprise-security-extension/ese-getting-started.html#getting-started-with-sql-databases

Not sure what could be wrong here. The ESE comes with a sample config that pretty much reflects what I want to do (using JWT for authentication and SQL for authorization):

/opt/hivemq/extensions/hivemq-enterprise-security-extension/conf/examples/jwt-realm/enterprise-security-extension.xml

However I don’t understand how HiveMQ verifies authorization, I’d have expected this is based on username supplied by MQTT client. So if username is the same as in SQL USERS table authorization should succeed, if not it should fail. Does not seem to be this way though. Could it be that for successful authorization there needs to be some information in the bearer token?

Hello janpetzold,

Welcome to the HiveMQ Community, Nice to see that you’re taking interest in HiveMQ.

In order to help with the question, can you please share the version of Hivemq and enterprise security extension configurations with us?

Regards,
Sheetal from HiveMQ Team

Hi, thanks for getting back. HiveMQ is version 4.5.2, Enterprise extension seems to be the same 4.5.2.

Just to add some detail: I’m able to publish messages using a valid token, I can also subscribe to same queue successfully (so the actual CONNECT part works) but messages are never received. So some sort of authorization check seems to happen but not sure where exactly.

Hello ,

Thanks for the reply.

However, to help you further it would be helpful if you share enterprise security extension configurations(enterprise-security-extension.xml), access.log, and event.log.

Kind regards,
Sheetal

Thanks. This is the relevant part of event.log:

Client ID: mosq-mwRvwalOgX2yGmk2D2, IP: XX.XX.XX.XX was disconnected. reason: Not authorized to publish on topic 'foo/bar' with QoS '0' and retain 'false'.

access.log:

authorization-succeeded - Client succeeded authorization: ID mosq-mwRvwalOgX2yGmk2D2, IP XX.XX.XX.X, permissions [].

So authentication/authorization succeeds but there is no permission and therefore publishing must fail (no error on command-line though). I’m using mosquitto_pub for publishing and there I get

Client mosq-mwRvwalOgX2yGmk2D2 sending CONNECT
Client mosq-mwRvwalOgX2yGmk2D2 received CONNACK (0)
Client mosq-mwRvwalOgX2yGmk2D2 sending PUBLISH (d0, q0, r0, m1, 'foo/bar', ... (5 bytes))
Client mosq-mwRvwalOgX2yGmk2D2 sending DISCONNECT

This is weird since configuration in database should be correct (in fact it works if I use plain SQL-based authentication+authorization, but not with JWT-based authentication).

I uploaded the enterprise-security-extension.xml here:

Thanks for looking at this!

Hello

Thanks for sharing the logs and config file.

From the configurations we have observed that you have set <use-authorization-role-key>true</use-authorization-role-key> to use ESE role permissions.

In order to have authorization with role permissions, the variable authorization-role-key must be set so that the ESE can query the corresponding role and its permissions from the database.

Note: The database must have entries for the roles and permissions that you are looking for.

You can also verify this in your access.log if associated permissions are there or not in the permissions array.

You can set this variable with the help of authorization-preprocessors and jwt-preprocessors; Please check following code snippet,

            <authorization-preprocessors>
                <!-- Set claim "sub" as role key for Authorization -->            
                <jwt-preprocessor>
                    <source>authentication-byte-secret</source>
                    <jwt-extractions>
                        <jwt-extraction>
                            <jwt-claim>sub</jwt-claim>
                            <ese-variable>authorization-role-key</ese-variable>
                        </jwt-extraction>
                    </jwt-extractions>
                </jwt-preprocessor>
                <!-- Verify role-key that is used for authorization -->
                <logging-preprocessor>
                    <message>Key = ${authorization-key} - Role Key = ${authorization-role-key}</message>
                    <level>info</level>
                    <name>com.example.logger</name>
                </logging-preprocessor>
            </authorization-preprocessors>

You can find detailed documentation about the same here.

I hope this helps.

Kind regards,
Sheetal from the HiveMQ Team

2 Likes

Hi Sheetal,

thanks a ton, works exactly as you described. Wasn’t really clear to me that the JWT preprocessor is sort of “mandatory” but of course it makes sense to me now. Maybe consider adding this to some tutorial of yours.

For everyone else stumbling upon this: If you want to use authorization via JWT using an SQL DB and follow the example make sure to note the “sub” value (=subject / unique user ID) in your tokens. If you use Okta like me just decode your token via

and note the “sub” value. Add this in your ROLES.NAME column and make sure this is correctly referenced in ROLE.PERMISSIONS and PERMISSIONS. You can test this with mosquitto_sub and the username you use there does not seem to matter anymore (still need to provide one) since the authorization key is taken from the token.