From 95ed769c5737c096e86cbeb077d7aaeb3bed85b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Charvet=20=E9=BB=91=E7=93=9C?= Date: Mon, 5 Aug 2024 19:50:49 +0100 Subject: [PATCH] Basic check for scopes when refreshing This is incomplete but until teiserver supports more than one scope it is not really possible to test that. It also doesn't really make sense. --- .../controllers/o_auth/code_controller.ex | 27 +++++++++++++++++++ .../o_auth/code_controller_test.exs | 12 +++++++++ 2 files changed, 39 insertions(+) diff --git a/lib/teiserver_web/controllers/o_auth/code_controller.ex b/lib/teiserver_web/controllers/o_auth/code_controller.ex index a92eb147e..51a64dcda 100644 --- a/lib/teiserver_web/controllers/o_auth/code_controller.ex +++ b/lib/teiserver_web/controllers/o_auth/code_controller.ex @@ -16,6 +16,11 @@ defmodule TeiserverWeb.OAuth.CodeController do end end + # As per https://datatracker.ietf.org/doc/html/rfc6749#section-6 this + # endpoint should accept the parameter `scope`. + # As of writing, there is only ever one scope allowed: tachyon.lobby + # so this parameter is ignored. + # If we ever expand the allowed scopes, this feature should be added def token(conn, %{"grant_type" => "refresh_token"} = params) do case check_required_keys(params, ["grant_type", "refresh_token", "client_id"]) do :ok -> @@ -68,6 +73,9 @@ defmodule TeiserverWeb.OAuth.CodeController do defp refresh_token(conn, params) do with {:ok, app} <- get_app_by_uid(params["client_id"]), + # Once we support more than one single scope, modify this function to allow + # changing the scope of the new token + {:ok, _scopes} <- check_scopes(app, params), {:ok, token} <- OAuth.get_valid_token(params["refresh_token"]), :ok <- if(token.application_id == app.id, @@ -137,6 +145,25 @@ defmodule TeiserverWeb.OAuth.CodeController do end end + defp check_scopes(app, params) do + scopes = Map.get(params, "scope", "") |> String.split() |> Enum.into(MapSet.new()) + app_scopes = MapSet.new(app.scopes) + + cond do + MapSet.size(scopes) == 0 -> + {:ok, app.scopes} + + MapSet.subset?(scopes, app_scopes) -> + {:ok, MapSet.to_list(scopes)} + + true -> + invalid_scopes = MapSet.difference(scopes, app_scopes) + + {:error, + "the following scopes aren't allowed: #{inspect(MapSet.to_list(invalid_scopes))}"} + end + end + def metadata(conn, _params) do conn |> put_status(200) |> render(:metadata) end 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 42e82393a..9355be0ea 100644 --- a/test/teiserver_web/controllers/o_auth/code_controller_test.exs +++ b/test/teiserver_web/controllers/o_auth/code_controller_test.exs @@ -299,6 +299,18 @@ defmodule TeiserverWeb.OAuth.CodeControllerTest do resp = post(conn, ~p"/oauth/token", data) assert %{"error" => "invalid_request"} = json_response(resp, 400) end + + test "must provide valid scope", %{conn: conn} = setup_data do + data = %{ + grant_type: "refresh_token", + client_id: setup_data[:app].uid, + refresh_token: setup_data[:token].refresh_token.value, + scope: "lolscope" + } + + resp = post(conn, ~p"/oauth/token", data) + assert %{"error" => "invalid_request"} = json_response(resp, 400) + end end describe "medatata endpoint" do