fix: correctly generate sign-in tokens when requested.

This commit is contained in:
James Harton 2024-06-06 14:34:30 +12:00
parent d8588b9b89
commit 81236e1ed5
Signed by: james
GPG key ID: 90E82DAA13F624F4
4 changed files with 64 additions and 28 deletions

View file

@ -38,28 +38,23 @@ defmodule AshAuthentication.Strategy.Password.SignInPreparation do
if strategy.hash_provider.valid?( if strategy.hash_provider.valid?(
password, password,
Map.get(record, strategy.hashed_password_field) Map.get(record, strategy.hashed_password_field)
), ) do
do: token_type = query.context[:token_type] || :user
{:ok,
[ {:ok, [maybe_generate_token(token_type, record, strategy)]}
maybe_generate_token( else
query.context[:token_type] || :user, {:error,
record, AuthenticationFailed.exception(
strategy strategy: strategy,
) query: query,
]}, caused_by: %{
else: module: __MODULE__,
{:error, action: query.action,
AuthenticationFailed.exception( resource: query.resource,
strategy: strategy, message: "Password is not valid"
query: query, }
caused_by: %{ )}
module: __MODULE__, end
action: query.action,
resource: query.resource,
message: "Password is not valid"
}
)}
query, [] -> query, [] ->
strategy.hash_provider.simulate() strategy.hash_provider.simulate()
@ -118,11 +113,11 @@ defmodule AshAuthentication.Strategy.Password.SignInPreparation do
end end
end end
defp generate_token(purpose, record, strategy) defp generate_token(:sign_in, record, strategy) when strategy.sign_in_tokens_enabled? do
when is_integer(strategy.sign_in_token_lifetime) and purpose == :sign_in do
{:ok, token, _claims} = {:ok, token, _claims} =
Jwt.token_for_user(record, %{"purpose" => to_string(purpose)}, Jwt.token_for_user(record, %{"purpose" => "sign_in"},
token_lifetime: strategy.sign_in_token_lifetime token_lifetime: strategy.sign_in_token_lifetime,
purpose: :sign_in
) )
Ash.Resource.put_metadata(record, :token, token) Ash.Resource.put_metadata(record, :token, token)

View file

@ -24,7 +24,7 @@ defmodule AshAuthentication.Strategy.Password.StrategyTest do
assert MapSet.equal?(phases, MapSet.new(~w[register sign_in reset_request reset]a)) assert MapSet.equal?(phases, MapSet.new(~w[register sign_in reset_request reset]a))
end end
test "it returns the correct phases when the strategy doesn't suport resetting" do test "it returns the correct phases when the strategy doesn't support resetting" do
strategy = %Password{} strategy = %Password{}
phases = phases =
@ -48,7 +48,7 @@ defmodule AshAuthentication.Strategy.Password.StrategyTest do
assert MapSet.equal?(actions, MapSet.new(~w[register sign_in reset_request reset]a)) assert MapSet.equal?(actions, MapSet.new(~w[register sign_in reset_request reset]a))
end end
test "it returns the correct actions when the strategy doesn't suport resetting" do test "it returns the correct actions when the strategy doesn't support resetting" do
strategy = %Password{} strategy = %Password{}
actions = actions =

View file

@ -13,6 +13,8 @@ defmodule AshAuthentication.Strategy.PasswordTest do
Strategy.Password.Resettable Strategy.Password.Resettable
} }
alias Example.User
doctest Password doctest Password
describe "reset_token_for/1" do describe "reset_token_for/1" do
@ -26,4 +28,39 @@ defmodule AshAuthentication.Strategy.PasswordTest do
assert claims["act"] == to_string(resettable.password_reset_action_name) assert claims["act"] == to_string(resettable.password_reset_action_name)
end end
end end
describe "regressions" do
test "only one user token is generated for a new user registration" do
user = build_user()
subject = AshAuthentication.user_to_subject(user)
tokens = Example.Token |> Ash.read!() |> Enum.group_by(& &1.purpose)
assert [%{subject: ^subject}] = tokens["user"]
token_types = tokens |> Map.keys() |> MapSet.new()
assert token_types == MapSet.new(["user", "confirm"])
end
test "only one token is generated for a user sign-in" do
user = build_user()
subject = AshAuthentication.user_to_subject(user)
Example.Token |> Ash.bulk_destroy!(:destroy, %{})
strategy = AshAuthentication.Info.strategy!(User, :password)
{:ok, _signed_in_user} =
Strategy.action(
strategy,
:sign_in,
%{
username: user.username,
password: user.__metadata__.password
},
context: [token_type: :sign_in]
)
assert [%{subject: ^subject, purpose: "sign_in"}] = Example.Token |> Ash.read!()
end
end
end end

View file

@ -9,4 +9,8 @@ defmodule Example.Token do
table("tokens") table("tokens")
repo(Example.Repo) repo(Example.Repo)
end end
actions do
defaults [:read, :destroy]
end
end end