Merge branch '2510-oauth-app-tokens-further-support' into 'develop'
[#2510] Improved support for app-bound OAuth tokens Closes #2510 See merge request pleroma/pleroma!3316
This commit is contained in:
commit
264cb2c77f
|
@ -3,6 +3,11 @@
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.MastodonAPI.AppController do
|
defmodule Pleroma.Web.MastodonAPI.AppController do
|
||||||
|
@moduledoc """
|
||||||
|
Controller for supporting app-related actions.
|
||||||
|
If authentication is an option, app tokens (user-unbound) must be supported.
|
||||||
|
"""
|
||||||
|
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
|
@ -17,11 +22,9 @@ defmodule Pleroma.Web.MastodonAPI.AppController do
|
||||||
plug(
|
plug(
|
||||||
:skip_plug,
|
:skip_plug,
|
||||||
[OAuthScopesPlug, EnsurePublicOrAuthenticatedPlug]
|
[OAuthScopesPlug, EnsurePublicOrAuthenticatedPlug]
|
||||||
when action == :create
|
when action in [:create, :verify_credentials]
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(OAuthScopesPlug, %{scopes: ["read"]} when action == :verify_credentials)
|
|
||||||
|
|
||||||
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||||
|
|
||||||
@local_mastodon_name "Mastodon-Local"
|
@local_mastodon_name "Mastodon-Local"
|
||||||
|
@ -44,10 +47,13 @@ def create(%{body_params: params} = conn, _params) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "GET /api/v1/apps/verify_credentials"
|
@doc """
|
||||||
def verify_credentials(%{assigns: %{user: _user, token: token}} = conn, _) do
|
GET /api/v1/apps/verify_credentials
|
||||||
with %Token{app: %App{} = app} <- Repo.preload(token, :app) do
|
Gets compact non-secret representation of the app. Supports app tokens and user tokens.
|
||||||
render(conn, "short.json", app: app)
|
"""
|
||||||
|
def verify_credentials(%{assigns: %{token: %Token{} = token}} = conn, _) do
|
||||||
|
with %{app: %App{} = app} <- Repo.preload(token, :app) do
|
||||||
|
render(conn, "compact_non_secret.json", app: app)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -34,10 +34,10 @@ def render("show.json", %{app: %App{} = app}) do
|
||||||
|> with_vapid_key()
|
|> with_vapid_key()
|
||||||
end
|
end
|
||||||
|
|
||||||
def render("short.json", %{app: %App{website: webiste, client_name: name}}) do
|
def render("compact_non_secret.json", %{app: %App{website: website, client_name: name}}) do
|
||||||
%{
|
%{
|
||||||
name: name,
|
name: name,
|
||||||
website: webiste
|
website: website
|
||||||
}
|
}
|
||||||
|> with_vapid_key()
|
|> with_vapid_key()
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,6 +3,10 @@
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.Plugs.EnsureAuthenticatedPlug do
|
defmodule Pleroma.Web.Plugs.EnsureAuthenticatedPlug do
|
||||||
|
@moduledoc """
|
||||||
|
Ensures _user_ authentication (app-bound user-unbound tokens are not accepted).
|
||||||
|
"""
|
||||||
|
|
||||||
import Plug.Conn
|
import Plug.Conn
|
||||||
import Pleroma.Web.TranslationHelpers
|
import Pleroma.Web.TranslationHelpers
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,11 @@
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug do
|
defmodule Pleroma.Web.Plugs.EnsurePublicOrAuthenticatedPlug do
|
||||||
|
@moduledoc """
|
||||||
|
Ensures instance publicity or _user_ authentication
|
||||||
|
(app-bound user-unbound tokens are accepted only if the instance is public).
|
||||||
|
"""
|
||||||
|
|
||||||
import Pleroma.Web.TranslationHelpers
|
import Pleroma.Web.TranslationHelpers
|
||||||
import Plug.Conn
|
import Plug.Conn
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,11 @@ def call(%{assigns: %{user: %User{id: user_id}} = assigns} = conn, _) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# App-bound token case (obtained with client_id and client_secret)
|
||||||
|
def call(%{assigns: %{token: %Token{user_id: nil}}} = conn, _) do
|
||||||
|
assign(conn, :user, nil)
|
||||||
|
end
|
||||||
|
|
||||||
def call(conn, _) do
|
def call(conn, _) do
|
||||||
conn
|
conn
|
||||||
|> assign(:user, nil)
|
|> assign(:user, nil)
|
||||||
|
|
|
@ -37,11 +37,13 @@ defmodule Pleroma.Web.Router do
|
||||||
plug(Pleroma.Web.Plugs.EnsureUserTokenAssignsPlug)
|
plug(Pleroma.Web.Plugs.EnsureUserTokenAssignsPlug)
|
||||||
end
|
end
|
||||||
|
|
||||||
pipeline :expect_authentication do
|
# Note: expects _user_ authentication (user-unbound app-bound tokens don't qualify)
|
||||||
|
pipeline :expect_user_authentication do
|
||||||
plug(Pleroma.Web.Plugs.ExpectAuthenticatedCheckPlug)
|
plug(Pleroma.Web.Plugs.ExpectAuthenticatedCheckPlug)
|
||||||
end
|
end
|
||||||
|
|
||||||
pipeline :expect_public_instance_or_authentication do
|
# Note: expects public instance or _user_ authentication (user-unbound tokens don't qualify)
|
||||||
|
pipeline :expect_public_instance_or_user_authentication do
|
||||||
plug(Pleroma.Web.Plugs.ExpectPublicOrAuthenticatedCheckPlug)
|
plug(Pleroma.Web.Plugs.ExpectPublicOrAuthenticatedCheckPlug)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -66,23 +68,30 @@ defmodule Pleroma.Web.Router do
|
||||||
plug(OpenApiSpex.Plug.PutApiSpec, module: Pleroma.Web.ApiSpec)
|
plug(OpenApiSpex.Plug.PutApiSpec, module: Pleroma.Web.ApiSpec)
|
||||||
end
|
end
|
||||||
|
|
||||||
pipeline :api do
|
pipeline :no_auth_or_privacy_expectations_api do
|
||||||
plug(:expect_public_instance_or_authentication)
|
|
||||||
plug(:base_api)
|
plug(:base_api)
|
||||||
plug(:after_auth)
|
plug(:after_auth)
|
||||||
plug(Pleroma.Web.Plugs.IdempotencyPlug)
|
plug(Pleroma.Web.Plugs.IdempotencyPlug)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Pipeline for app-related endpoints (no user auth checks — app-bound tokens must be supported)
|
||||||
|
pipeline :app_api do
|
||||||
|
plug(:no_auth_or_privacy_expectations_api)
|
||||||
|
end
|
||||||
|
|
||||||
|
pipeline :api do
|
||||||
|
plug(:expect_public_instance_or_user_authentication)
|
||||||
|
plug(:no_auth_or_privacy_expectations_api)
|
||||||
|
end
|
||||||
|
|
||||||
pipeline :authenticated_api do
|
pipeline :authenticated_api do
|
||||||
plug(:expect_authentication)
|
plug(:expect_user_authentication)
|
||||||
plug(:base_api)
|
plug(:no_auth_or_privacy_expectations_api)
|
||||||
plug(:after_auth)
|
|
||||||
plug(Pleroma.Web.Plugs.EnsureAuthenticatedPlug)
|
plug(Pleroma.Web.Plugs.EnsureAuthenticatedPlug)
|
||||||
plug(Pleroma.Web.Plugs.IdempotencyPlug)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
pipeline :admin_api do
|
pipeline :admin_api do
|
||||||
plug(:expect_authentication)
|
plug(:expect_user_authentication)
|
||||||
plug(:base_api)
|
plug(:base_api)
|
||||||
plug(Pleroma.Web.Plugs.AdminSecretAuthenticationPlug)
|
plug(Pleroma.Web.Plugs.AdminSecretAuthenticationPlug)
|
||||||
plug(:after_auth)
|
plug(:after_auth)
|
||||||
|
@ -432,8 +441,6 @@ defmodule Pleroma.Web.Router do
|
||||||
post("/accounts/:id/mute", AccountController, :mute)
|
post("/accounts/:id/mute", AccountController, :mute)
|
||||||
post("/accounts/:id/unmute", AccountController, :unmute)
|
post("/accounts/:id/unmute", AccountController, :unmute)
|
||||||
|
|
||||||
get("/apps/verify_credentials", AppController, :verify_credentials)
|
|
||||||
|
|
||||||
get("/conversations", ConversationController, :index)
|
get("/conversations", ConversationController, :index)
|
||||||
post("/conversations/:id/read", ConversationController, :mark_as_read)
|
post("/conversations/:id/read", ConversationController, :mark_as_read)
|
||||||
|
|
||||||
|
@ -524,6 +531,13 @@ defmodule Pleroma.Web.Router do
|
||||||
put("/settings", MastoFEController, :put_settings)
|
put("/settings", MastoFEController, :put_settings)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
scope "/api/v1", Pleroma.Web.MastodonAPI do
|
||||||
|
pipe_through(:app_api)
|
||||||
|
|
||||||
|
post("/apps", AppController, :create)
|
||||||
|
get("/apps/verify_credentials", AppController, :verify_credentials)
|
||||||
|
end
|
||||||
|
|
||||||
scope "/api/v1", Pleroma.Web.MastodonAPI do
|
scope "/api/v1", Pleroma.Web.MastodonAPI do
|
||||||
pipe_through(:api)
|
pipe_through(:api)
|
||||||
|
|
||||||
|
@ -540,8 +554,6 @@ defmodule Pleroma.Web.Router do
|
||||||
get("/instance", InstanceController, :show)
|
get("/instance", InstanceController, :show)
|
||||||
get("/instance/peers", InstanceController, :peers)
|
get("/instance/peers", InstanceController, :peers)
|
||||||
|
|
||||||
post("/apps", AppController, :create)
|
|
||||||
|
|
||||||
get("/statuses", StatusController, :index)
|
get("/statuses", StatusController, :index)
|
||||||
get("/statuses/:id", StatusController, :show)
|
get("/statuses/:id", StatusController, :show)
|
||||||
get("/statuses/:id/context", StatusController, :context)
|
get("/statuses/:id/context", StatusController, :context)
|
||||||
|
|
|
@ -12,22 +12,26 @@ defmodule Pleroma.Web.MastodonAPI.AppControllerTest do
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
|
||||||
test "apps/verify_credentials", %{conn: conn} do
|
test "apps/verify_credentials", %{conn: conn} do
|
||||||
token = insert(:oauth_token)
|
user_bound_token = insert(:oauth_token)
|
||||||
|
app_bound_token = insert(:oauth_token, user: nil)
|
||||||
|
refute app_bound_token.user
|
||||||
|
|
||||||
conn =
|
for token <- [app_bound_token, user_bound_token] do
|
||||||
conn
|
conn =
|
||||||
|> put_req_header("authorization", "Bearer #{token.token}")
|
conn
|
||||||
|> get("/api/v1/apps/verify_credentials")
|
|> put_req_header("authorization", "Bearer #{token.token}")
|
||||||
|
|> get("/api/v1/apps/verify_credentials")
|
||||||
|
|
||||||
app = Repo.preload(token, :app).app
|
app = Repo.preload(token, :app).app
|
||||||
|
|
||||||
expected = %{
|
expected = %{
|
||||||
"name" => app.client_name,
|
"name" => app.client_name,
|
||||||
"website" => app.website,
|
"website" => app.website,
|
||||||
"vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
|
"vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
|
||||||
}
|
}
|
||||||
|
|
||||||
assert expected == json_response_and_validate_schema(conn, 200)
|
assert expected == json_response_and_validate_schema(conn, 200)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
test "creates an oauth app", %{conn: conn} do
|
test "creates an oauth app", %{conn: conn} do
|
||||||
|
|
Loading…
Reference in New Issue