Implement X-OAUTH2 authentication for mod_mqtt

This will only work for MQTT 5.0 connections.
A client MUST set "Authentication Method" property of CONNECT
packet to "X-OAUTH2" and MUST set the token in "Authentication Data"
property of the same CONNECT packet.

The server responses as usual with CONNACK.
This commit is contained in:
Evgeny Khramtsov 2019-10-11 16:46:16 +03:00
parent 8d571adca8
commit 211cc80fd4
1 changed files with 40 additions and 18 deletions

View File

@ -677,12 +677,17 @@ set_will_properties(State, _) ->
-spec get_connack_properties(state(), connect()) -> properties(). -spec get_connack_properties(state(), connect()) -> properties().
get_connack_properties(#state{session_expiry = SessExp, jid = JID}, get_connack_properties(#state{session_expiry = SessExp, jid = JID},
#connect{client_id = ClientID, #connect{client_id = ClientID,
keep_alive = KeepAlive}) -> keep_alive = KeepAlive,
properties = Props}) ->
Props1 = case ClientID of Props1 = case ClientID of
<<>> -> #{assigned_client_identifier => JID#jid.lresource}; <<>> -> #{assigned_client_identifier => JID#jid.lresource};
_ -> #{} _ -> #{}
end, end,
Props1#{session_expiry_interval => SessExp div 1000, Props2 = case maps:find(authentication_method, Props) of
{ok, Method} -> Props1#{authentication_method => Method};
error -> Props1
end,
Props2#{session_expiry_interval => SessExp div 1000,
shared_subscription_available => false, shared_subscription_available => false,
topic_alias_maximum => topic_alias_maximum(JID#jid.lserver), topic_alias_maximum => topic_alias_maximum(JID#jid.lserver),
server_keep_alive => KeepAlive}. server_keep_alive => KeepAlive}.
@ -1169,24 +1174,41 @@ parse_credentials(JID, ClientID) ->
end. end.
-spec authenticate(connect(), peername()) -> {ok, jid:jid()} | {error, reason_code()}. -spec authenticate(connect(), peername()) -> {ok, jid:jid()} | {error, reason_code()}.
authenticate(#connect{password = Pass} = Pkt, IP) -> authenticate(Pkt, IP) ->
case authenticate(Pkt) of
{ok, JID, AuthModule} ->
?INFO_MSG("Accepted MQTT authentication for ~ts by ~s backend from ~s",
[jid:encode(JID),
ejabberd_auth:backend_type(AuthModule),
ejabberd_config:may_hide_data(misc:ip_to_list(IP))]),
{ok, JID};
{error, _} = Err ->
Err
end.
-spec authenticate(connect()) -> {ok, jid:jid(), module()} | {error, reason_code()}.
authenticate(#connect{password = Pass, properties = Props} = Pkt) ->
case parse_credentials(Pkt) of case parse_credentials(Pkt) of
{ok, #jid{luser = LUser, lserver = LServer} = JID} -> {ok, #jid{luser = LUser, lserver = LServer} = JID} ->
case ejabberd_auth:check_password_with_authmodule( case maps:find(authentication_method, Props) of
LUser, <<>>, LServer, Pass) of {ok, <<"X-OAUTH2">>} ->
{true, AuthModule} -> Token = maps:get(authentication_data, Props, <<>>),
?INFO_MSG( case ejabberd_oauth:check_token(
"Accepted MQTT authentication for ~ts " LUser, LServer, [<<"sasl_auth">>], Token) of
"by ~ts backend from ~ts", true -> {ok, JID, ejabberd_oauth};
[jid:encode(JID), _ -> {error, 'not-authorized'}
ejabberd_auth:backend_type(AuthModule), end;
ejabberd_config:may_hide_data(misc:ip_to_list(IP))]), {ok, _} ->
{ok, JID}; {error, 'bad-authentication-method'};
false -> error ->
{error, 'not-authorized'} case ejabberd_auth:check_password_with_authmodule(
end; LUser, <<>>, LServer, Pass) of
{error, _} = Err -> {true, AuthModule} -> {ok, JID, AuthModule};
Err false -> {error, 'not-authorized'}
end
end;
{error, _} = Err ->
Err
end. end.
%%%=================================================================== %%%===================================================================