riverrun / one_time_pass_ecto Goto Github PK
View Code? Open in Web Editor NEWNo longer maintained - One-time password library with Ecto support (for Elixir)
No longer maintained - One-time password library with Ecto support (for Elixir)
Hi,
Would it be possible to add some examples on how to integrate this with existing applications in the README?
Is it also possible to add some code examples in an examples/
directory?
Thanks in advance! ๐
If you use 0
as the "last" value, the verification fails:
secret = OneTimePassEcto.Base.gen_secret(32)
code = OneTimePassEcto.Base.gen_hotp(secret, 0)
OneTimePassEcto.Base.check_hotp(code, secret) # => false
But it works with 1
:
secret = OneTimePassEcto.Base.gen_secret(32)
code = OneTimePassEcto.Base.gen_hotp(secret, 1)
OneTimePassEcto.Base.check_hotp(code, secret) # => 1
Took me a while to figure this out! Seems like it would be good to document this somewhere?
I think there may be a bug with check_totp/3
. The call to check_token/4
always returns false and I'm not sure why. It appears that the first case clause in check_token/4
never gets hit. Is there a reason why this is happening?
Here is the part of the library code that I believe is the culprit.
def check_totp(token, secret, opts \\ []) do
valid_token(token, Keyword.get(opts, :token_length, 6)) and
(
{count, window} =
{
Keyword.get(opts, :interval_length, 30) |> interval_count,
Keyword.get(opts, :window, 1)
}
check_token(token, secret, count - window, count + window, opts)
)
end
defp interval_count(interval_length) do
trunc(System.system_time(:second) / interval_length)
end
defp check_token(_token, _secret, current, last, _opts) when current > last do
false
end
defp check_token(token, secret, current, last, opts) do
case gen_hotp(secret, current, opts) do
^token ->
# never enters this clause
current
_ ->
# current will keep increasing until it is greater than last,
# which will lead to the preceding function clause always returning false
check_token(token, secret, current + 1, last, opts)
end
Erlang/OTP 24 dropped :crypto.hmac
.
(UndefinedFunctionError) function :crypto.hmac/3 is undefined or private, use crypto:mac/4 instead
One must use :crypto.mac/4
which is OTP 22+
It's not super clear to me how to handle Erlang as a dependency; I'd propose a PR but I'm not sure how to not break compatiblity with OTP < 22.
1) test check totp with default options (OneTimePassEctoTest)
test/one_time_pass_ecto_test.exs:62
** (UndefinedFunctionError) function :crypto.hmac/3 is undefined or private, use crypto:mac/4 instead
code: token = OneTimePassEcto.Base.gen_totp("MFRGGZDFMZTWQ2LK")
stacktrace:
(crypto 5.0.1) :crypto.hmac(:sha, "abcdefghij", <<0, 0, 0, 0, 3, 56, 206, 215>>)
(one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto/base.ex:73: OneTimePassEcto.Base.gen_hotp/3
test/one_time_pass_ecto_test.exs:63: (test)
2) test check hotp using different secret blocks access using original hotp secret (OneTimePassEctoTest)
test/one_time_pass_ecto_test.exs:45
** (UndefinedFunctionError) function :crypto.hmac/3 is undefined or private, use crypto:mac/4 instead
code: login(user, otp_secret: :second_otp_secret, otp_last: :second_otp_last)
stacktrace:
(crypto 5.0.1) :crypto.hmac(:sha, <<39, 17, 127, 0, 44, 86, 111, 243, 55, 190>>, <<0, 0, 0, 0, 0, 0, 0, 1>>)
(one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto/base.ex:73: OneTimePassEcto.Base.gen_hotp/3
(one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto/base.ex:155: OneTimePassEcto.Base.check_token/5
(one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto.ex:86: OneTimePassEcto.check_hotp/3
(one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto.ex:69: anonymous fn/5 in OneTimePassEcto.verify/4
(ecto_sql 3.0.3) lib/ecto/adapters/sql.ex:798: anonymous fn/3 in Ecto.Adapters.SQL.checkout_or_transaction/4
(db_connection 2.0.3) lib/db_connection.ex:1349: DBConnection.run_transaction/4
(one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto.ex:67: OneTimePassEcto.verify/4
test/one_time_pass_ecto_test.exs:48: (test)
3) test check totp with default options and alternate secret (OneTimePassEctoTest)
test/one_time_pass_ecto_test.exs:69
** (UndefinedFunctionError) function :crypto.hmac/3 is undefined or private, use crypto:mac/4 instead
code: token = OneTimePassEcto.Base.gen_totp("E4IX6ABMKZX7GN56")
stacktrace:
(crypto 5.0.1) :crypto.hmac(:sha, <<39, 17, 127, 0, 44, 86, 111, 243, 55, 190>>, <<0, 0, 0, 0, 3, 56, 206, 215>>)
(one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto/base.ex:73: OneTimePassEcto.Base.gen_hotp/3
test/one_time_pass_ecto_test.exs:70: (test)
4) test check hotp with default options (OneTimePassEctoTest)
test/one_time_pass_ecto_test.exs:24
** (UndefinedFunctionError) function :crypto.hmac/3 is undefined or private, use crypto:mac/4 instead
code: {:ok, %{id: id, otp_last: otp_last}} = login(user, [])
stacktrace:
(crypto 5.0.1) :crypto.hmac(:sha, "abcdefghij", <<0, 0, 0, 0, 0, 0, 0, 1>>)
(one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto/base.ex:73: OneTimePassEcto.Base.gen_hotp/3
(one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto/base.ex:155: OneTimePassEcto.Base.check_token/5
(one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto.ex:86: OneTimePassEcto.check_hotp/3
(one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto.ex:69: anonymous fn/5 in OneTimePassEcto.verify/4
(ecto_sql 3.0.3) lib/ecto/adapters/sql.ex:798: anonymous fn/3 in Ecto.Adapters.SQL.checkout_or_transaction/4
(db_connection 2.0.3) lib/db_connection.ex:1349: DBConnection.run_transaction/4
(one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto.ex:67: OneTimePassEcto.verify/4
test/one_time_pass_ecto_test.exs:26: (test)
5) test disallow totp check with same token (OneTimePassEctoTest)
test/one_time_pass_ecto_test.exs:76
** (UndefinedFunctionError) function :crypto.hmac/3 is undefined or private, use crypto:mac/4 instead
code: token = OneTimePassEcto.Base.gen_totp("MFRGGZDFMZTWQ2LK")
stacktrace:
(crypto 5.0.1) :crypto.hmac(:sha, "abcdefghij", <<0, 0, 0, 0, 3, 56, 206, 215>>)
(one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto/base.ex:73: OneTimePassEcto.Base.gen_hotp/3
test/one_time_pass_ecto_test.exs:77: (test)
6) test disallow totp check with earlier token that is still valid (OneTimePassEctoTest)
test/one_time_pass_ecto_test.exs:85
** (UndefinedFunctionError) function :crypto.hmac/3 is undefined or private, use crypto:mac/4 instead
code: token = OneTimePassEcto.Base.gen_totp("MFRGGZDFMZTWQ2LK")
stacktrace:
(crypto 5.0.1) :crypto.hmac(:sha, "abcdefghij", <<0, 0, 0, 0, 3, 56, 206, 215>>)
(one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto/base.ex:73: OneTimePassEcto.Base.gen_hotp/3
test/one_time_pass_ecto_test.exs:86: (test)
7) test check hotp with updated last (OneTimePassEctoTest)
test/one_time_pass_ecto_test.exs:35
** (UndefinedFunctionError) function :crypto.hmac/3 is undefined or private, use crypto:mac/4 instead
code: {:ok, %{id: id, otp_last: otp_last}} = login(user, [])
stacktrace:
(crypto 5.0.1) :crypto.hmac(:sha, "abcdefghij", <<0, 0, 0, 0, 0, 0, 0, 19>>)
(one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto/base.ex:73: OneTimePassEcto.Base.gen_hotp/3
(one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto/base.ex:155: OneTimePassEcto.Base.check_token/5
(one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto.ex:86: OneTimePassEcto.check_hotp/3
(one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto.ex:69: anonymous fn/5 in OneTimePassEcto.verify/4
(ecto_sql 3.0.3) lib/ecto/adapters/sql.ex:798: anonymous fn/3 in Ecto.Adapters.SQL.checkout_or_transaction/4
(db_connection 2.0.3) lib/db_connection.ex:1349: DBConnection.run_transaction/4
(one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto.ex:67: OneTimePassEcto.verify/4
test/one_time_pass_ecto_test.exs:37: (test)
8) test check hotp using alternate secret (OneTimePassEctoTest)
test/one_time_pass_ecto_test.exs:51
** (UndefinedFunctionError) function :crypto.hmac/3 is undefined or private, use crypto:mac/4 instead
code: {:ok, %{id: id, second_otp_last: otp_last}} = login(user, opts)
stacktrace:
(crypto 5.0.1) :crypto.hmac(:sha, <<39, 17, 127, 0, 44, 86, 111, 243, 55, 190>>, <<0, 0, 0, 0, 0, 0, 0, 1>>)
(one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto/base.ex:73: OneTimePassEcto.Base.gen_hotp/3
(one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto/base.ex:155: OneTimePassEcto.Base.check_token/5
(one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto.ex:86: OneTimePassEcto.check_hotp/3
(one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto.ex:69: anonymous fn/5 in OneTimePassEcto.verify/4
(ecto_sql 3.0.3) lib/ecto/adapters/sql.ex:798: anonymous fn/3 in Ecto.Adapters.SQL.checkout_or_transaction/4
(db_connection 2.0warning: Supervisor.terminate_child/2 with a PID is deprecated, please use DynamicSupervisor instead
(elixir 1.12.0) lib/supervisor.ex:857: Supervisor.terminate_child/2
(db_connection 2.0.3) lib/db_connection/watcher.ex:38: DBConnection.Watcher.handle_info/2
(stdlib 3.15) gen_server.erl:695: :gen_server.try_dispatch/4
(stdlib 3.15) gen_server.erl:771: :gen_server.handle_msg/6
(stdlib 3.15) proc_lib.erl:226: :proc_lib.init_p_do_apply/3
.3) lib/db_connection.ex:1349: DBConnection.run_transaction/4
(one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto.ex:67: OneTimePassEcto.verify/4
test/one_time_pass_ecto_test.exs:54: (test)
9) test check hotp (OneTimePassEcto.BaseTest)
test/base_test.exs:44
** (UndefinedFunctionError) function :crypto.hmac/3 is undefined or private, use crypto:mac/4 instead
code: assert Base.check_hotp("816065", "MFRGGZDFMZTWQ2LK") == 2
stacktrace:
(crypto 5.0.1) :crypto.hmac(:sha, "abcdefghij", <<0, 0, 0, 0, 0, 0, 0, 1>>)
(one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto/base.ex:73: OneTimePassEcto.Base.gen_hotp/3
(one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto/base.ex:155: OneTimePassEcto.Base.check_token/5
test/base_test.exs:45: (test)
10) test generate hotp (OneTimePassEcto.BaseTest)
test/base_test.exs:33
** (UndefinedFunctionError) function :crypto.hmac/3 is undefined or private, use crypto:mac/4 instead
code: assert Base.gen_hotp("MFRGGZDFMZTWQ2LK", 1) == "765705"
stacktrace:
(crypto 5.0.1) :crypto.hmac(:sha, "abcdefghij", <<0, 0, 0, 0, 0, 0, 0, 1>>)
(one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto/base.ex:73: OneTimePassEcto.Base.gen_hotp/3
test/base_test.exs:34: (test)
11) test check hotp fails for outside window (OneTimePassEcto.BaseTest)
test/base_test.exs:50
** (UndefinedFunctionError) function :crypto.hmac/3 is undefined or private, use crypto:mac/4 instead
code: refute Base.check_hotp("088239", "MFRGGZDFMZTWQ2LK", last: 10)
stacktrace:
(crypto 5.0.1) :crypto.hmac(:sha, "abcdefghij", <<0, 0, 0, 0, 0, 0, 0, 11>>)
(one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto/base.ex:73: OneTimePassEcto.Base.gen_hotp/3
(one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto/base.ex:155: OneTimePassEcto.Base.check_token/5
test/base_test.exs:51: (test)
..
12) test check totp (OneTimePassEcto.BaseTest)
test/base_test.exs:56
** (UndefinedFunctionError) function :crypto.hmac/3 is undefined or private, use crypto:mac/4 instead
code: assert Base.gen_totp("MFRGGZDFMZTWQ2LK") |> Base.check_totp("MFRGGZDFMZTWQ2LK")
stacktrace:
(crypto 5.0.1) :crypto.hmac(:sha, "abcdefghij", <<0, 0, 0, 0, 3, 56, 206, 215>>)
(one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto/base.ex:73: OneTimePassEcto.Base.gen_hotp/3
test/base_test.exs:57: (test)
13) test check totp fails for outside window (OneTimePassEcto.BaseTest)
test/base_test.exs:63
** (UndefinedFunctionError) function :crypto.hmac/3 is undefined or private, use crypto:mac/4 instead
code: token = Base.gen_hotp("MFRGGZDFMZTWQ2LK", get_count() - 2)
stacktrace:
(crypto 5.0.1) :crypto.hmac(:sha, "abcdefghij", <<0, 0, 0, 0, 3, 56, 206, 213>>)
(one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto/base.ex:73: OneTimePassEcto.Base.gen_hotp/3
test/base_test.exs:64: (test)
.
14) test generate hotp with zero padding (OneTimePassEcto.BaseTest)
test/base_test.exs:40
** (UndefinedFunctionError) function :crypto.hmac/3 is undefined or private, use crypto:mac/4 instead
code: assert Base.gen_hotp("MFRGGZDFMZTWQ2LK", 19) == "088239"
stacktrace:
(crypto 5.0.1) :crypto.hmac(:sha, "abcdefghij", <<0, 0, 0, 0, 0, 0, 0, 19>>)
(one_time_pass_ecto 1.0.3) lib/one_time_pass_ecto/base.ex:73: OneTimePassEcto.Base.gen_hotp/3
test/base_test.exs:41: (test)
Finished in 0.1 seconds (0.00s async, 0.1s sync)
17 tests, 14 failures
Randomized with seed 669903
The iex shell shows the following warning when using the library with Elixir 1.8 compiled using OTP 21.
iex> OneTimePassEcto.Base.gen_totp(s, [{:token_length, 4}, {:interval_length, 300}])
warning: deprecated time unit: :seconds. A time unit should be :second, :millisecond,
:microsecond, :nanosecond, or a positive integer
(one_time_pass_ecto) lib/one_time_pass_ecto/base.ex:147: OneTimePassEcto.Base.interval_count/1
(one_time_pass_ecto) lib/one_time_pass_ecto/base.ex:97: OneTimePassEcto.Base.gen_totp/2
(stdlib) erl_eval.erl:680: :erl_eval.do_apply/6
(elixir) src/elixir.erl:258: :elixir.eval_forms/4
(iex) lib/iex/evaluator.ex:257: IEx.Evaluator.handle_eval/5
(iex) lib/iex/evaluator.ex:237: IEx.Evaluator.do_eval/3
(iex) lib/iex/evaluator.ex:215: IEx.Evaluator.eval/3
(iex) lib/iex/evaluator.ex:103: IEx.Evaluator.loop/1
(iex) lib/iex/evaluator.ex:27: IEx.Evaluator.init/4
(stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3
"8613"
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.