Packages
ash_authentication
5.0.0-rc.8
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/totp/generate_pending_setup_change.ex
# SPDX-FileCopyrightText: 2022 Alembic Pty Ltd
#
# SPDX-License-Identifier: MIT
defmodule AshAuthentication.Strategy.Totp.GeneratePendingSetupChange do
@moduledoc """
Generates a pending TOTP setup for two-step confirmation.
This change is used when `confirm_setup_enabled?` is true. Instead of storing
the secret directly on the user, it:
1. Generates a new TOTP secret
2. Creates a setup token containing the secret
3. Stores the token in the token resource
4. Returns the setup_token and totp_url in the user's metadata
The user must then call the confirm_setup action with a valid TOTP code to
activate the secret.
"""
use Ash.Resource.Change
alias Ash.{Changeset, Resource}
alias Ash.Error.Framework.AssumptionFailed
alias AshAuthentication.{Info, Jwt, TokenResource}
@doc false
@impl true
def change(changeset, _context, _opts) do
case Info.strategy_for_action(changeset.resource, changeset.action.name) do
{:ok, strategy} ->
do_change(changeset, strategy)
:error ->
raise AssumptionFailed,
message: "Action does not correlate with an authentication strategy"
end
end
defp do_change(changeset, strategy) do
changeset
|> Changeset.set_context(%{private: %{ash_authentication?: true}})
|> Changeset.after_action(fn _changeset, user ->
with {:ok, secret, totp_url} <- generate_secret_and_url(user, strategy),
{:ok, setup_token} <- generate_and_store_setup_token(user, secret, strategy) do
user =
user
|> Resource.put_metadata(:setup_token, setup_token)
|> Resource.put_metadata(:totp_url, totp_url)
{:ok, user}
end
end)
end
defp generate_secret_and_url(user, strategy) do
secret = NimbleTOTP.secret(strategy.secret_length)
identity = Map.get(user, strategy.identity_field)
totp_url =
NimbleTOTP.otpauth_uri("#{strategy.issuer}:#{identity}", secret,
issuer: strategy.issuer,
period: strategy.period
)
{:ok, secret, totp_url}
end
defp generate_and_store_setup_token(user, secret, strategy) do
with {:ok, token_resource} <- Info.authentication_tokens_token_resource(strategy.resource),
{:ok, token, _claims} <-
Jwt.token_for_user(
user,
%{},
purpose: :totp_setup,
token_lifetime: strategy.setup_token_lifetime
) do
encoded_secret = Base.encode64(secret)
case TokenResource.Actions.store_token(
token_resource,
%{
"token" => token,
"purpose" => "totp_setup",
"extra_data" => %{"secret" => encoded_secret}
},
[]
) do
:ok -> {:ok, token}
{:error, reason} -> {:error, reason}
end
end
end
end