Packages
ash_authentication
4.3.2
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/password/sign_in_preparation.ex
defmodule AshAuthentication.Strategy.Password.SignInPreparation do
@moduledoc """
Prepare a query for sign in
This preparation performs two jobs, one before the query executes and one
after.
Firstly, it constrains the query to match the identity field passed to the
action.
Secondly, it validates the supplied password using the configured hash
provider, and if correct allows the record to be returned, otherwise returns
an authentication failed error.
"""
use Ash.Resource.Preparation
alias AshAuthentication.{Errors.AuthenticationFailed, Info, Jwt}
alias Ash.{Error.Unknown, Query, Resource.Preparation}
require Ash.Query
@doc false
@impl true
@spec prepare(Query.t(), keyword, Preparation.Context.t()) :: Query.t()
def prepare(query, options, context) do
{:ok, strategy} = Info.find_strategy(query, context, options)
identity_field = strategy.identity_field
identity = Query.get_argument(query, identity_field)
query =
if is_nil(identity) do
# This will fail due to the argument being `nil`, so this is just a formality
Query.filter(query, false)
else
Query.filter(query, ^ref(identity_field) == ^identity)
end
query
|> check_sign_in_token_configuration(strategy)
|> Query.before_action(fn query ->
Ash.Query.ensure_selected(query, [strategy.hashed_password_field])
end)
|> Query.after_action(fn
query, [record] when is_binary(:erlang.map_get(strategy.hashed_password_field, record)) ->
password = Query.get_argument(query, strategy.password_field)
if strategy.hash_provider.valid?(
password,
Map.get(record, strategy.hashed_password_field)
) do
token_type = query.context[:token_type] || :user
{:ok, [maybe_generate_token(token_type, record, strategy)]}
else
{:error,
AuthenticationFailed.exception(
strategy: strategy,
query: query,
caused_by: %{
module: __MODULE__,
action: query.action,
resource: query.resource,
message: "Password is not valid"
}
)}
end
query, [] ->
strategy.hash_provider.simulate()
{:error,
AuthenticationFailed.exception(
strategy: strategy,
query: query,
caused_by: %{
module: __MODULE__,
strategy: strategy,
action: :sign_in,
message: "Query returned no users"
}
)}
query, users when is_list(users) ->
strategy.hash_provider.simulate()
{:error,
AuthenticationFailed.exception(
strategy: strategy,
query: query,
caused_by: %{
module: __MODULE__,
strategy: strategy,
action: :sign_in,
message: "Query returned too many users"
}
)}
end)
end
defp check_sign_in_token_configuration(query, strategy)
when query.context.token_type == :sign_in and not strategy.sign_in_tokens_enabled? do
Query.add_error(
query,
Unknown.exception(
message: """
Invalid configuration detected. A sign in token was requested for the #{strategy.name} strategy on #{inspect(query.resource)}, but that strategy
does not support sign in tokens. See `sign_in_tokens_enabled?` for more.
"""
)
)
end
defp check_sign_in_token_configuration(query, _) do
query
end
defp maybe_generate_token(purpose, record, strategy) when purpose in [:user, :sign_in] do
if AshAuthentication.Info.authentication_tokens_enabled?(record.__struct__) do
generate_token(purpose, record, strategy)
else
record
end
end
defp generate_token(:sign_in, record, strategy) when strategy.sign_in_tokens_enabled? do
{:ok, token, _claims} =
Jwt.token_for_user(record, %{"purpose" => "sign_in"},
token_lifetime: strategy.sign_in_token_lifetime,
purpose: :sign_in
)
Ash.Resource.put_metadata(record, :token, token)
end
defp generate_token(purpose, record, _strategy) do
{:ok, token, _claims} = Jwt.token_for_user(record, %{"purpose" => to_string(purpose)})
Ash.Resource.put_metadata(record, :token, token)
end
end