Packages
ash_authentication
4.5.4
5.0.0-rc.12
5.0.0-rc.11
5.0.0-rc.10
5.0.0-rc.9
5.0.0-rc.8
5.0.0-rc.7
5.0.0-rc.6
5.0.0-rc.5
5.0.0-rc.4
5.0.0-rc.3
5.0.0-rc.2
5.0.0-rc.1
5.0.0-rc.0
4.14.1
4.14.0
4.13.7
4.13.6
4.13.5
4.13.4
4.13.3
4.13.2
retired
4.13.1
retired
4.13.0
4.12.0
4.11.0
4.10.0
4.9.9
4.9.8
4.9.7
4.9.6
4.9.5
4.9.4
4.9.3
4.9.2
4.9.1
4.9.0
4.8.7
4.8.6
4.8.5
4.8.3
4.8.2
4.8.1
4.8.0
4.7.6
4.7.5
4.7.4
4.7.3
4.7.2
4.7.1
4.7.0
4.6.4
4.6.3
4.6.2
4.6.1
4.6.0
4.5.6
4.5.5
4.5.4
4.5.3
4.5.2
4.5.1
4.5.0
4.4.9
4.4.8
4.4.7
4.4.6
4.4.5
4.4.4
4.4.3
4.4.2
4.4.1
4.4.0
4.3.12
4.3.11
4.3.10
4.3.9
4.3.8
4.3.7
4.3.6
4.3.5
4.3.4
4.3.3
4.3.2
4.3.1
4.3.0
4.2.7
4.2.6
4.2.5
4.2.4
4.2.3
4.2.2
4.2.1
4.2.0
4.1.0
4.0.4
4.0.3
4.0.2
4.0.1
4.0.0
4.0.0-rc.6
4.0.0-rc.5
4.0.0-rc.3
4.0.0-rc.2
4.0.0-rc.1
4.0.0-rc.0
3.12.4
3.12.3
3.12.2
3.12.1
3.12.0
3.11.16
3.11.15
3.11.14
3.11.13
3.11.12
3.11.11
3.11.10
3.11.9
3.11.8
3.11.7
3.11.6
3.11.5
3.11.4
3.11.3
3.11.2
3.11.1
3.11.0
3.10.8
3.10.7
3.10.6
3.10.5
3.10.4
3.10.3
3.10.2
3.10.1
3.10.0
3.9.6
3.9.5
3.9.4
3.9.3
3.9.2
3.9.1
3.9.0
3.8.0
3.7.9
3.7.8
3.7.6
3.7.5
3.7.4
3.7.3
3.7.2
3.7.1
3.7.0
3.6.1
3.6.0
3.5.3
3.5.2
3.5.1
3.5.0
3.3.1
3.3.0
3.2.2
3.2.1
3.2.0
3.1.0
3.0.3
Authentication extension for the Ash Framework.
Security advisory:
This version has known vulnerabilities.
View advisories
Current section
Files
Jump to
Current section
Files
lib/ash_authentication/add_ons/confirmation/actions.ex
defmodule AshAuthentication.AddOn.Confirmation.Actions do
@moduledoc """
Actions for the confirmation add-on.
Provides the code interface for working with resources via confirmation.
"""
alias Ash.{Changeset, Error.Framework.AssumptionFailed, Query, Resource}
alias AshAuthentication.{
AddOn.Confirmation,
Errors.InvalidToken,
Info,
Jwt,
Strategy,
TokenResource
}
@doc """
Attempt to confirm a user.
"""
@spec confirm(Confirmation.t(), map, keyword) :: {:ok, Resource.record()} | {:error, any}
def confirm(strategy, params, opts \\ []) do
with {:ok, token} <- Map.fetch(params, "confirm"),
{:ok, %{"sub" => subject}, _} <- Jwt.verify(token, strategy.resource, opts),
{:ok, user} <- AshAuthentication.subject_to_user(subject, strategy.resource, opts),
{:ok, token_resource} <- Info.authentication_tokens_token_resource(strategy.resource) do
opts =
opts
|> Keyword.put_new_lazy(:domain, fn ->
Info.domain!(strategy.resource)
end)
user
|> Changeset.new()
|> Changeset.set_context(%{
private: %{
ash_authentication?: true
}
})
|> Changeset.for_update(strategy.confirm_action_name, params)
|> Changeset.after_action(fn _changeset, record ->
case TokenResource.revoke(token_resource, token) do
:ok -> {:ok, record}
{:error, reason} -> {:error, reason}
end
end)
|> Ash.update(opts)
else
:error -> {:error, InvalidToken.exception(type: :confirmation)}
{:error, reason} -> {:error, reason}
end
end
@doc """
Store changes in the tokens resource for later re-use.
"""
@spec store_changes(Confirmation.t(), String.t(), Changeset.t(), keyword) :: :ok | {:error, any}
def store_changes(strategy, token, changeset, opts \\ []) do
changes =
strategy.monitor_fields
|> Stream.filter(&Changeset.changing_attribute?(changeset, &1))
|> Enum.reduce_while({:ok, %{}}, fn field, {:ok, acc} ->
if Keyword.has_key?(changeset.atomics, field) do
{:halt,
{:error,
"Cannot store the changes to the field #{field} because it is being updated atomically."}}
else
{:cont,
{:ok, Map.put(acc, field, to_string(Changeset.get_attribute(changeset, field)))}}
end
end)
with {:ok, changes} <- changes,
{:ok, token_resource} <- Info.authentication_tokens_token_resource(strategy.resource),
{:ok, domain} <- TokenResource.Info.token_domain(token_resource),
opts <- opts |> Keyword.put(:upsert?, true) |> Keyword.put_new(:domain, domain),
{:ok, store_changes_action} <-
TokenResource.Info.token_confirmation_store_changes_action_name(token_resource),
{:ok, _token_record} <-
token_resource
|> Changeset.new()
|> Changeset.set_context(%{
private: %{
ash_authentication?: true
}
})
|> Changeset.for_create(store_changes_action, %{
token: token,
extra_data: changes,
purpose: to_string(Strategy.name(strategy))
})
|> Ash.create(opts) do
:ok
else
{:error, reason} ->
{:error, reason}
:error ->
{:error,
AssumptionFailed.exception(
message: "Configuration error storing confirmation token data"
)}
end
end
@doc """
Get changes from the tokens resource for application.
"""
@spec get_changes(Confirmation.t(), String.t(), keyword) :: {:ok, map} | :error
def get_changes(strategy, jti, opts \\ []) do
with {:ok, token_resource} <- Info.authentication_tokens_token_resource(strategy.resource),
opts <-
Keyword.put_new_lazy(opts, :domain, fn ->
TokenResource.Info.token_domain!(token_resource)
end),
{:ok, get_changes_action} <-
TokenResource.Info.token_confirmation_get_changes_action_name(token_resource),
{:ok, [token_record]} <-
token_resource
|> Query.new()
|> Query.set_context(%{
private: %{
ash_authentication?: true
}
})
|> Query.set_context(%{strategy: strategy})
|> Query.for_read(get_changes_action, %{"jti" => jti})
|> Ash.read(opts) do
changes =
strategy.monitor_fields
|> Stream.map(&to_string/1)
|> Stream.map(&{&1, Map.get(token_record.extra_data, &1)})
|> Stream.reject(&is_nil(elem(&1, 1)))
|> Map.new()
{:ok, changes}
else
_ -> :error
end
end
end