Current section

Files

Jump to
ash_authentication lib ash_authentication strategies oauth2 sign_in_preparation.ex
Raw

lib/ash_authentication/strategies/oauth2/sign_in_preparation.ex

defmodule AshAuthentication.Strategy.OAuth2.SignInPreparation do
@moduledoc """
Prepare a query for sign in
Performs three main tasks:
1. Ensures that there is only one matching user record returned, otherwise
returns an authentication failed error.
2. Generates an access token if token generation is enabled.
3. Updates the user identity resource, if one is enabled.
"""
use Ash.Resource.Preparation
alias Ash.{Error.Framework.AssumptionFailed, Query, Resource.Preparation}
alias AshAuthentication.{Errors.AuthenticationFailed, Info, Jwt, UserIdentity}
require Ash.Query
import AshAuthentication.Utils, only: [is_falsy: 1]
@doc false
@impl true
@spec prepare(Query.t(), keyword, Preparation.context()) :: Query.t()
def prepare(query, _opts, _context) do
case Info.strategy_for_action(query.resource, query.action.name) do
:error ->
{:error,
AssumptionFailed.exception(message: "Strategy is missing from the changeset context.")}
{:ok, strategy} ->
query
|> Query.after_action(fn
query, [user] ->
with {:ok, user} <- maybe_update_identity(user, query, strategy) do
{:ok, [maybe_generate_token(user)]}
end
_, _ ->
{:error, AuthenticationFailed.exception(query: query)}
end)
end
end
defp maybe_update_identity(user, _query, strategy) when is_falsy(strategy.identity_resource),
do: user
defp maybe_update_identity(user, query, strategy) do
strategy.identity_resource
|> UserIdentity.Actions.upsert(%{
user_info: Query.get_argument(query, :user_info),
oauth_tokens: Query.get_argument(query, :oauth_tokens),
strategy: strategy.name,
user_id: user.id
})
|> case do
{:ok, _identity} ->
user
|> query.api.load(strategy.identity_relationship_name)
{:error, reason} ->
{:error, reason}
end
end
defp maybe_generate_token(user) do
if AshAuthentication.Info.authentication_tokens_enabled?(user.__struct__) do
{:ok, token, _claims} = Jwt.token_for_user(user)
%{user | __metadata__: Map.put(user.__metadata__, :token, token)}
else
user
end
end
end