Packages
ash_authentication
4.13.5
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/strategies/magic_link/sign_in_preparation.ex
# SPDX-FileCopyrightText: 2022 Alembic Pty Ltd
#
# SPDX-License-Identifier: MIT
defmodule AshAuthentication.Strategy.MagicLink.SignInPreparation do
@moduledoc """
Prepare a query for sign in.
"""
use Ash.Resource.Preparation
alias Ash.{Query, Resource, Resource.Preparation}
alias AshAuthentication.{Errors, Info, Jwt, TokenResource}
require Ash.Query
require Logger
import Ash.Expr
@doc false
@impl true
@spec prepare(Query.t(), keyword, Preparation.Context.t()) :: Query.t()
def prepare(query, _opts, context) do
subject_name =
query.resource
|> Info.authentication_subject_name!()
|> to_string()
with {:ok, strategy} <- Info.strategy_for_action(query.resource, query.action.name),
token when is_binary(token) <- Query.get_argument(query, strategy.token_param_name),
{:ok, %{"act" => token_action, "sub" => subject} = claims, _} <-
Jwt.verify(token, query.resource, Ash.Context.to_opts(context)),
^token_action <- to_string(strategy.sign_in_action_name),
%URI{path: ^subject_name, query: primary_key} <- URI.parse(subject) do
query
|> Query.set_context(%{private: %{ash_authentication?: true}})
|> then(fn query ->
cond do
not is_nil(primary_key) ->
primary_key =
primary_key
|> URI.decode_query()
|> Enum.to_list()
Query.filter(query, ^primary_key)
identity = claims["identity"] ->
identity_field = strategy.identity_field
Query.filter(query, ^ref(identity_field) == ^identity)
true ->
Query.do_filter(query, false)
end
end)
|> Query.after_action(fn
query, [record] ->
if strategy.single_use_token? do
token_resource = Info.authentication_tokens_token_resource!(query.resource)
:ok = TokenResource.revoke(token_resource, token, Ash.Context.to_opts(context))
end
extra_claims =
case strategy.extra_claims do
nil -> %{}
fun when is_function(fun, 4) -> fun.(record, strategy, claims, context)
end
{:ok, token, _claims} =
Jwt.token_for_user(record, extra_claims, Ash.Context.to_opts(context))
{:ok, [Resource.put_metadata(record, :token, token)]}
_query, [] ->
{:ok, []}
end)
else
_error ->
query
|> Query.do_filter(false)
|> maybe_add_error_on_invalid_token()
end
end
defp maybe_add_error_on_invalid_token(query) do
return_error =
Application.get_env(:ash_authentication, :return_error_on_invalid_magic_link_token?)
if is_nil(return_error) && Info.strategy_present?(query.resource, :audit_log) do
Logger.warning("""
return_error_on_invalid_magic_link_token? is not set and the AshAuthentication audit_log add-on is present.
The backward compatible behaviour is for the query to be successful and return an empty list if the token is
invalid. This will be logged as a success in the audit log even though the sign in failed.
The new behaviour is to return an error and log the sign in attempt as a failure in the audit log. In the
next major version this will be the default behaviour.
To suppress this warning, set return_error_on_invalid_magic_link_token? in your config:
config :ash_authentication, return_error_on_invalid_magic_link_token?: true
""")
end
if return_error do
query |> add_error_on_invalid_token()
else
query
end
end
defp add_error_on_invalid_token(query) do
{:ok, strategy} = Info.strategy_for_action(query.resource, query.action.name)
Query.after_action(query, fn query, _result ->
{:error,
Errors.AuthenticationFailed.exception(
strategy: strategy,
query: query,
caused_by:
Errors.InvalidToken.exception(
field: strategy.token_param_name,
reason: "Token did not pass verification",
type: :magic_link
)
)}
end)
end
end