Fix OAuth consumer mode #668
2 changed files with 54 additions and 24 deletions
|
@ -39,6 +39,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|
||||||
action_fallback(Pleroma.Web.OAuth.FallbackController)
|
action_fallback(Pleroma.Web.OAuth.FallbackController)
|
||||||
|
|
||||||
@oob_token_redirect_uri "urn:ietf:wg:oauth:2.0:oob"
|
@oob_token_redirect_uri "urn:ietf:wg:oauth:2.0:oob"
|
||||||
|
@state_cookie_name "akkoma_oauth_state"
|
||||||
|
|
||||||
# Note: this definition is only called from error-handling methods with `conn.params` as 2nd arg
|
# Note: this definition is only called from error-handling methods with `conn.params` as 2nd arg
|
||||||
def authorize(%Plug.Conn{} = conn, %{"authorization" => _} = params) do
|
def authorize(%Plug.Conn{} = conn, %{"authorization" => _} = params) do
|
||||||
|
@ -445,7 +446,7 @@ def prepare_request(%Plug.Conn{} = conn, %{
|
||||||
|
|
||||||
# Handing the request to Ueberauth
|
# Handing the request to Ueberauth
|
||||||
conn
|
conn
|
||||||
|> put_resp_cookie("akkoma_oauth_state", state)
|
|> put_resp_cookie(@state_cookie_name, state)
|
||||||
|> redirect(to: ~p"/oauth/#{provider}")
|
|> redirect(to: ~p"/oauth/#{provider}")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -469,12 +470,18 @@ def callback(%Plug.Conn{assigns: %{ueberauth_failure: failure}} = conn, params)
|
||||||
messages = for e <- Map.get(failure, :errors, []), do: e.message
|
messages = for e <- Map.get(failure, :errors, []), do: e.message
|
||||||
message = Enum.join(messages, "; ")
|
message = Enum.join(messages, "; ")
|
||||||
|
|
||||||
|
error_message = dgettext("errors", "Failed to authenticate: %{message}.", message: message)
|
||||||
|
|
||||||
|
if params["redirect_uri"] do
|
||||||
conn
|
conn
|
||||||
|> put_flash(
|
|> put_flash(
|
||||||
:error,
|
:error,
|
||||||
dgettext("errors", "Failed to authenticate: %{message}.", message: message)
|
error_message
|
||||||
)
|
)
|
||||||
|> redirect(external: redirect_uri(conn, params["redirect_uri"]))
|
|> redirect(external: redirect_uri(conn, params["redirect_uri"]))
|
||||||
|
else
|
||||||
|
send_resp(conn, :bad_request, error_message)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def callback(%Plug.Conn{} = conn, params) do
|
def callback(%Plug.Conn{} = conn, params) do
|
||||||
|
@ -510,7 +517,7 @@ def callback(%Plug.Conn{} = conn, params) do
|
||||||
|
|
||||||
defp callback_params(%Plug.Conn{} = conn, params) do
|
defp callback_params(%Plug.Conn{} = conn, params) do
|
||||||
fetch_cookies(conn)
|
fetch_cookies(conn)
|
||||||
Map.merge(params, Jason.decode!(Map.get(conn.req_cookies, "akkoma_oauth_state", "{}")))
|
Map.merge(params, Jason.decode!(Map.get(conn.req_cookies, @state_cookie_name, "{}")))
|
||||||
end
|
end
|
||||||
|
|
||||||
def registration_details(%Plug.Conn{} = conn, %{"authorization" => auth_attrs}) do
|
def registration_details(%Plug.Conn{} = conn, %{"authorization" => auth_attrs}) do
|
||||||
|
|
|
@ -81,9 +81,7 @@ test "GET /oauth/prepare_request encodes parameters as `state` and redirects", %
|
||||||
|
|
||||||
assert html_response(conn, 302)
|
assert html_response(conn, 302)
|
||||||
|
|
||||||
redirect_query = URI.parse(redirected_to(conn)).query
|
assert {:ok, state_components} = Jason.decode(conn.resp_cookies["akkoma_oauth_state"].value)
|
||||||
assert %{"state" => state_param} = URI.decode_query(redirect_query)
|
|
||||||
assert {:ok, state_components} = Jason.decode(state_param)
|
|
||||||
|
|
||||||
expected_client_id = app.client_id
|
expected_client_id = app.client_id
|
||||||
expected_redirect_uri = app.redirect_uris
|
expected_redirect_uri = app.redirect_uris
|
||||||
|
@ -97,7 +95,7 @@ test "GET /oauth/prepare_request encodes parameters as `state` and redirects", %
|
||||||
end
|
end
|
||||||
|
|
||||||
test "with user-bound registration, GET /oauth/<provider>/callback redirects to `redirect_uri` with `code`",
|
test "with user-bound registration, GET /oauth/<provider>/callback redirects to `redirect_uri` with `code`",
|
||||||
%{app: app, conn: conn} do
|
%{app: app, conn: _} do
|
||||||
registration = insert(:registration)
|
registration = insert(:registration)
|
||||||
redirect_uri = OAuthController.default_redirect_uri(app)
|
redirect_uri = OAuthController.default_redirect_uri(app)
|
||||||
|
|
||||||
|
@ -109,15 +107,17 @@ test "with user-bound registration, GET /oauth/<provider>/callback redirects to
|
||||||
}
|
}
|
||||||
|
|
||||||
conn =
|
conn =
|
||||||
conn
|
build_conn()
|
||||||
|
|> put_req_cookie("akkoma_oauth_state", Jason.encode!(state_params))
|
||||||
|
|> Plug.Session.call(Plug.Session.init(@session_opts))
|
||||||
|
|> fetch_session()
|
||||||
|> assign(:ueberauth_auth, %{provider: registration.provider, uid: registration.uid})
|
|> assign(:ueberauth_auth, %{provider: registration.provider, uid: registration.uid})
|
||||||
|> get(
|
|> get(
|
||||||
"/oauth/twitter/callback",
|
"/oauth/twitter/callback",
|
||||||
%{
|
%{
|
||||||
"oauth_token" => "G-5a3AAAAAAAwMH9AAABaektfSM",
|
"oauth_token" => "G-5a3AAAAAAAwMH9AAABaektfSM",
|
||||||
"oauth_verifier" => "QZl8vUqNvXMTKpdmUnGejJxuHG75WWWs",
|
"oauth_verifier" => "QZl8vUqNvXMTKpdmUnGejJxuHG75WWWs",
|
||||||
"provider" => "twitter",
|
"provider" => "twitter"
|
||||||
"state" => Jason.encode!(state_params)
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -162,15 +162,42 @@ test "with user-unbound registration, GET /oauth/<provider>/callback renders reg
|
||||||
|
|
||||||
test "on authentication error, GET /oauth/<provider>/callback redirects to `redirect_uri`", %{
|
test "on authentication error, GET /oauth/<provider>/callback redirects to `redirect_uri`", %{
|
||||||
app: app,
|
app: app,
|
||||||
conn: conn
|
conn: _
|
||||||
} do
|
} do
|
||||||
state_params = %{
|
state_params = %{
|
||||||
"scope" => Enum.join(app.scopes, " "),
|
"scope" => Enum.join(app.scopes, " "),
|
||||||
"client_id" => app.client_id,
|
"client_id" => app.client_id,
|
||||||
"redirect_uri" => OAuthController.default_redirect_uri(app),
|
"redirect_uri" => OAuthController.default_redirect_uri(app)
|
||||||
"state" => ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> put_req_cookie("akkoma_oauth_state", Jason.encode!(state_params))
|
||||||
|
|> Plug.Session.call(Plug.Session.init(@session_opts))
|
||||||
|
|> fetch_session()
|
||||||
|
|> assign(:ueberauth_failure, %{errors: [%{message: "(error description)"}]})
|
||||||
|
|> get(
|
||||||
|
"/oauth/twitter/callback",
|
||||||
|
%{
|
||||||
|
"oauth_token" => "G-5a3AAAAAAAwMH9AAABaektfSM",
|
||||||
|
"oauth_verifier" => "QZl8vUqNvXMTKpdmUnGejJxuHG75WWWs",
|
||||||
|
"provider" => "twitter",
|
||||||
|
"state" => ""
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert html_response(conn, 302)
|
||||||
|
assert redirected_to(conn) == app.redirect_uris
|
||||||
|
|
||||||
|
assert Phoenix.Flash.get(conn.assigns.flash, :error) ==
|
||||||
|
"Failed to authenticate: (error description)."
|
||||||
|
end
|
||||||
|
|
||||||
|
test "on authentication error with no prior state, GET /oauth/<provider>/callback returns 400",
|
||||||
|
%{
|
||||||
|
app: _,
|
||||||
|
conn: conn
|
||||||
|
} do
|
||||||
conn =
|
conn =
|
||||||
conn
|
conn
|
||||||
|> assign(:ueberauth_failure, %{errors: [%{message: "(error description)"}]})
|
|> assign(:ueberauth_failure, %{errors: [%{message: "(error description)"}]})
|
||||||
|
@ -180,15 +207,11 @@ test "on authentication error, GET /oauth/<provider>/callback redirects to `redi
|
||||||
"oauth_token" => "G-5a3AAAAAAAwMH9AAABaektfSM",
|
"oauth_token" => "G-5a3AAAAAAAwMH9AAABaektfSM",
|
||||||
"oauth_verifier" => "QZl8vUqNvXMTKpdmUnGejJxuHG75WWWs",
|
"oauth_verifier" => "QZl8vUqNvXMTKpdmUnGejJxuHG75WWWs",
|
||||||
"provider" => "twitter",
|
"provider" => "twitter",
|
||||||
"state" => Jason.encode!(state_params)
|
"state" => ""
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
assert html_response(conn, 302)
|
assert response(conn, 400)
|
||||||
assert redirected_to(conn) == app.redirect_uris
|
|
||||||
|
|
||||||
assert Phoenix.Flash.get(conn.assigns.flash, :error) ==
|
|
||||||
"Failed to authenticate: (error description)."
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "GET /oauth/registration_details renders registration details form", %{
|
test "GET /oauth/registration_details renders registration details form", %{
|
||||||
|
|
Loading…
Reference in a new issue