defmodule AshAuthentication.Strategy.OAuth2.Actions do
@moduledoc """
Actions for the oauth2 strategy.
Provides the code interface for working with resources via an OAuth2 strategy.
"""
alias Ash.{Changeset, Error.Invalid.NoSuchAction, Query, Resource}
alias AshAuthentication.{Errors, Info, Strategy.OAuth2}
@doc """
Attempt to sign in a user.
"""
@spec sign_in(OAuth2.t(), map, keyword) :: {:ok, Resource.record()} | {:error, any}
def sign_in(%OAuth2{} = strategy, _params, _options) when strategy.registration_enabled?,
do:
{:error,
NoSuchAction.exception(
resource: strategy.resource,
action: strategy.sign_in_action_name,
type: :read
)}
def sign_in(%OAuth2{} = strategy, params, options) do
api = Info.authentication_api!(strategy.resource)
strategy.resource
|> Query.new()
|> Query.set_context(%{
private: %{
ash_authentication?: true
}
})
|> Query.for_read(strategy.sign_in_action_name, params)
|> api.read(options)
|> case do
{:ok, [user]} ->
{:ok, user}
{:ok, []} ->
{:error,
Errors.AuthenticationFailed.exception(
strategy: strategy,
caused_by: %{
module: __MODULE__,
strategy: strategy,
action: :sign_in,
message: "Query returned no users"
}
)}
{:ok, _users} ->
{:error,
Errors.AuthenticationFailed.exception(
strategy: strategy,
caused_by: %{
module: __MODULE__,
strategy: strategy,
action: :sign_in,
message: "Query returned too many users"
}
)}
{:error, error} when is_struct(error, Errors.AuthenticationFailed) ->
{:error, error}
{:error, error} when is_exception(error) ->
{:error,
Errors.AuthenticationFailed.exception(
strategy: strategy,
caused_by: error
)}
{:error, error} ->
{:error,
Errors.AuthenticationFailed.exception(
strategy: strategy,
caused_by: %{
module: __MODULE__,
strategy: strategy,
action: :sign_in,
message: "Query returned error: #{inspect(error)}"
}
)}
end
end
@doc """
Attempt to register a new user.
"""
@spec register(OAuth2.t(), map, keyword) :: {:ok, Resource.record()} | {:error, any}
def register(%OAuth2{} = strategy, params, options) when strategy.registration_enabled? do
api = Info.authentication_api!(strategy.resource)
action = Resource.Info.action(strategy.resource, strategy.register_action_name, :create)
strategy.resource
|> Changeset.new()
|> Changeset.set_context(%{
private: %{
ash_authentication?: true
}
})
|> Changeset.for_create(strategy.register_action_name, params,
upsert?: true,
upsert_identity: action.upsert_identity
)
|> api.create(options)
end
def register(%OAuth2{} = strategy, _params, _options),
do:
{:error,
NoSuchAction.exception(
resource: strategy.resource,
action: strategy.register_action_name,
type: :create
)}
end