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)
|
const action = new URL(form.action, window.location.origin)
|
||||||
if (action.origin === window.location.origin && action.pathname === "/users/log-out") {
|
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"}))
|
window.WhoNeedHelpAndroid?.postMessage?.(JSON.stringify({action: "google_sign_out"}))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,8 @@ defmodule WhoNeedHelpWeb.UserSessionController do
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
alias WhoNeedHelp.{Accounts, GoogleAuth}
|
alias WhoNeedHelp.{Accounts, GoogleAuth, Notifications}
|
||||||
|
alias WhoNeedHelp.Accounts.Scope
|
||||||
alias WhoNeedHelp.Trust.RateLimiter
|
alias WhoNeedHelp.Trust.RateLimiter
|
||||||
alias WhoNeedHelpWeb.{GoogleAuthPending, UserAuth}
|
alias WhoNeedHelpWeb.{GoogleAuthPending, UserAuth}
|
||||||
|
|
||||||
|
|
@ -125,12 +126,26 @@ defmodule WhoNeedHelpWeb.UserSessionController do
|
||||||
|> render(:new, form: Phoenix.Component.to_form(%{}, as: "user"))
|
|> render(:new, form: Phoenix.Component.to_form(%{}, as: "user"))
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete(conn, _params) do
|
def delete(conn, params) do
|
||||||
conn
|
conn
|
||||||
|
|> maybe_disable_logout_push_device(params)
|
||||||
|> put_flash(:info, gettext("Logged out successfully."))
|
|> put_flash(:info, gettext("Logged out successfully."))
|
||||||
|> UserAuth.log_out_user()
|
|> UserAuth.log_out_user()
|
||||||
end
|
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
|
defp invalid_password_response(conn, user_params) do
|
||||||
# Keep the response identical for unknown accounts and incorrect passwords.
|
# Keep the response identical for unknown accounts and incorrect passwords.
|
||||||
conn
|
conn
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ defmodule WhoNeedHelpWeb.UserSessionControllerTest do
|
||||||
|
|
||||||
import WhoNeedHelp.AccountsFixtures
|
import WhoNeedHelp.AccountsFixtures
|
||||||
import Swoosh.TestAssertions
|
import Swoosh.TestAssertions
|
||||||
alias WhoNeedHelp.Accounts
|
alias WhoNeedHelp.{Accounts, Notifications}
|
||||||
|
|
||||||
setup do
|
setup do
|
||||||
%{unconfirmed_user: unconfirmed_user_fixture(), user: user_fixture()}
|
%{unconfirmed_user: unconfirmed_user_fixture(), user: user_fixture()}
|
||||||
|
|
@ -273,5 +273,56 @@ defmodule WhoNeedHelpWeb.UserSessionControllerTest do
|
||||||
refute get_session(conn, :user_token)
|
refute get_session(conn, :user_token)
|
||||||
assert Phoenix.Flash.get(conn.assigns.flash, :info) =~ "Logged out successfully"
|
assert Phoenix.Flash.get(conn.assigns.flash, :info) =~ "Logged out successfully"
|
||||||
end
|
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
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user