diff --git a/lib/teiserver_web/controllers/o_auth/code_controller.ex b/lib/teiserver_web/controllers/o_auth/code_controller.ex index 960e46c1b..30dd436dc 100644 --- a/lib/teiserver_web/controllers/o_auth/code_controller.ex +++ b/lib/teiserver_web/controllers/o_auth/code_controller.ex @@ -32,15 +32,12 @@ defmodule TeiserverWeb.OAuth.CodeController do end def token(conn, %{"grant_type" => "client_credentials"} = params) do - case Enum.find( - ["grant_type", "client_id", "client_secret"], - fn key -> not Map.has_key?(params, key) end - ) do - nil -> - get_token_from_credentials(conn, params) + case get_credentials(conn, params) do + {:ok, client_id, client_secret} -> + get_token_from_credentials(conn, client_id, client_secret) - missing_key -> - conn |> put_status(400) |> render(:error, error_description: "missing #{missing_key}") + {:error, msg} -> + conn |> put_status(400) |> render(:error, error_description: msg) end end @@ -71,8 +68,21 @@ defmodule TeiserverWeb.OAuth.CodeController do end end - defp get_token_from_credentials(conn, params) do - with {:ok, cred} <- OAuth.get_valid_credentials(params["client_id"], params["client_secret"]), + defp get_credentials(conn, params) do + basic = Plug.BasicAuth.parse_basic_auth(conn) + post_params = {Map.get(params, "client_id"), Map.get(params, "client_secret")} + + case {basic, post_params} do + {:error, {nil, nil}} -> {:error, "unauthorized"} + {{user, pass}, _} -> {:ok, user, pass} + {_, {nil, _}} -> {:error, "missing client_id"} + {_, {_, nil}} -> {:error, "missing client_secret"} + {_, {client_id, client_secret}} -> {:ok, client_id, client_secret} + end + end + + defp get_token_from_credentials(conn, client_id, client_secret) do + with {:ok, cred} <- OAuth.get_valid_credentials(client_id, client_secret), {:ok, token} <- OAuth.get_token_from_credentials(cred) do conn |> put_status(200) |> render(:token, token: token) else diff --git a/lib/teiserver_web/views/api/o_auth/code_view.ex b/lib/teiserver_web/views/api/o_auth/code_view.ex index 341e0897f..1df896536 100644 --- a/lib/teiserver_web/views/api/o_auth/code_view.ex +++ b/lib/teiserver_web/views/api/o_auth/code_view.ex @@ -23,7 +23,7 @@ defmodule TeiserverWeb.OAuth.CodeView do issuer: base, authorization_endpoint: base <> ~p"/oauth/authorize", token_endpoint: base <> ~p"/oauth/token", - token_endpoint_auth_methods_supported: ["none", "client_secret_post"], + token_endpoint_auth_methods_supported: ["none", "client_secret_post", "client_secret_basic"], grant_types_supported: [ "authorization_code", "refresh_token", diff --git a/test/teiserver_web/controllers/o_auth/code_controller_test.exs b/test/teiserver_web/controllers/o_auth/code_controller_test.exs index 5273f2267..74c00d97e 100644 --- a/test/teiserver_web/controllers/o_auth/code_controller_test.exs +++ b/test/teiserver_web/controllers/o_auth/code_controller_test.exs @@ -15,8 +15,9 @@ defmodule TeiserverWeb.OAuth.CodeControllerTest do end defp setup_conn(_context) do - GeneralTestLib.conn_setup(Teiserver.TeiserverTestLib.player_permissions()) - |> Teiserver.TeiserverTestLib.conn_setup() + conn = Phoenix.ConnTest.build_conn() + user = GeneralTestLib.make_user() + {:ok, conn: conn, user: user} end defp setup_app(context) do @@ -154,6 +155,35 @@ defmodule TeiserverWeb.OAuth.CodeControllerTest do resp = post(conn, ~p"/oauth/token", data) json_resp = json_response(resp, 400) end + + test "can also use basic auth", + %{conn: conn, credential: credential, credential_secret: secret} = setup_data do + data = %{grant_type: "client_credentials"} + auth_header = Plug.BasicAuth.encode_basic_auth(credential.client_id, secret) + + conn = + conn + |> put_req_header("authorization", auth_header) + + resp = post(conn, ~p"/oauth/token", data) + json_resp = json_response(resp, 200) + assert is_binary(json_resp["access_token"]), "has access_token" + assert is_integer(json_resp["expires_in"]), "has expires_in" + assert is_binary(json_resp["refresh_token"]), "has refresh_token" + assert json_resp["token_type"] == "Bearer", "bearer token type" + end + + test "basic auth check", %{conn: conn, credential: credential} = setup_data do + data = %{grant_type: "client_credentials"} + auth_header = Plug.BasicAuth.encode_basic_auth(credential.client_id, "lolnope") + + conn = + conn + |> put_req_header("authorization", auth_header) + + resp = post(conn, ~p"/oauth/token", data) + json_resp = json_response(resp, 400) + end end describe "refresh token" do @@ -213,7 +243,11 @@ defmodule TeiserverWeb.OAuth.CodeControllerTest do "issuer" => "https://beyondallreason.info", "authorization_endpoint" => "https://beyondallreason.info/oauth/authorize", "token_endpoint" => "https://beyondallreason.info/oauth/token", - "token_endpoint_auth_methods_supported" => ["none", "client_secret_post"], + "token_endpoint_auth_methods_supported" => [ + "none", + "client_secret_post", + "client_secret_basic" + ], "grant_types_supported" => [ "authorization_code", "refresh_token",