ensure token matches
This commit is contained in:
parent
1e025ee28d
commit
d1bbec0441
4 changed files with 131 additions and 12 deletions
|
@ -71,16 +71,27 @@ def authorize(
|
||||||
|
|
||||||
def authorize(%Plug.Conn{} = conn, params), do: do_authorize(conn, params)
|
def authorize(%Plug.Conn{} = conn, params), do: do_authorize(conn, params)
|
||||||
|
|
||||||
|
defp maybe_remove_token(%Plug.Conn{assigns: %{token: %{app: id}}} = conn, %App{id: id}) do
|
||||||
|
conn
|
||||||
|
end
|
||||||
|
|
||||||
|
defp maybe_remove_token(conn, _app) do
|
||||||
|
conn
|
||||||
|
|> assign(:token, nil)
|
||||||
|
end
|
||||||
|
|
||||||
defp do_authorize(%Plug.Conn{} = conn, params) do
|
defp do_authorize(%Plug.Conn{} = conn, params) do
|
||||||
app = Repo.get_by(App, client_id: params["client_id"])
|
app = Repo.get_by(App, client_id: params["client_id"])
|
||||||
|
conn = maybe_remove_token(conn, app)
|
||||||
available_scopes = (app && app.scopes) || []
|
available_scopes = (app && app.scopes) || []
|
||||||
scopes = Scopes.fetch_scopes(params, available_scopes)
|
scopes = Scopes.fetch_scopes(params, available_scopes)
|
||||||
|
|
||||||
# if we already have a token for this specific setup, we can use that
|
# if we already have a token for this specific setup, we can use that
|
||||||
with false <- Params.truthy_param?(params["force_login"]),
|
with false <- Params.truthy_param?(params["force_login"]),
|
||||||
%App{} <- app,
|
%App{} <- app,
|
||||||
{:ok, _} <- Scopes.validate(scopes, app.scopes),
|
%{assigns: %{user: %Pleroma.User{} = user}} <- conn,
|
||||||
{:ok, %Token{} = token} <- Token.get_by_app(app) do
|
{:ok, %Token{} = token} <- Token.get_preexisting_by_app_and_user(app, user),
|
||||||
|
true <- scopes == token.scopes do
|
||||||
token = Repo.preload(token, :app)
|
token = Repo.preload(token, :app)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|
|
|
@ -39,9 +39,9 @@ def get_by_token(token) do
|
||||||
|> Repo.find_resource()
|
|> Repo.find_resource()
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_by_app(%App{} = app) do
|
def get_preexisting_by_app_and_user(%App{} = app, %User{} = user) do
|
||||||
app.id
|
app.id
|
||||||
|> Query.get_unexpired_by_app()
|
|> Query.get_unexpired_by_app_and_user(user)
|
||||||
|> Repo.find_resource()
|
|> Repo.find_resource()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -23,10 +23,14 @@ def get_by_token(query \\ Token, token) do
|
||||||
from(q in query, where: q.token == ^token)
|
from(q in query, where: q.token == ^token)
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec get_unexpired_by_app(query, String.t()) :: query
|
@spec get_unexpired_by_app_and_user(query, String.t()) :: query
|
||||||
def get_unexpired_by_app(query \\ Token, app_id) do
|
def get_unexpired_by_app_and_user(query \\ Token, app_id, %Pleroma.User{id: user_id}) do
|
||||||
time = NaiveDateTime.utc_now()
|
time = NaiveDateTime.utc_now()
|
||||||
from(q in query, where: q.app_id == ^app_id and q.valid_until > ^time, limit: 1)
|
|
||||||
|
from(q in query,
|
||||||
|
where: q.app_id == ^app_id and q.valid_until > ^time and q.user_id == ^user_id,
|
||||||
|
limit: 1
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec get_by_app(query, String.t()) :: query
|
@spec get_by_app(query, String.t()) :: query
|
||||||
|
|
|
@ -470,12 +470,17 @@ test "renders authentication page if user is already authenticated but `force_lo
|
||||||
assert html_response(conn, 200) =~ ~s(type="submit")
|
assert html_response(conn, 200) =~ ~s(type="submit")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "renders authentication page if user is already authenticated but user request with another client",
|
test "reuses authentication if the user is authenticated with another client",
|
||||||
%{
|
%{
|
||||||
app: app,
|
|
||||||
conn: conn
|
conn: conn
|
||||||
} do
|
} do
|
||||||
token = insert(:oauth_token, app: app)
|
user = insert(:user)
|
||||||
|
|
||||||
|
app = insert(:oauth_app, redirect_uris: "https://redirect.url")
|
||||||
|
other_app = insert(:oauth_app, redirect_uris: "https://redirect.url")
|
||||||
|
|
||||||
|
token = insert(:oauth_token, user: user, app: app)
|
||||||
|
reusable_token = insert(:oauth_token, app: other_app, user: user)
|
||||||
|
|
||||||
conn =
|
conn =
|
||||||
conn
|
conn
|
||||||
|
@ -484,12 +489,111 @@ test "renders authentication page if user is already authenticated but user requ
|
||||||
"/oauth/authorize",
|
"/oauth/authorize",
|
||||||
%{
|
%{
|
||||||
"response_type" => "code",
|
"response_type" => "code",
|
||||||
"client_id" => "another_client_id",
|
"client_id" => other_app.client_id,
|
||||||
"redirect_uri" => OAuthController.default_redirect_uri(app),
|
"redirect_uri" => OAuthController.default_redirect_uri(other_app),
|
||||||
"scope" => "read"
|
"scope" => "read"
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
assert URI.decode(redirected_to(conn)) ==
|
||||||
|
"https://redirect.url?access_token=#{reusable_token.token}"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "does not reuse other people's tokens",
|
||||||
|
%{
|
||||||
|
conn: conn
|
||||||
|
} do
|
||||||
|
user = insert(:user)
|
||||||
|
other_user = insert(:user)
|
||||||
|
|
||||||
|
app = insert(:oauth_app, redirect_uris: "https://redirect.url")
|
||||||
|
other_app = insert(:oauth_app, redirect_uris: "https://redirect.url")
|
||||||
|
|
||||||
|
token = insert(:oauth_token, user: user, app: app)
|
||||||
|
_not_reusable_token = insert(:oauth_token, app: other_app, user: other_user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("authorization", "Bearer #{token.token}")
|
||||||
|
|> get(
|
||||||
|
"/oauth/authorize",
|
||||||
|
%{
|
||||||
|
"response_type" => "code",
|
||||||
|
"client_id" => other_app.client_id,
|
||||||
|
"redirect_uri" => OAuthController.default_redirect_uri(other_app),
|
||||||
|
"scope" => "read"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert html_response(conn, 200) =~ ~s(type="submit")
|
||||||
|
end
|
||||||
|
|
||||||
|
test "does not reuse expired tokens",
|
||||||
|
%{
|
||||||
|
conn: conn
|
||||||
|
} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
app = insert(:oauth_app, redirect_uris: "https://redirect.url")
|
||||||
|
|
||||||
|
other_app = insert(:oauth_app, redirect_uris: "https://redirect.url")
|
||||||
|
|
||||||
|
token = insert(:oauth_token, user: user, app: app)
|
||||||
|
|
||||||
|
_not_reusable_token =
|
||||||
|
insert(:oauth_token,
|
||||||
|
app: other_app,
|
||||||
|
user: user,
|
||||||
|
valid_until: NaiveDateTime.add(NaiveDateTime.utc_now(), -60 * 100)
|
||||||
|
)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("authorization", "Bearer #{token.token}")
|
||||||
|
|> get(
|
||||||
|
"/oauth/authorize",
|
||||||
|
%{
|
||||||
|
"response_type" => "code",
|
||||||
|
"client_id" => other_app.client_id,
|
||||||
|
"redirect_uri" => OAuthController.default_redirect_uri(other_app),
|
||||||
|
"scope" => "read"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert html_response(conn, 200) =~ ~s(type="submit")
|
||||||
|
end
|
||||||
|
|
||||||
|
test "does not reuse tokens with the wrong scopes",
|
||||||
|
%{
|
||||||
|
conn: conn
|
||||||
|
} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
app = insert(:oauth_app, redirect_uris: "https://redirect.url")
|
||||||
|
|
||||||
|
other_app = insert(:oauth_app, redirect_uris: "https://redirect.url")
|
||||||
|
|
||||||
|
token = insert(:oauth_token, user: user, app: app, scopes: ["read"])
|
||||||
|
|
||||||
|
_not_reusable_token =
|
||||||
|
insert(:oauth_token,
|
||||||
|
app: other_app,
|
||||||
|
user: user
|
||||||
|
)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("authorization", "Bearer #{token.token}")
|
||||||
|
|> get(
|
||||||
|
"/oauth/authorize",
|
||||||
|
%{
|
||||||
|
"response_type" => "code",
|
||||||
|
"client_id" => other_app.client_id,
|
||||||
|
"redirect_uri" => OAuthController.default_redirect_uri(other_app),
|
||||||
|
"scope" => "read write"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
assert html_response(conn, 200) =~ ~s(type="submit")
|
assert html_response(conn, 200) =~ ~s(type="submit")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue