processone / ejabberd-contrib

Growing and curated ejabberd contributions repository - PR or ask to join !
http://ejabberd.im
250 stars 140 forks source link

ejabberd_auth_http: generated POST requests do not set Content-Type: application/x-www-form-urlencoded header #273

Closed vkelman closed 5 years ago

vkelman commented 5 years ago

I was trying to understand why my REST end point refuses to handle "register" request sent by ejabberd_auth_http, while seemingly identical request sent by POSTMAN are handled fine. After setting up a reverse proxy I discovered that ejabberd_auth_http fails to set Content-Type: application/x-www-form-urlencoded header when generating POST requests.

vkelman commented 5 years ago

Here is the RAW request issued by POSTMAN:

POST http://localhost/services/imd35000/api/v1/ejabberd/register HTTP/1.1
cache-control: no-cache
Postman-Token: fc739f64-88ee-41d4-93c0-56f70ec63d39
User-Agent: PostmanRuntime/7.6.1
Accept: */*
Host: localhost
content-type: application/x-www-form-urlencoded
accept-encoding: gzip, deflate
content-length: 47
Connection: keep-alive
user=vkelman&server=planetrisk.com&pass=vkelman

Compare it with RAW request issued by ejaberrd_auth_http:

POST http://localhost/services/imd35000/api/v1/ejabberd/register HTTP/1.1
Host: localhost
Content-Length: 47
Authorization: Basic
user=vkelman&server=planetrisk.com&pass=vkelman

The absence of content-type: application/x-www-form-urlencoded header breaks client app controller routing.

badlop commented 5 years ago

Then, something like this fixes it?

diff --git a/ejabberd_auth_http/src/ejabberd_auth_http.erl b/ejabberd_auth_http/src/ejabberd_auth_http.erl
index 2e657d8..a665f9e 100644
--- a/ejabberd_auth_http/src/ejabberd_auth_http.erl
+++ b/ejabberd_auth_http/src/ejabberd_auth_http.erl
@@ -219,6 +219,7 @@ make_req(Method, Path, LUser, LServer, Password) ->
     PasswordE = list_to_binary(http_uri:encode(binary_to_list(Password))),
     Query = <<"user=", LUserE/binary, "&server=", LServerE/binary, "&pass=", PasswordE/binary>>,
     Header = [{<<"Authorization">>, <<"Basic ", BasicAuth64/binary>>}],
+    ContentType = {<<"Content-Type">>, <<"application/x-www-form-urlencoded">>},
     Connection = cuesport:get_worker(existing_pool_name(LServer)),

     ?DEBUG("Making request '~s' for user ~s@~s...", [Path, LUser, LServer]),
@@ -226,7 +227,7 @@ make_req(Method, Path, LUser, LServer, Password) ->
         get -> fusco:request(Connection, <<PathPrefix/binary, Path/binary, "?", Query/binary>>,
                              "GET", Header, "", 2, 5000);
         post -> fusco:request(Connection, <<PathPrefix/binary, Path/binary>>,
-                              "POST", Header, Query, 2, 5000)
+                              "POST", [ContentType|Header], Query, 2, 5000)
     end,

     ?DEBUG("Request result: ~s: ~p", [Code, RespBody]),
vkelman commented 5 years ago

@badlop - perhaps. I'm sorry, I cannot update ejabberd_auth_http source code as you said, compile it and try with my (.NET) web service, because I'm not Erlang developer yet and don't have developer's environment. I'm pretty sure I could find some workaround and force my web service to work without Content-Type: application/x-www-form-urlencoded header, but that header is pretty standard thing, so it's better to add it to ejabberd_auth_http. Let me know if you want me to close this ticket or maybe you are going to make that change in ejabberd_auth_http code first?

badlop commented 5 years ago

I've applied the fix to the git repository. You can update the source code and upgrade the code to confirm this fixes the problem. Something like:

ejabberdctl modules_update_specs
ejabberdctl module_upgrade ejabberd_auth_http
vkelman commented 5 years ago

Will do and let you know, thank you.

vkelman commented 5 years ago

Hi @badlop ,

Finally got a moment to test registering a user with updated ejabberd_auth_http. My C# REST end point now got "register" request from ejabberd_auth_http. "user" and "server" arguments were populated, but "pass" was null. It's quite possible an error in my code; I'll check the exact request I'm receiving from ejabberd_auth_http using reverse proxy to figure it out.

vkelman commented 5 years ago

Yes, it works fine... and I now suspect the initial problem wasn't in a lack of "content-type: application/x-www-form-urlencoded" header. ASP.NET WebAPI request parameters handling is pretty brittle and picky: https://weblog.west-wind.com/posts/2012/Aug/16/Mapping-UrlEncoded-POST-Values-in-ASPNET-Web-API https://stackoverflow.com/questions/20369361/asp-net-web-api-cannot-get-application-x-www-form-urlencoded-http-post

Anyway, I think it was a good idea to add that header anyway, because it's kind of standard.

mremond commented 5 years ago

Indeed, I agree that it would be very useful to add that header as default.

vkelman commented 5 years ago

But that's what @badlop has already done - his checked in code now adds this header by default.

mremond commented 5 years ago

ah ok, sorry, I had missed the commit.

Thanks!