Open pouriya opened 4 years ago
ping
Did you add that to your local ejabberd deployment? In what file did you add it?
@pouriya: Have you seen the @badlop comment?
Hi. Sorry for late reply.
@badlop I currently use process dictionary to keep decoded JWT in ejabberd_auth_jwt
and add it in c2s state after success authentication AND I KNOW it's dirty 👎🏼 .
But I am thinking of:
% ejabberd_c2s.erl
% ...
check_password_fun(<<"X-OAUTH2">>, #{lserver := LServer}) ->
Pid = self(),
fun(User, _AuthzId, Token) ->
Res = case ejabberd_oauth:check_token(User, LServer, [<<"sasl_auth">>], Token) of
true -> {true, ejabberd_oauth};
_ -> {false, ejabberd_oauth}
end,
ejabberd_hooks:run(c2s_check_password_result, LServer, [Res, User, LServer, Token, Pid]),
Res
end;
check_password_fun(_Mech, #{lserver := LServer}) ->
Pid = self(),
fun(U, AuthzId, P) ->
Res = ejabberd_auth:check_password_with_authmodule(U, AuthzId, LServer, P),
ejabberd_hooks:run(c2s_check_password_result, LServer, [Res, User, LServer, P, Pid]),
Res
end.
check_password_digest_fun(_Mech, #{lserver := LServer}) ->
Pid = self(),
fun(U, AuthzId, P, D, DG) ->
Res = ejabberd_auth:check_password_with_authmodule(U, AuthzId, LServer, P, D, DG),
ejabberd_hooks:run(c2s_check_password_result, LServer, [Res, User, LServer, P, Pid]),
Res
end.
% ...
And:
% ejabberd_auth_jwt.erl
% ...
start(Host) ->
% ...
ejabberd_hooks:add(c2s_check_password_result, Host, ?MODULE, send_decoded_jwt_fields, 50),
ejabberd_hooks:add(c2s_handle_info, Host, ?MODULE, c2s_handle_info_jwt_fields, 50),
% ...
.
send_decoded_jwt_fields({true, ?MODULE}=Ret, User, Server, Password, Pid) ->
{true, {jose_jwt, Fields}, _} = jose_jwt:verify(JWK, Password),
ejabberd_cluster:send(Pid, {jwt_fields, Fields}),
Ret;
send_decoded_jwt_fields(Acc, _, _, _, _) ->
Acc.
c2s_handle_info_jwt_fields(State, {jwt_fields, Fields}) ->
{stop, ejabberd_hooks:run_fold(process_decoded_jwt, LServer, State, [Fields])};
c2s_handle_info_jwt_fields(Acc, _) ->
Acc.
% ...
Keeping Fields
in process dictionary currently works for me. I'm not sure but I think in above code I'm sending Fields
to self()
. If this is correct, So we can leave ejabberd_c2s.erl
unchanged and simply do:
% ejabberd_auth_jwt.erl
% ...
start(Host) ->
% ...
ejabberd_hooks:add(c2s_handle_info, Host, ?MODULE, c2s_handle_info_jwt_fields, 50),
% ...
.
% ...
c2s_handle_info_jwt_fields(State, {jwt_fields, Fields}) ->
{stop, ejabberd_hooks:run_fold(process_decoded_jwt, LServer, State, [Fields])};
c2s_handle_info_jwt_fields(Acc, _) ->
Acc.
% ...
%%%----------------------------------------------------------------------
%%% Internal functions
%%%----------------------------------------------------------------------
check_jwt_token(User, Server, Token) ->
% ...
Ret = ejabberd_hooks:run_fold(
check_decoded_jwt,
Server,
true,
[Fields, Signature, Server, User]
),
Ret == true andalso ejabberd_cluster:send(self(), {jwt_fields, Fields}),
Ret
% ...
.
If the only file to change is ejabberd_auth_jwt.erl, then it will be quite easy and safe to include. Check if that's the case.
If ejabberd_c2s.erl requires changes, and they are only adding calls to ejabberd_hooks:run, it seems small changes and could quite safe too.
Describe the solution you'd like something like:
So I can check my custom metadata in given fields and update state and so on.
Describe alternatives you've considered Since
ejabberd_c2s
usesejabberd_auth:*
API and the entire API does not acceptC2SState
, We can not put above hook here. So I think the fastest (and dirtiest) way is using for examplec2s_handle_recv
hook and check if auth method for detected server is JWT, if yes then I have to check JWT myself and it's error prone !