Disable current push device on logout
This commit is contained in:
parent
d18470f77a
commit
e7cfddb587
|
|
@ -192,6 +192,26 @@ document.addEventListener("submit", event => {
|
|||
|
||||
const action = new URL(form.action, window.location.origin)
|
||||
if (action.origin === window.location.origin && action.pathname === "/users/log-out") {
|
||||
const deviceId = window.localStorage.getItem("wnh.push.server-device-id")
|
||||
|
||||
if (deviceId) {
|
||||
const input = document.createElement("input")
|
||||
input.type = "hidden"
|
||||
input.name = "push_device_id"
|
||||
input.value = deviceId
|
||||
form.append(input)
|
||||
window.localStorage.removeItem("wnh.push.server-device-id")
|
||||
}
|
||||
|
||||
if (window.WhoNeedHelpAndroid?.postMessage) {
|
||||
window.WhoNeedHelpAndroid.postMessage(JSON.stringify({action: "disable_push"}))
|
||||
} else if ("serviceWorker" in navigator) {
|
||||
navigator.serviceWorker.ready
|
||||
.then(registration => registration.pushManager.getSubscription())
|
||||
.then(subscription => subscription?.unsubscribe())
|
||||
.catch(error => console.warn("Browser push unsubscribe on logout failed", error))
|
||||
}
|
||||
|
||||
window.WhoNeedHelpAndroid?.postMessage?.(JSON.stringify({action: "google_sign_out"}))
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@ defmodule WhoNeedHelpWeb.UserSessionController do
|
|||
|
||||
require Logger
|
||||
|
||||
alias WhoNeedHelp.{Accounts, GoogleAuth}
|
||||
alias WhoNeedHelp.{Accounts, GoogleAuth, Notifications}
|
||||
alias WhoNeedHelp.Accounts.Scope
|
||||
alias WhoNeedHelp.Trust.RateLimiter
|
||||
alias WhoNeedHelpWeb.{GoogleAuthPending, UserAuth}
|
||||
|
||||
|
|
@ -125,12 +126,26 @@ defmodule WhoNeedHelpWeb.UserSessionController do
|
|||
|> render(:new, form: Phoenix.Component.to_form(%{}, as: "user"))
|
||||
end
|
||||
|
||||
def delete(conn, _params) do
|
||||
def delete(conn, params) do
|
||||
conn
|
||||
|> maybe_disable_logout_push_device(params)
|
||||
|> put_flash(:info, gettext("Logged out successfully."))
|
||||
|> UserAuth.log_out_user()
|
||||
end
|
||||
|
||||
defp maybe_disable_logout_push_device(
|
||||
%{assigns: %{current_scope: %Scope{user: %Accounts.User{}} = scope}} = conn,
|
||||
%{"push_device_id" => device_id}
|
||||
)
|
||||
when is_binary(device_id) do
|
||||
# The context scopes the lookup to the signed-in user. Missing, malformed, or
|
||||
# another user's device IDs must never prevent the session from being closed.
|
||||
_result = Notifications.disable_device(scope, device_id)
|
||||
conn
|
||||
end
|
||||
|
||||
defp maybe_disable_logout_push_device(conn, _params), do: conn
|
||||
|
||||
defp invalid_password_response(conn, user_params) do
|
||||
# Keep the response identical for unknown accounts and incorrect passwords.
|
||||
conn
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ defmodule WhoNeedHelpWeb.UserSessionControllerTest do
|
|||
|
||||
import WhoNeedHelp.AccountsFixtures
|
||||
import Swoosh.TestAssertions
|
||||
alias WhoNeedHelp.Accounts
|
||||
alias WhoNeedHelp.{Accounts, Notifications}
|
||||
|
||||
setup do
|
||||
%{unconfirmed_user: unconfirmed_user_fixture(), user: user_fixture()}
|
||||
|
|
@ -273,5 +273,56 @@ defmodule WhoNeedHelpWeb.UserSessionControllerTest do
|
|||
refute get_session(conn, :user_token)
|
||||
assert Phoenix.Flash.get(conn.assigns.flash, :info) =~ "Logged out successfully"
|
||||
end
|
||||
|
||||
test "disables only the current browser push device", %{conn: conn, user: user} do
|
||||
scope = Accounts.Scope.for_user(user)
|
||||
{:ok, current_device} = Notifications.register_device(scope, push_device_attrs())
|
||||
{:ok, remaining_device} = Notifications.register_device(scope, push_device_attrs())
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> log_in_user(user)
|
||||
|> delete(~p"/users/log-out", %{"push_device_id" => current_device.id})
|
||||
|
||||
assert redirected_to(conn) == ~p"/"
|
||||
assert Enum.map(Notifications.list_devices(scope), & &1.id) == [remaining_device.id]
|
||||
end
|
||||
|
||||
test "cannot disable another user's push device during logout", %{conn: conn, user: user} do
|
||||
other_user = user_fixture()
|
||||
other_scope = Accounts.Scope.for_user(other_user)
|
||||
{:ok, other_device} = Notifications.register_device(other_scope, push_device_attrs())
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> log_in_user(user)
|
||||
|> delete(~p"/users/log-out", %{"push_device_id" => other_device.id})
|
||||
|
||||
assert redirected_to(conn) == ~p"/"
|
||||
assert [%{id: device_id}] = Notifications.list_devices(other_scope)
|
||||
assert device_id == other_device.id
|
||||
end
|
||||
|
||||
test "malformed push device ID does not prevent logout", %{conn: conn, user: user} do
|
||||
conn =
|
||||
conn
|
||||
|> log_in_user(user)
|
||||
|> delete(~p"/users/log-out", %{"push_device_id" => "not-a-device-id"})
|
||||
|
||||
assert redirected_to(conn) == ~p"/"
|
||||
refute get_session(conn, :user_token)
|
||||
end
|
||||
end
|
||||
|
||||
defp push_device_attrs do
|
||||
%{
|
||||
"platform" => "web",
|
||||
"provider" => "web_push",
|
||||
"token" => "https://push.example/subscriptions/#{Ecto.UUID.generate()}",
|
||||
"installation_id" => Ecto.UUID.generate(),
|
||||
"p256dh" => "test-public-key",
|
||||
"auth_secret" => "test-auth-secret",
|
||||
"device_label" => "Test browser"
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user