Packages
ash_authentication_phoenix
1.4.8
3.0.0-rc.9
3.0.0-rc.8
3.0.0-rc.7
3.0.0-rc.6
3.0.0-rc.4
3.0.0-rc.3
3.0.0-rc.2
3.0.0-rc.1
3.0.0-rc.0
2.17.2
2.17.1
2.17.0
2.16.0
2.15.0
2.14.1
2.14.0
2.13.1
2.13.0
2.12.2
2.12.1
2.12.0
2.11.0
2.10.5
2.10.4
2.10.3
2.10.2
2.10.1
2.10.0
2.9.0
2.8.0
2.7.0
2.6.3
2.6.2
2.6.1
2.6.0
2.5.4
2.5.3
2.5.2
2.5.1
2.5.0
2.4.8
2.4.7
2.4.6
2.4.5
2.4.4
2.4.3
2.4.2
2.4.1
2.4.0
2.3.0
2.2.1
2.2.0
2.1.11
2.1.10
2.1.9
2.1.8
2.1.7
2.1.6
2.1.5
2.1.4
2.1.3
2.1.2
2.1.1
2.1.0
2.0.2
2.0.1
2.0.0
2.0.0-rc.2
2.0.0-rc.1
2.0.0-rc.0
1.9.4
1.9.3
1.9.2
1.9.1
1.9.0
1.8.7
1.8.6
1.8.5
1.8.4
1.8.3
1.8.2
1.8.1
1.8.0
1.7.3
1.7.2
1.7.1
1.7.0
1.6.6
1.6.5
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0
1.5.1
1.5.0
1.4.8
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.1
1.3.0
1.2.0
1.1.0
1.0.1
1.0.0
Phoenix integration for Ash Authentication
Security advisory:
This version has known vulnerabilities.
View advisories
Current section
Files
Jump to
Current section
Files
lib/ash_authentication_phoenix/live_session.ex
defmodule AshAuthentication.Phoenix.LiveSession do
@moduledoc """
Ensures that any loaded users which are present in a conn's assigns are also
present in a live view socket's assigns.
Typical usage is via the `ash_authentication_live_session/2` macro, but can also
manually called like so:
```elixir
scope "/", ExampleWeb do
pipe_through(:browser)
live_session :authenticated, on_mount: LiveSession, session: {LiveSession, :session, []} do
live "/", ExampleLive
end
end
```
"""
import Phoenix.Component, only: [assign: 3]
import AshAuthentication.Phoenix.Components.Helpers
alias AshAuthentication.{Info, Phoenix.LiveSession}
alias Phoenix.LiveView.Socket
@doc """
Generate a live session wherein all subject assigns are copied from the conn
into the socket.
"""
@spec ash_authentication_live_session(atom, opts :: Keyword.t()) :: Macro.t()
defmacro ash_authentication_live_session(session_name \\ :ash_authentication, opts \\ [],
do: block
) do
quote do
on_mount = LiveSession
session = {LiveSession, :generate_session, []}
opts =
unquote(opts)
|> Keyword.update(:on_mount, on_mount, &[on_mount | List.wrap(&1)])
|> Keyword.update(:session, session, &[session | List.wrap(&1)])
live_session unquote(session_name), opts do
unquote(block)
end
end
end
@doc """
Inspects the incoming session for any subject_name -> subject values and loads
them into the socket's assigns.
For example a session containing `{"user",
"user?id=aa6c179c-ee75-4d49-8796-528c2981b396"}` becomes an assign called
`current_user` with the loaded user as the value.
"""
@spec on_mount(atom, %{required(String.t()) => any}, %{required(String.t()) => any}, Socket.t()) ::
{:cont | :halt, Socket.t()}
def on_mount(:default, _params, session, socket) do
otp_app = otp_app_from_socket(socket)
resources =
otp_app
|> AshAuthentication.authenticated_resources()
|> Stream.map(&{to_string(Info.authentication_subject_name!(&1)), &1})
|> Map.new()
socket =
session
|> Enum.reduce(socket, fn {key, value}, socket ->
with {:ok, resource} <- Map.fetch(resources, key),
{:ok, user} <- AshAuthentication.subject_to_user(value, resource),
{:ok, subject_name} <- Info.authentication_subject_name(resource) do
assign(socket, String.to_existing_atom("current_#{subject_name}"), user)
else
_ -> socket
end
end)
{:cont, socket}
end
def on_mount(_, _params, _session, socket), do: {:cont, socket}
@doc """
Supplements the session with any `current_X` assigns which are authenticated
resource records from the conn.
"""
@spec generate_session(Plug.Conn.t()) :: %{required(String.t()) => String.t()}
def generate_session(conn) do
otp_app = conn.private.phoenix_endpoint.config(:otp_app)
otp_app
|> AshAuthentication.authenticated_resources()
|> Stream.map(&{to_string(Info.authentication_subject_name!(&1)), &1})
|> Enum.reduce(%{}, fn {subject_name, resource}, session ->
case Map.fetch(conn.assigns, String.to_existing_atom("current_#{subject_name}")) do
{:ok, user} when is_struct(user, resource) ->
Map.put(session, subject_name, AshAuthentication.user_to_subject(user))
_ ->
session
end
end)
end
end