Current section

Files

Jump to
ash_authentication lib ash_authentication strategies password strategy.ex
Raw

lib/ash_authentication/strategies/password/strategy.ex

# SPDX-FileCopyrightText: 2022 Alembic Pty Ltd
#
# SPDX-License-Identifier: MIT
defimpl AshAuthentication.Strategy, for: AshAuthentication.Strategy.Password do
@moduledoc """
Implementation of `AshAuthentication.Strategy` for
`AshAuthentication.Strategy.Password`.
Because the password strategy can optionally provide password reset
functionality it provides more than the usual number of routes, actions, etc.
"""
alias Ash.Resource
alias AshAuthentication.{Info, Strategy, Strategy.Password}
alias Plug.Conn
import AshAuthentication.Utils
@typedoc """
The possible request phases for the password strategy.
Only the first two will be used if password resets are disabled.
"""
@type phase :: :register | :sign_in | :reset_request | :reset | :sign_in_with_token
@doc false
@spec name(Password.t()) :: atom
def name(strategy), do: strategy.name
@doc false
@spec phases(Password.t()) :: [phase]
def phases(strategy) do
[]
|> maybe_append(
strategy.sign_in_tokens_enabled? && strategy.sign_in_enabled?,
:sign_in_with_token
)
|> maybe_append(strategy.registration_enabled?, :register)
|> maybe_append(strategy.sign_in_enabled?, :sign_in)
|> maybe_concat(strategy.resettable, [:reset_request, :reset])
end
@doc false
@spec actions(Password.t()) :: [phase]
def actions(strategy), do: phases(strategy)
@doc false
@spec method_for_phase(Password.t(), phase) :: Strategy.http_method()
def method_for_phase(_, phase) when phase in [:sign_in_with_token], do: :get
def method_for_phase(_, _), do: :post
@doc """
Return a list of routes for use by the strategy.
"""
@spec routes(Password.t()) :: [Strategy.route()]
def routes(strategy) do
subject_name = Info.authentication_subject_name!(strategy.resource)
strategy
|> phases()
|> Enum.map(fn phase ->
path =
[subject_name, strategy.name, phase]
|> Enum.map(&to_string/1)
|> Path.join()
{"/#{path}", phase}
end)
end
@doc """
Handle HTTP requests.
"""
@spec plug(Password.t(), phase, Conn.t()) :: Conn.t()
def plug(strategy, :register, conn), do: Password.Plug.register(conn, strategy)
def plug(strategy, :sign_in, conn), do: Password.Plug.sign_in(conn, strategy)
def plug(strategy, :reset_request, conn), do: Password.Plug.reset_request(conn, strategy)
def plug(strategy, :reset, conn), do: Password.Plug.reset(conn, strategy)
def plug(strategy, :sign_in_with_token, conn),
do: Password.Plug.sign_in_with_token(conn, strategy)
@doc """
Perform actions.
"""
@spec action(Password.t(), phase, map, keyword) :: {:ok, Resource.record()} | {:error, any}
def action(strategy, :register, params, options),
do: Password.Actions.register(strategy, params, options)
def action(strategy, :sign_in, params, options),
do: Password.Actions.sign_in(strategy, params, options)
def action(strategy, :reset_request, params, options),
do: Password.Actions.reset_request(strategy, params, options)
def action(strategy, :reset, params, options),
do: Password.Actions.reset(strategy, params, options)
def action(strategy, :sign_in_with_token, params, options),
do: Password.Actions.sign_in_with_token(strategy, params, options)
@doc false
@spec tokens_required?(Password.t()) :: boolean
def tokens_required?(strategy) when strategy.sign_in_tokens_enabled?, do: true
def tokens_required?(strategy) when is_map(strategy.resettable), do: true
def tokens_required?(_), do: false
end