Packages
ash_authentication
5.0.0-rc.12
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.
Current section
Files
Jump to
Current section
Files
lib/ash_authentication/strategies/oauth2/identity_change.ex
# SPDX-FileCopyrightText: 2022 Alembic Pty Ltd
#
# SPDX-License-Identifier: MIT
defmodule AshAuthentication.Strategy.OAuth2.IdentityChange do
@moduledoc """
Resolves and updates the user's identity when registering via OAuth2/OIDC.
Runs in two phases:
* `before_action` - resolves *which* local user this sign-in belongs to,
using the provider's `iss`/`sub` (never the email). See
`AshAuthentication.Strategy.OAuth2.UserResolver` for the matching rules.
* `after_action` - upserts the identity row for the resolved user so that
future sign-ins with the same `iss`/`sub` resolve to them.
"""
use Ash.Resource.Change
alias Ash.{Changeset, Error.Framework.AssumptionFailed, Resource.Change}
alias AshAuthentication.{Info, Strategy, Strategy.OAuth2, UserIdentity}
import AshAuthentication.Utils, only: [is_falsy: 1]
@doc false
@impl true
@spec change(Changeset.t(), keyword, Change.context()) :: Changeset.t()
def change(changeset, _opts, context) do
case Info.strategy_for_action(changeset.resource, changeset.action.name) do
{:ok, strategy} ->
do_change(changeset, strategy, context)
:error ->
{:error,
AssumptionFailed.exception(
message: "Action does not correlate with an authentication strategy"
)}
end
end
defp do_change(changeset, strategy, _context) when is_falsy(strategy.identity_resource),
do: changeset
defp do_change(changeset, strategy, context) do
opts = [tenant: context.tenant, actor: context.actor]
changeset
|> Changeset.before_action(&OAuth2.UserResolver.resolve(&1, strategy, opts))
|> Changeset.after_action(&upsert_identity(&1, &2, strategy, opts))
end
defp upsert_identity(changeset, user, strategy, opts) do
with {:ok, user_id_attribute_name} <-
UserIdentity.Info.user_identity_user_id_attribute_name(strategy.identity_resource),
{:ok, _identity} <-
UserIdentity.Actions.upsert(
strategy.identity_resource,
%{
user_id_attribute_name => user.id,
user_info: Changeset.get_argument(changeset, :user_info),
oauth_tokens: Changeset.get_argument(changeset, :oauth_tokens),
strategy: Strategy.name(strategy)
},
opts
) do
user
|> Ash.load(
[
{strategy.identity_relationship_name,
Ash.Query.new(strategy.identity_resource)
|> Ash.Query.set_context(%{
private: %{
ash_authentication?: true
}
})}
],
Keyword.put(opts, :domain, Info.domain!(strategy.resource))
)
else
{:error, reason} -> {:error, reason}
end
end
end