Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adding the ability to provide custom login token identity #3

Merged
merged 3 commits into from
Jul 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 23 additions & 11 deletions lib/nopass.ex
Original file line number Diff line number Diff line change
Expand Up @@ -35,22 +35,23 @@ defmodule Nopass do

Parameters:
`entity`: the entity (e.g. email address) for which you are generating the one-time password
`expires`_after_seconds: the lifetime of the generated one-time passwords after which the password expires. Optional, default: 600
`length`: the length of the random portion of the one-time password. Optional, default: 20
`opts`: optional options, including:
`expires`_after_seconds: the lifetime of the generated one-time passwords after which the password expires. Optional, default: 600
`length`: the length of the random portion of the one-time password. Optional, default: 20

## Examples

iex> _one_time_password = Nopass.new_one_time_password("luigi@mansion", after_seconds: 900, length: 30)
"""
def new_one_time_password(entity, opts \\ []) do
params =
one_time_password_params =
Enum.into(opts, %{
expires_after_seconds: 600,
length: 20
})

one_time_password = "otp" <> Nanoid.generate(params.length, @password_dictionary)
expires_at = System.os_time(:second) + params.expires_after_seconds
one_time_password = "otp" <> Nanoid.generate(one_time_password_params.length, @password_dictionary)
expires_at = System.os_time(:second) + one_time_password_params.expires_after_seconds

%Nopass.Schema.OneTimePassword{
identity: entity,
Expand All @@ -67,20 +68,23 @@ defmodule Nopass do

Parameters:
`one_time_password`: the one-time password to verify and consume.
`expires`_after_seconds: the lifetime of the generated login token, in seconds, after which the login token expires. Optional, default: 86400 (one day)
`length`: the length of the random portion of the login token to be generated. Optional, default: 50
`opts`: optional options, including:
`expires`_after_seconds: the lifetime of the generated login token, in seconds, after which the login token expires. Optional, default: 86400 (one day)
`length`: the length of the random portion of the login token to be generated. Optional, default: 50
'login_token_identity': the value of the identity to be associated with the login token or the function for computing it, based on the value of the one-time-password's identity. Optional, default: the identity associated with the one-time password.

## Examples

iex> one_time_password = Nopass.new_one_time_password("luigi@mansion")
iex> {:ok, login_token} = Nopass.trade_one_time_password_for_login_token(one_time_password)
iex> {:ok, login_token} = Nopass.trade_one_time_password_for_login_token(one_time_password, login_token_identity: fn x -> "user known as " <> x end)
iex> Nopass.verify_login_token(login_token)
{:ok, "luigi@mansion"}
{:ok, "user known as luigi@mansion"}
iex> {:error, :expired_or_missing} = Nopass.trade_one_time_password_for_login_token(one_time_password)
"""
def trade_one_time_password_for_login_token(one_time_password, opts \\ []) do
params =
login_token_params =
Enum.into(opts, %{
login_token_identity: fn otp_identity -> otp_identity end,
expires_after_seconds: 600,
length: 50
})
Expand All @@ -97,7 +101,15 @@ defmodule Nopass do

otp_record ->
Nopass.Repo.delete(otp_record)
insert_login_token(otp_record.identity, params.expires_after_seconds, params.length)

login_token_identity =
if is_function(login_token_params.login_token_identity) do
login_token_params.login_token_identity.(otp_record.identity)
else
login_token_params.login_token_identity
end

insert_login_token(login_token_identity, login_token_params.expires_after_seconds, login_token_params.length)
end
end

Expand Down
28 changes: 27 additions & 1 deletion test/nopass_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,33 @@ defmodule NopassTest do

# Deleting an already deleted or simply bad login token should be fine.
:ok = Nopass.delete_login_token(login_token)
:ok = Nopass.delete_login_token("non such")
:ok = Nopass.delete_login_token("nonesuch")
end

test "login token identity, value" do
one_time_password = Nopass.new_one_time_password("luigi")

{:ok, login_token} =
Nopass.trade_one_time_password_for_login_token(
one_time_password,
login_token_identity: "mario"
)

{:ok, "mario"} = Nopass.verify_login_token(login_token)
end

test "login token identity, function" do
one_time_password = Nopass.new_one_time_password("luigi")

f_prepend_with_yay = fn x -> "yay" <> x end

{:ok, login_token} =
Nopass.trade_one_time_password_for_login_token(
one_time_password,
login_token_identity: f_prepend_with_yay
)

{:ok, "yayluigi"} = Nopass.verify_login_token(login_token)
end

test "try to use an expired one-time password" do
Expand Down
Loading