Giter Site home page Giter Site logo

phauxth's People

Contributors

bryanjbryce avatar fabrik42 avatar gitter-badger avatar guidotripaldi avatar jamesnolanverran avatar maxnordlund avatar milmazz avatar riverrun avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

phauxth's Issues

Branch 1.5 Changes - Breaking?

Hey @riverrun

Just wondering how the 1.5 branch is coming along? Is it breaking? Just looking to implement Phauxth in a greenfield project and I'm pretty new with Elixir & Phoenix so I'm trying to make sure I am working with stable packages.

Cheers,

Rich

Encode / decode the token with JSON

Replace term_to_binary and binary_to_term variants with JSON encoding / decoding (provided by Poison).

Side-effects of this change

Tokens created with the old implementation will no longer be valid after upgrading Phauxth to the new, JSON, implementation.

Why add session in tests?

Hi @riverrun, and thanks again for the Phauxth library, it really "just works".
I noted when testing user controller actions, you create a session to test the actions that need a logged-in user. Regarding this, the authors in Programming Phoenix say:

You might be tempted to place the user_id in the session for the Auth plug to pick up:

​  conn()
​  |> fetch_session()
​  |> put_session(​:user_id​, user.id)
​  |> get(​"​​/videos"​)

This approach is a little messy. We don’t want to store anything directly in the session, because we don’t want to leak implementation details.

The way they do it is they just put a user in conn.assigns and add a condition to the auth plug to first check if there is already a user in conn.assigns. The auth plug is tested in isolation.

I don't know if your familiar with this, but I would like to know the reason behind the decision to do that this way. ( I must say, however, I honestly don't know what they mean with "we don't want to leak implementation details").

Resend confirmation email

There doesn't seem to be a built-in solution for resending confirmation emails? Sometimes the email might be lost or end up in spam etc.

Allowing authentication by either token or session for a given pipline

I have tried:

  pipeline :api do
        plug :accepts, ["json"]
	plug :fetch_session
        plug Phauxth.Authenticate, method: [:token, :session], user_context: Beta.Users
  end

But failed with:

 no function clause matching in Phauxth.Authenticate.get_user/2
        (phauxth) lib/phauxth/authenticate.ex:35: Phauxth.Authenticate.get_user(%Plug.Conn{adapter: 

Getting errors when passing a socket to Phauxth.Token.verify

I'm just guessing, but it seems Phauxth.Token.verify isn't compatible with receiving a socket instead of a connection. The error I am getting is:

Request: GET /socket/websocket?token=fake_token&vsn=2.0.0
** (exit) an exception was raised:
** (ArgumentError) argument error
:erlang.apply(%Phoenix.Socket{assigns: %{}, channel: nil, channel_pid: nil, endpoint: Test.Endpoint, handler: Test.UserSocket, id: nil, join_ref: nil, joined: false, private: %{}, pubsub_server: Reddlst.PubSub, ref: nil, serializer: Phoenix.Transports.V2.WebSocketSerializer, topic: nil, transport: Phoenix.Transports.WebSocket, transport_name: :websocket, transport_pid: #PID<0.6284.0>, vsn: "2.0.0"}, :config, [])
(phauxth) lib/phauxth/token.ex:74: Phauxth.Token.get_key_base/1

What do you think about adding support for socket to Phauxth.Token. Phoenix.Token has done it
https://github.com/phoenixframework/phoenix/blob/master/lib/phoenix/token.ex

function Bcrypt.no_user_verify/1 is undefined (module Bcrypt is not available)

I think perhaps it's a namespacing issue? Library looks really promising, thanks for the hard work and effort 👍

Request: POST /sessions
** (exit) an exception was raised:
    ** (UndefinedFunctionError) function Bcrypt.no_user_verify/1 is undefined (module Bcrypt is not available)
        Bcrypt.no_user_verify([])
        (phauxth) lib/phauxth/login.ex:38: Phauxth.Login.check_pass/4
        (phauxth) lib/phauxth/login.ex:38: Phauxth.Login.verify/3

Make session auth depend on more than the static user_id for independent invalidation

Invalidating active phoenix sessions in case any do get compromised does require changing the session secret, a.k.a. dropping all active sessions. A more secure way would be to add some kind of db backed session key to the session, which can be reset/changed independently per user.

Some of this could already be changed with phauxth by using custom implementations, but it seems the assumption of the session holding a current_user key with the user_id is hardcoded into the project.

Is this something you would consider adding? I might even send a PR, because otherwise your library seems really nice.

See here:
https://youtu.be/w3lKmFsmlvQ?t=24m9s
https://elixirforum.com/t/33-elixirconf-2017-plugging-the-security-holes-in-your-phoenix-application/8565/21

Basic 0.15 project fails tests when created with --confirm option

I'm trying to migrate some projects from Phx1.2/Openmaize to Phx1.3/Phauxth, so I've start to play with Phauxth 0.15, but it seems that v.015 isn't yet ready when the --confirm option is given, because eight tests failed with these two kinds of errors:

** (UndefinedFunctionError) function Phauxth.Confirm.gen_token/0 is undefined or private
and
** (UndefinedFunctionError) function Phauxth.Confirm.verify/2 is undefined or private. Did you mean one of: * verify/3

I've created a new playground project through the following steps:

new project:
$ mix phx.new alibaba_html_confirm && cd alibaba_html_confirm

phauxth setup (html + confirm):
$ mix phauxth.new --confirm

add following dependency to mix.ex file:
{:phauxth, "~> 0.15"}, {:bcrypt_elixir, "~> 0.12"}

run usual initialization commands:
$ mix deps.get && mix deps.compile && mix ecto.create && mix ecto.migrate

run tests:
$ mix test

Full transcript of the errors:

[alibaba_html_confirm]$ mix test
...

  1. test creates user when data is valid (AlibabaHtmlConfirmWeb.UserControllerTest)
    test/alibaba_html_confirm_web/controllers/user_controller_test.exs:46
    ** (UndefinedFunctionError) function Phauxth.Confirm.gen_token/0 is undefined or private
    code: conn = post conn, user_path(conn, :create), user: @create_attrs
    stacktrace:
    (phauxth) Phauxth.Confirm.gen_token()
    (alibaba_html_confirm) lib/alibaba_html_confirm_web/controllers/user_controller.ex:21: AlibabaHtmlConfirmWeb.UserController.create/2
    (alibaba_html_confirm) lib/alibaba_html_confirm_web/controllers/user_controller.ex:1: AlibabaHtmlConfirmWeb.UserController.action/2
    (alibaba_html_confirm) lib/alibaba_html_confirm_web/controllers/user_controller.ex:1: AlibabaHtmlConfirmWeb.UserController.phoenix_controller_pipeline/2
    (alibaba_html_confirm) lib/alibaba_html_confirm_web/endpoint.ex:1: AlibabaHtmlConfirmWeb.Endpoint.instrument/4
    (phoenix) lib/phoenix/router.ex:278: Phoenix.Router.call/1
    (alibaba_html_confirm) lib/alibaba_html_confirm_web/endpoint.ex:1: AlibabaHtmlConfirmWeb.Endpoint.plug_builder_call/2
    (alibaba_html_confirm) lib/alibaba_html_confirm_web/endpoint.ex:1: AlibabaHtmlConfirmWeb.Endpoint.call/2
    (phoenix) lib/phoenix/test/conn_test.ex:224: Phoenix.ConnTest.dispatch/5
    test/alibaba_html_confirm_web/controllers/user_controller_test.exs:47: (test)

...

  1. test does not create user and renders errors when data is invalid (AlibabaHtmlConfirmWeb.UserControllerTest)
    test/alibaba_html_confirm_web/controllers/user_controller_test.exs:51
    ** (UndefinedFunctionError) function Phauxth.Confirm.gen_token/0 is undefined or private
    code: conn = post conn, user_path(conn, :create), user: @invalid_attrs
    stacktrace:
    (phauxth) Phauxth.Confirm.gen_token()
    (alibaba_html_confirm) lib/alibaba_html_confirm_web/controllers/user_controller.ex:21: AlibabaHtmlConfirmWeb.UserController.create/2
    (alibaba_html_confirm) lib/alibaba_html_confirm_web/controllers/user_controller.ex:1: AlibabaHtmlConfirmWeb.UserController.action/2
    (alibaba_html_confirm) lib/alibaba_html_confirm_web/controllers/user_controller.ex:1: AlibabaHtmlConfirmWeb.UserController.phoenix_controller_pipeline/2
    (alibaba_html_confirm) lib/alibaba_html_confirm_web/endpoint.ex:1: AlibabaHtmlConfirmWeb.Endpoint.instrument/4
    (phoenix) lib/phoenix/router.ex:278: Phoenix.Router.call/1
    (alibaba_html_confirm) lib/alibaba_html_confirm_web/endpoint.ex:1: AlibabaHtmlConfirmWeb.Endpoint.plug_builder_call/2
    (alibaba_html_confirm) lib/alibaba_html_confirm_web/endpoint.ex:1: AlibabaHtmlConfirmWeb.Endpoint.call/2
    (phoenix) lib/phoenix/test/conn_test.ex:224: Phoenix.ConnTest.dispatch/5
    test/alibaba_html_confirm_web/controllers/user_controller_test.exs:52: (test)

.......10:51:35.793 request_id=glv3c4k25r1t1j8ejak36qp7k3ldjkv2 [warn] user=nil message="invalid password"
..10:51:37.130 request_id=rcf15o9v97ip75oq6s1trs5f9kjphi6o [warn] user=nil message="account unconfirmed"
..........

  1. test confirmation fails for incorrect key (AlibabaHtmlConfirmWeb.ConfirmControllerTest)
    test/alibaba_html_confirm_web/controllers/confirm_controller_test.exs:21
    ** (UndefinedFunctionError) function Phauxth.Confirm.verify/2 is undefined or private. Did you mean one of:

      * verify/3
    

    code: conn = get(conn, confirm_path(conn, :new, email: email, key: key))
    stacktrace:
    (phauxth) Phauxth.Confirm.verify(%{"email" => "[email protected]", "key" => "pu9-VNdgE8V9QzO19RLCG3KUNjpxuixg"}, AlibabaHtmlConfirm.Accounts)
    (alibaba_html_confirm) lib/alibaba_html_confirm_web/controllers/confirm_controller.ex:8: AlibabaHtmlConfirmWeb.ConfirmController.new/2
    (alibaba_html_confirm) lib/alibaba_html_confirm_web/controllers/confirm_controller.ex:1: AlibabaHtmlConfirmWeb.ConfirmController.action/2
    (alibaba_html_confirm) lib/alibaba_html_confirm_web/controllers/confirm_controller.ex:1: AlibabaHtmlConfirmWeb.ConfirmController.phoenix_controller_pipeline/2
    (alibaba_html_confirm) lib/alibaba_html_confirm_web/endpoint.ex:1: AlibabaHtmlConfirmWeb.Endpoint.instrument/4
    (phoenix) lib/phoenix/router.ex:278: Phoenix.Router.call/1
    (alibaba_html_confirm) lib/alibaba_html_confirm_web/endpoint.ex:1: AlibabaHtmlConfirmWeb.Endpoint.plug_builder_call/2
    (alibaba_html_confirm) lib/alibaba_html_confirm_web/endpoint.ex:1: AlibabaHtmlConfirmWeb.Endpoint.call/2
    (phoenix) lib/phoenix/test/conn_test.ex:224: Phoenix.ConnTest.dispatch/5
    test/alibaba_html_confirm_web/controllers/confirm_controller_test.exs:24: (test)

  2. test confirmation fails for incorrect email (AlibabaHtmlConfirmWeb.ConfirmControllerTest)
    test/alibaba_html_confirm_web/controllers/confirm_controller_test.exs:29
    ** (UndefinedFunctionError) function Phauxth.Confirm.verify/2 is undefined or private. Did you mean one of:

      * verify/3
    

    code: conn = get(conn, confirm_path(conn, :new, email: email, key: key))
    stacktrace:
    (phauxth) Phauxth.Confirm.verify(%{"email" => "[email protected]", "key" => "pu9-VNdgE8V9qZo19rlcg3KUNjpxuixg"}, AlibabaHtmlConfirm.Accounts)
    (alibaba_html_confirm) lib/alibaba_html_confirm_web/controllers/confirm_controller.ex:8: AlibabaHtmlConfirmWeb.ConfirmController.new/2
    (alibaba_html_confirm) lib/alibaba_html_confirm_web/controllers/confirm_controller.ex:1: AlibabaHtmlConfirmWeb.ConfirmController.action/2
    (alibaba_html_confirm) lib/alibaba_html_confirm_web/controllers/confirm_controller.ex:1: AlibabaHtmlConfirmWeb.ConfirmController.phoenix_controller_pipeline/2
    (alibaba_html_confirm) lib/alibaba_html_confirm_web/endpoint.ex:1: AlibabaHtmlConfirmWeb.Endpoint.instrument/4
    (phoenix) lib/phoenix/router.ex:278: Phoenix.Router.call/1
    (alibaba_html_confirm) lib/alibaba_html_confirm_web/endpoint.ex:1: AlibabaHtmlConfirmWeb.Endpoint.plug_builder_call/2
    (alibaba_html_confirm) lib/alibaba_html_confirm_web/endpoint.ex:1: AlibabaHtmlConfirmWeb.Endpoint.call/2
    (phoenix) lib/phoenix/test/conn_test.ex:224: Phoenix.ConnTest.dispatch/5
    test/alibaba_html_confirm_web/controllers/confirm_controller_test.exs:32: (test)

  3. test confirmation succeeds for correct key (AlibabaHtmlConfirmWeb.ConfirmControllerTest)
    test/alibaba_html_confirm_web/controllers/confirm_controller_test.exs:13
    ** (UndefinedFunctionError) function Phauxth.Confirm.verify/2 is undefined or private. Did you mean one of:

      * verify/3
    

    code: conn = get(conn, confirm_path(conn, :new, email: email, key: key))
    stacktrace:
    (phauxth) Phauxth.Confirm.verify(%{"email" => "[email protected]", "key" => "pu9-VNdgE8V9qZo19rlcg3KUNjpxuixg"}, AlibabaHtmlConfirm.Accounts)
    (alibaba_html_confirm) lib/alibaba_html_confirm_web/controllers/confirm_controller.ex:8: AlibabaHtmlConfirmWeb.ConfirmController.new/2
    (alibaba_html_confirm) lib/alibaba_html_confirm_web/controllers/confirm_controller.ex:1: AlibabaHtmlConfirmWeb.ConfirmController.action/2
    (alibaba_html_confirm) lib/alibaba_html_confirm_web/controllers/confirm_controller.ex:1: AlibabaHtmlConfirmWeb.ConfirmController.phoenix_controller_pipeline/2
    (alibaba_html_confirm) lib/alibaba_html_confirm_web/endpoint.ex:1: AlibabaHtmlConfirmWeb.Endpoint.instrument/4
    (phoenix) lib/phoenix/router.ex:278: Phoenix.Router.call/1
    (alibaba_html_confirm) lib/alibaba_html_confirm_web/endpoint.ex:1: AlibabaHtmlConfirmWeb.Endpoint.plug_builder_call/2
    (alibaba_html_confirm) lib/alibaba_html_confirm_web/endpoint.ex:1: AlibabaHtmlConfirmWeb.Endpoint.call/2
    (phoenix) lib/phoenix/test/conn_test.ex:224: Phoenix.ConnTest.dispatch/5
    test/alibaba_html_confirm_web/controllers/confirm_controller_test.exs:16: (test)

  4. test reset password fails for invalid email (AlibabaHtmlConfirmWeb.PasswordResetControllerTest)
    test/alibaba_html_confirm_web/controllers/password_reset_controller_test.exs:25
    ** (UndefinedFunctionError) function Phauxth.Confirm.gen_token/0 is undefined or private
    code: conn = post(conn, password_reset_path(conn, :create), password_reset: @invalid_email)
    stacktrace:
    (phauxth) Phauxth.Confirm.gen_token()
    (alibaba_html_confirm) lib/alibaba_html_confirm_web/controllers/password_reset_controller.ex:11: AlibabaHtmlConfirmWeb.PasswordResetController.create/2
    (alibaba_html_confirm) lib/alibaba_html_confirm_web/controllers/password_reset_controller.ex:1: AlibabaHtmlConfirmWeb.PasswordResetController.action/2
    (alibaba_html_confirm) lib/alibaba_html_confirm_web/controllers/password_reset_controller.ex:1: AlibabaHtmlConfirmWeb.PasswordResetController.phoenix_controller_pipeline/2
    (alibaba_html_confirm) lib/alibaba_html_confirm_web/endpoint.ex:1: AlibabaHtmlConfirmWeb.Endpoint.instrument/4
    (phoenix) lib/phoenix/router.ex:278: Phoenix.Router.call/1
    (alibaba_html_confirm) lib/alibaba_html_confirm_web/endpoint.ex:1: AlibabaHtmlConfirmWeb.Endpoint.plug_builder_call/2
    (alibaba_html_confirm) lib/alibaba_html_confirm_web/endpoint.ex:1: AlibabaHtmlConfirmWeb.Endpoint.call/2
    (phoenix) lib/phoenix/test/conn_test.ex:224: Phoenix.ConnTest.dispatch/5
    test/alibaba_html_confirm_web/controllers/password_reset_controller_test.exs:26: (test)

  5. test reset password fails for incorrect key (AlibabaHtmlConfirmWeb.PasswordResetControllerTest)
    test/alibaba_html_confirm_web/controllers/password_reset_controller_test.exs:30
    ** (UndefinedFunctionError) function Phauxth.Confirm.PassReset.verify/2 is undefined or private. Did you mean one of:

      * verify/3
    

    code: conn = put(conn, password_reset_path(conn, :update), password_reset: @invalid_attrs)
    stacktrace:
    (phauxth) Phauxth.Confirm.PassReset.verify(%{"email" => "[email protected]", "key" => "pu9-VNDGe8v9QzO19RLCg3KUNjpxuixg", "password" => "^hEsdg*F899"}, AlibabaHtmlConfirm.Accounts)
    (alibaba_html_confirm) lib/alibaba_html_confirm_web/controllers/password_reset_controller.ex:27: AlibabaHtmlConfirmWeb.PasswordResetController.update/2
    (alibaba_html_confirm) lib/alibaba_html_confirm_web/controllers/password_reset_controller.ex:1: AlibabaHtmlConfirmWeb.PasswordResetController.action/2
    (alibaba_html_confirm) lib/alibaba_html_confirm_web/controllers/password_reset_controller.ex:1: AlibabaHtmlConfirmWeb.PasswordResetController.phoenix_controller_pipeline/2
    (alibaba_html_confirm) lib/alibaba_html_confirm_web/endpoint.ex:1: AlibabaHtmlConfirmWeb.Endpoint.instrument/4
    (phoenix) lib/phoenix/router.ex:278: Phoenix.Router.call/1
    (alibaba_html_confirm) lib/alibaba_html_confirm_web/endpoint.ex:1: AlibabaHtmlConfirmWeb.Endpoint.plug_builder_call/2
    (alibaba_html_confirm) lib/alibaba_html_confirm_web/endpoint.ex:1: AlibabaHtmlConfirmWeb.Endpoint.call/2
    (phoenix) lib/phoenix/test/conn_test.ex:224: Phoenix.ConnTest.dispatch/5
    test/alibaba_html_confirm_web/controllers/password_reset_controller_test.exs:31: (test)

  6. test reset password succeeds for correct key (AlibabaHtmlConfirmWeb.PasswordResetControllerTest)
    test/alibaba_html_confirm_web/controllers/password_reset_controller_test.exs:19
    ** (UndefinedFunctionError) function Phauxth.Confirm.PassReset.verify/2 is undefined or private. Did you mean one of:

      * verify/3
    

    code: conn = put(conn, password_reset_path(conn, :update), password_reset: @valid_attrs)
    stacktrace:
    (phauxth) Phauxth.Confirm.PassReset.verify(%{"email" => "[email protected]", "key" => "pu9-VNdgE8V9qZo19rlcg3KUNjpxuixg", "password" => "^hEsdg*F899"}, AlibabaHtmlConfirm.Accounts)
    (alibaba_html_confirm) lib/alibaba_html_confirm_web/controllers/password_reset_controller.ex:27: AlibabaHtmlConfirmWeb.PasswordResetController.update/2
    (alibaba_html_confirm) lib/alibaba_html_confirm_web/controllers/password_reset_controller.ex:1: AlibabaHtmlConfirmWeb.PasswordResetController.action/2
    (alibaba_html_confirm) lib/alibaba_html_confirm_web/controllers/password_reset_controller.ex:1: AlibabaHtmlConfirmWeb.PasswordResetController.phoenix_controller_pipeline/2
    (alibaba_html_confirm) lib/alibaba_html_confirm_web/endpoint.ex:1: AlibabaHtmlConfirmWeb.Endpoint.instrument/4
    (phoenix) lib/phoenix/router.ex:278: Phoenix.Router.call/1
    (alibaba_html_confirm) lib/alibaba_html_confirm_web/endpoint.ex:1: AlibabaHtmlConfirmWeb.Endpoint.plug_builder_call/2
    (alibaba_html_confirm) lib/alibaba_html_confirm_web/endpoint.ex:1: AlibabaHtmlConfirmWeb.Endpoint.call/2
    (phoenix) lib/phoenix/test/conn_test.ex:224: Phoenix.ConnTest.dispatch/5
    test/alibaba_html_confirm_web/controllers/password_reset_controller_test.exs:20: (test)

.

Finished in 13.8 seconds
34 tests, 8 failures

Randomized with seed 268880
[alibaba_html_confirm]$

Provide two-factor authentication workflow guide

There is an Phauxth.Otp plug which provides a one-time password check. However, there is currently no guidance on how to integrate this into a web application. Considering that many security issues arise from implementation errors, there needs to be more information about implementation in the wiki and / or documentation.

Multiple user models, authorization and other questions

I guess my issue could be split into multiple parts:

  1. How can I setup authentication for a model other than user?
  2. How can I setup multiple user models (eg. buyer, seller, etc.) using phauxth?
  3. I noticed there is a module called authorization, since this library handles just authentication and not authorization (please correct me if I'm wrong) wouldn't it be appropriate to rename it to authentication.ex instead?
    Thank you.

id_check not working because 'id' in params refers to resource ID

Params contain 'user_id' as my user_id, and 'id' refers to a nested resource in the project I'm working with.

This means I can't use the id_check plug effectively. I'm wondering if there's some way around it. I'm a novice to Phoenix in general, so I'm sure I must be doing something a bit wacky.

The routes look something like this -- http://localhost:4000/users/2/route_details/5

The '2' in this case is (obviously) a user's ID and the '5' is the particular 'route_details' I'm trying to access or delete. My params in this case would look like %{"user_id" => "2", "id" => "5"}

I'm mostly just having issues with deleting things, as the :delete action is one of the things I had going through the :id_check plug. I was tempted to just write a :user_id_check plug, but it seems that this might be considered an issue anyway, so I thought you might be interested to know.

Hopefully this makes some sense. My project isn't very complex -- it's mostly the boilerplate stuff.

Manageable Cookies

I want user able to keep track of where they have logged (remember me) and ability to manually sign out (e.g., lost device).

I'm thinking of adding a :cookies field just like sessions, so user can choose to remove unwanted cookie.

In Phauxth.Remember:

def call(%Plug.Conn{req_cookies: %{"remember_me" => token}} = conn, {opts, log_meta}) do
    case get_user(conn, token, opts)
      |> report(log_meta)
      |> set_user(conn)
    do
      %Plug.Conn{assigns: %{current_user: nil}} ->
        delete_rem_cookie(conn)
      _ ->
        conn
    end
  end

def get_user(conn, token, {max_age, user_context, opts}) do
    with {:ok, user_id} <- Token.verify(conn, token, max_age, opts)
         %{cookies: cookies} = user <- user_context.get(user_id),
         true <- Map.has_key?(cookies, token),
    do
      user
    end
  end

field :cookies, :map

cookies: %{
  "SFMyNTY.eyJzaWduZWQiOjE1MzYwODY4OTEsImRhdGEiOjF9.E8Gk7Gv2mqcLQMh8uyZdXie58fjHU7yu2jHozBFOu5Q" => %{
    "timestamp" => System.system_time(:second),
    "user-agent" => "Mozilla/5.0 (iPad; CPU OS 7_0_4 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Version/7.0 Mobile/11B554a Safari/9537.53",
    "remote_ip" => {127, 0, 0, 1}
  }
}

I think better to store it in a separate table.

Expire token validity only after X idle time

I assume that the default setting of 4 hours max-age for token validity does not take into consideration if the token is actively being used or if it is idle, i.e. it always expires 4 hours after generation (login). How to make it expire only after 4 hours of being idle (not used)?

How to use Phauxth.Token.verify inside connect function of UserSocket

I am trying to verify the token inside:

  def connect(%{"token" => token}, socket) do
    IO.puts token
    case Phoenix.Token.verify(socket, "user salt", token, max_age: 86400) do
      {:ok, user_id} ->
        IO.puts user_id
        socket = assign(socket, :user, Repo.get!(User, user_id))
        {:ok, socket}
      {:error, _} ->
        IO.puts "error"
        :error
    end
  end

But getting errors. How to use Phauxth.Token.verify instead of Phoenix.Token.verify to verify the Phauxth token in this context (channels and sockets)?

Session metadata

Hi,

Love the project and I'm considering ripping out a similar implementation in a project at work for phxauth as it's more complete.

One thing on our design document is some additional session metadata so we can display something akin to https://github.com/blog/1658-view-active-browser-sessions;

  • User agent + OS information (if present)
  • Original sign in source IP (for geo-lookup purposes)

If I put together a patch, would this be something you'd be interested in pulling in? Currently I'm thinking of morphing the sessions field into an embeds_many

Authorization based on a list of user roles

Given a list of roles, I am trying to check if one of the roles is contained in the roles set. Can the wiki include an example?

user.ex schema

schema "users" do
  ...
  field :role, {:array, :string}
  ...
end

user_controller.ex

plug :role_check, [roles: ["admin", "superadmin"]] when action in [:index, :delete]

authorize.ex

def role_check(%Plug.Conn{assigns: %{current_user: nil}} = conn, _opts) do
  auth_error conn, "You need to log in to view this page", session_path(conn, :new)
end
def role_check(%Plug.Conn{assigns: %{current_user: current_user}} = conn, opts) do
  if opts[:roles] && current_user.role in opts[:roles], do: conn,
  else: auth_error conn, "You are not authorized to view this page", user_path(conn, :index)
end

Compile error on simple demo

Just made a new project and grabbed 0.10 for a simple demo, getting the following error on running the migrations for the first time:

== Compilation error on file lib/sesh2/web/controllers/confirm_controller.ex ==
** (UndefinedFunctionError) function Phauxth.Confirm.init/1 is undefined or private
    Phauxth.Confirm.init([])
    (plug) lib/plug/builder.ex:193: Plug.Builder.init_module_plug/3
    (plug) lib/plug/builder.ex:181: anonymous fn/4 in Plug.Builder.compile/3
    (elixir) lib/enum.ex:1755: Enum."-reduce/3-lists^foldl/2-0-"/3
    (plug) lib/plug/builder.ex:181: Plug.Builder.compile/3
    (phoenix) expanding macro: Phoenix.Controller.Pipeline.__before_compile__/1
    lib/sesh2/web/controllers/confirm_controller.ex:1: Sesh2.Web.ConfirmController (module)
    (elixir) lib/kernel/parallel_compiler.ex:117: anonymous fn/4 in 
Kernel.ParallelCompiler.spawn_compilers/1

This is a very basic setup, just followed the guides on the wiki. Steps to recreate:

mix phx.new sesh2
mix phauxth.new --confirm --api
vim mix.exs #add dependency
vim router.ex #add routes
mix ecto.setup

EDIT: So I had a chance to actually sit down and look, and there are no init or call functions in Phauxth.Confirm. I'm guessing this is expected? Should we be whipping up our own version of these depending on what method (email/sms) we want to use?

Use `context_app` config when determining the `user_context`

I have an umbrella application with my business logic in an app named AppExample, and my web application in an app named AppExampleWeb. This means that my user_context is App.Accounts, instead of AppWeb.Accounts, the default that Phauxth uses. This is fixable by overwriting the user_context config for Phauxth, but Phoenix actually already has a configuration field that specifies what application your contexts live in, for the purpose of using generators:

config :app_example_web,
  generators: [context_app: :app_example]

Instead of defaulting the user_context to the current application, you could instead see whether the generators are configured in such a manner.

v0.10 installer not working

I tried to install v0.10 and the installer didn't work. I am getting errors like:

[error] beam/beam_load.c(1287): Error loading module 'Elixir.Mix.Tasks.Phauxth.New':
  mandatory chunk of type "Atom" not found
somepath/phauxth_new/ebin/Elixir.Mix.Tasks.Phauxth.New.beam failed: :badfile

I'd also like to take the chance to thank you for your great work on comeonin, openmaize, phauxth and other projects.

Set up a way to donate to this project

This is my favorite Authentication solution I've found by far. @riverrun can you set up some kind of way for people to donate to this project? I 'd like to see it succeed and if getting you paid some helps that then I'm in.

Invalid credentials

Hi.
Thanks for phauxth..
Where can I find && alter this message: Invalid credentials
Cant find it..

Greetings

Return 401 when User token not authenticated

I've started using this and, firstly, just want to say it's a very nice library. I have one question though: I would've expected that the Authenticate plug would return a 401 status when a current_user wasn't found. Is there a way to easily extend the plug to do this? Or do I just need to write another plug function to check if current_user is nil?

Thanks again for the library!

Installer is too destructive

By that I mean it overwrites a bunch of files, which makes it a bit harder to use on an existing project. Instead it would be sweet if it acted like the new phx.gen.context mix task that only appends to the file instead of overwriting.

This may or may not be hard, I'm not sure how to best go about this. Either some sort of string manipulation, but the best would be to actually parse the code, modify the AST, and render it back again. Perhaps one could use macros to great effect here. Or look at how the various code formatters/linters do it.

Anyway, I do love it though. After a bit of manual restoration (while still keeping the important changes) it works very nice.

Consider providing cookie / session based mechanism for API applications

The current behavior of phauxth installer is to provide either:

  1. Session based authentication if the application is HTML based.
  2. Token based authentication if the api switch is used.

It would be nice to have additional switch for the installer to specify a JSON API setup based on session / cookie storage (not tokens) since storing the token inside a session cookie provided best security (compared to storing it for example in local storage by the client side JS application).

You are welcome to join the discussion here:
Sending cookies for stateless SPA authentication using JWT

funny results for get /users/:id

def show(%Plug.Conn{assigns: %{current_user: user}} = conn, _) do<%= if api do %>
render(conn, "show.json", user: user)<% else %>
render(conn, "show.html", user: user)<% end %>
end<%= if not api do %>

Regardless of the url (/users/1, /users/2,…) it always shows the current_user (same for the other actions). Not exactly what I would expect.

Is the stage too early for my expectations?

Make it easier to add custom metadata to the log functions

When calling the Plugs and verify functions, it should be easy to add custom metadata to the Phauxth.Log output.

Proposal

  • Add meta second argument to each of the Report functions.
  • Add log_meta option to the verify functions.
    • This means that the Confirm and Confirm.PassReset function will have opts as the third argument (like previous versions).
  • Add log_meta option to Authenticate / Remember Plugs.

Allow to translate strings via GetText

Hello,
First of all, thank you for working on phauxth, I'm really enjoying using it !

The library still has some hardcoded (english) strings in some places, like in https://github.com/riverrun/phauxth/blob/master/lib/phauxth/confirm/report.ex .

While this is not an issue for logs, some of theses messages appears for the end-user, so it needs to be translated in some way.

A nice developper experience would be to allow the library user to translate those string from its app .po files, like any other string. Being able to use the gettext mix tasks like mix gettext.extract would be a plus.

I could help with this, but I'm not sure how you'd want to approach this.

The Coherence library, for exemple, does this by implementing a Messages behavior (https://github.com/smpallen99/coherence/blob/bcff8bc6276596496e66da3376c8a8da2fe4c185/lib/coherence/messages.ex) that's implemented from the user app side (https://github.com/smpallen99/coherence/blob/bcff8bc6276596496e66da3376c8a8da2fe4c185/priv/templates/coh.install/coherence_messages.ex).

Redis backend, questions

Sorry if this is a silly question but I couldn't find any other way to ask it, and there was no information in the wiki.

Do I understand it right that:

  • phauxth is using server stored sessions vs. the client stored JWT method used by guardian?
  • If using server stored sessions, where does it store them? In the normal PostgreSQL database, running one SQL query per each request?
  • If so, would it be possible to use Redis in the future for session storage?
  • When using with a REST API, I see that session create is returning a JSON which the client can store. How does reading of the session work in case of API clients?

Phoenix.Router.NoRouteError

When you initially enter invalid user credentials, it redirects you back to /sessions/new. Immediately after, you enter in valid user credentials, you are then redirected to /sessions and receive a no route error.

Steps to reproduce:

  1. Create a user (email: "[email protected]", password: "password")
  2. Go to /sessions/new
  3. Enter invalid credentials (ex: "[email protected]", password: "foobar")
  4. Receive Invalid credentials error message
  5. Enter valid credentials
  6. You are redirected to /sessions and receive a no route found for GET /sessions error

Phauxth.Confirm.Report pattern matching

Thanks for developing Phauxth. I've added the library incrementally to my project; I started by running mix phauxth.new a while back, and today manually pulled in code generated by the --confirm flag.

While testing password resets I kept receiving the error message "user already confirmed" which was confusing. After digging I realized I was matching into line 27 of Phauxth.Confirm.Report:

def report(%{} = user, _, meta) do
    Log.warn(%Log{user: user.id, message: "user already confirmed", meta: meta})
    {:error, Config.user_messages().already_confirmed()}
  end

because my users table did not have the reset_sent_at column.

I'm guessing others may also upgrade incrementally in the future. Although I recognize I didn't follow the recommended installation method, it would have helped debugging if the pattern matching in Phauxth.Confirm.Report was more specific and an exception occurred -- happy to submit a small PR if you think this would be a good addition

Questions regarding 2.0

Hi David,
I understand you're pretty busy these days, and just wanted to check with you if 2.0 can be used in production? I know it has a lot of changes under the hood, but just wanted to double check before I use it in any client projects :)

Default user

It would be a good idea to have either a default user that is created upon installation or a mix task to create such a user.

Right now I had to comment out the plug Phauxth.Authenticate in the router.ex to bypass the authentication so that I can create an account the very first time.

Comeonin.Bcrypt.check_pass/3 is undefined (module Comeonin.Bcrypt is not available)

I can't seem get the Login to work with pbkdf2. It is still finding the Bcrypt reference somewhere even though I never added it.
mix phauxth.new

Mix.exs
defp deps do
[
{:phoenix, "> 1.3.0"},
{:phoenix_pubsub, "
> 1.0"},
{:phoenix_ecto, "> 3.2"},
{:postgrex, ">= 0.0.0"},
{:phoenix_html, "
> 2.10"},
{:phoenix_live_reload, "> 1.0", only: :dev},
{:gettext, "
> 0.11"},
{:phauxth, "> 1.2"},
{:pbkdf2_elixir, "
> 0.12"},
{:bamboo, "> 0.8"},
{:cowboy, "
> 1.0"}
]
end

user.ex
defp put_pass_hash(%Ecto.Changeset{valid?: true, changes:
%{password: password}} = changeset) do
change(changeset, Comeonin.Pbkdf2.add_hash(password))
end

session_controller.ex
def create(conn, %{"session" => params}) do
case Login.verify(params, Accounts) do
{:ok, user} ->
session_id = Login.gen_session_id("F")
Accounts.add_session(user, session_id, System.system_time(:second))
Phauxth.Login.verify(params, Alibaba.Accounts, crypto: Comeonin.Pbkdf2)
Login.add_session(conn, session_id, user.id)
|> login_success(user_path(conn, :index))
{:error, message} ->
error(conn, message, session_path(conn, :new))
end
end

Request: POST /sessions
** (exit) an exception was raised:
** (UndefinedFunctionError) function Comeonin.Bcrypt.check_pass/3 is undefined (module Comeonin.Bcrypt is not available)
Comeonin.Bcrypt.check_pass(%Alibaba.Accounts.User{meta: #Ecto.Schema.Metadata<:loaded, "users">, email: "[email protected]", id: 3, inserted_at: ~N[2018-02-23 14:09:14.155021], password: nil, password_hash: "$pbkdf2-sha512$160000$lsXaMTkXhwirR.TXArvc4Q$Mrl8FKhXh7kMRdhbZB/B99BQ9c05p9q3bxgwf7HW6cdQDYkqhYBwBCyBdebMpaCMCmVmrbxvmULIjI1Apj6n3Q", sessions: %{}, updated_at: ~N[2018-02-23 14:09:14.157897]}, "password", [])
(phauxth) lib/phauxth/login.ex:8: Phauxth.Login.verify/3
(alibaba) lib/alibaba_web/controllers/session_controller.ex:18: AlibabaWeb.SessionController.create/2
(alibaba) lib/alibaba_web/controllers/session_controller.ex:1: AlibabaWeb.SessionController.action/2
(alibaba) lib/alibaba_web/controllers/session_controller.ex:1: AlibabaWeb.SessionController.phoenix_controller_pipeline/2
(alibaba) lib/alibaba_web/endpoint.ex:1: AlibabaWeb.Endpoint.instrument/4
(phoenix) lib/phoenix/router.ex:278: Phoenix.Router.call/1
(alibaba) lib/alibaba_web/endpoint.ex:1: AlibabaWeb.Endpoint.plug_builder_call/2
(alibaba) lib/plug/debugger.ex:99: AlibabaWeb.Endpoint."call (overridable 3)"/2
(alibaba) lib/alibaba_web/endpoint.ex:1: AlibabaWeb.Endpoint.call/2
(plug) lib/plug/adapters/cowboy/handler.ex:15: Plug.Adapters.Cowboy.Handler.upgrade/4
(cowboy) /home/dankmeme/elixir/alibaba/deps/cowboy/src/cowboy_protocol.erl:442: :cowboy_protocol.execute/4

installer fails with errors


17:23:40.751 [error] beam/beam_load.c(1287): Error loading module 'Elixir.Mix.Tasks.Phauxth.New':
mandatory chunk of type 'Atom' not found




17:23:40.759 [error] Loading of ~/.mix/archives/phauxth_new/phauxth_new/ebin/Elixir.Mix.Tasks.Phauxth.New.beam failed: :badfile


17:23:40.772 [error] Loading of ~/.mix/archives/phauxth_new/phauxth_new/ebin/Elixir.Mix.Tasks.Phauxth.New.beam failed: :badfile


17:23:40.772 [error] beam/beam_load.c(1287): Error loading module 'Elixir.Mix.Tasks.Phauxth.New':
mandatory chunk of type 'Atom' not found




17:23:41.070 [error] Loading of ~/.mix/archives/phauxth_new/phauxth_new/ebin/Elixir.Mix.Tasks.Phauxth.New.beam failed: :badfile


17:23:41.071 [error] beam/beam_load.c(1287): Error loading module 'Elixir.Mix.Tasks.Phauxth.New':
mandatory chunk of type 'Atom' not found



Generated project app

17:23:41.585 [error] beam/beam_load.c(1287): Error loading module 'Elixir.Mix.Tasks.Phauxth.New':
mandatory chunk of type 'Atom' not found




17:23:41.589 [error] Loading of ~/.mix/archives/phauxth_new/phauxth_new/ebin/Elixir.Mix.Tasks.Phauxth.New.beam failed: :badfile


17:23:41.868 [error] Loading of ~/.mix/archives/phauxth_new/phauxth_new/ebin/Elixir.Mix.Tasks.Phauxth.New.beam failed: :badfile


17:23:41.868 [error] beam/beam_load.c(1287): Error loading module 'Elixir.Mix.Tasks.Phauxth.New':
mandatory chunk of type 'Atom' not found```

This is on macOS X sierra, not sure that it makes any difference. Had tried to setup openmaize on phoenix 1.3 then realised you'd created this to cover the new phoenix.

Flash message missing on successful password change

I noticed that the success flash message is missing after a successful password change. I believe this is due to the configure_session(conn, drop: true) on line: https://github.com/riverrun/phauxth/blob/master/installer/new/templates/password_reset_controller.ex#L45

Is this because drop: true causes the response to drop the Phoenix flash messages? I did some testing with IEx.pry, and the flash message is still in the conn when the controller action exits. So, perhaps this is happening after.

In any case, I was able to get the flash message to show up by adding the renew: true option to configure_session/2 (https://hexdocs.pm/plug/1.4.3/Plug.Conn.html#configure_session/2). This apparently "generates a new session id for the cookie." So, I'm not sure this is what everyone wants. But, I am using it to get the message to show.

If this is something you'd like a PR for, I'd be happy to assist.

Phauxth and Umbrella Projects

Hi David,
I would like to use Phauxth in an advanced project that will have both html (for web users) and api (for Rest client App) authentication, and I'm wondering which is the best workflow/approach for setting up the Phauxth stuff.

Ideally, the umbrella project should have this kind of structure:

my_umbrella
|__ apps
    |__ core
    |__ core_web
    |__ phauxth
    |__ phauxth_api_web
    |__ phauxth_html_web

where core and core_web will contains the app specific stuff, phauxth will contain the shared Phauxth Ecto Context, phauxth_api_web contains the rest api authentication server, and phauxth_html_web contains the html frontend app server for accounts administration.
I think that having two separated Phauxth app for html and api it should be easier to mantain than having a single hybrid app.

I would like to be able to use the Phoauxth installer to set up the Context, Api and Html CRUD and Tests stuff rather than doing it by hand, doing something similar to:

$ mix new my_umbrella --umbrella && cd my_umbrella/apps

$ mix phx.new.ecto core
$ mix phx.new.web core_web

$ mix phx.new.ecto phauxth
$ mix phx.new.web  phauxth_api_web --no-html --no-brunch
$ mix phx.new.web  phauxth_html_web

$ cd phauxth          && mix phauxth.new --ecto --confirm
$ cd phauxth_api_web  && mix phauxth.new --api  --confirm --context phauxth
$ cd phauxth_html_web && mix phauxth.new --html --confirm --context phauxth

But currently, the Phoauxth installer cannot be used neither for setting up an hybrid html+api app (as it hasn't a switch for specifying a desidered namespace) nor for setting up an umbrella app (if used with an umbrella app seems that it mess up its files).

Do you think that a) in a future the installer will support a sort of --namespace foo switch for namespacing the generated files, and b) that there will be a support for umbrella projects?

In the mean time, can you give us some hints for which can be the best workflow to setting up Phauxth things with the current version of the installer in an umbrella project in general and in particular like the one above?

Accept plug's 'extra' opt.

The add_rem_cookie function should accept plug's 'extra' option, one might want to add additional headers to their rem cookies, i.e. "SameSite=Lax".

Issue with how report/3 is called from login/base.ex:86

I'm looking at how I can add custom login logic as shown by the Phauxth.Confirm.Login.

I've implemented my own custom login module which pulls in the base via use Phauxth.Login.Base

defmodule Beffect.Accounts.Login do
  use Phauxth.Login.Base
  alias Beffect.Accounts.User

  # This is the custom logic path I'm running into problems with. 
  def check_pass(%User{active: false}, _, _, _) do
    {:error, "account locked"}
  end

  # this custom logic works fine because the report method to handle that is already defined via 
  # https://github.com/riverrun/phauxth/blob/a61a2f3ac2d9a9334de571561dc0371af2484b8c/lib/phauxth/login/base.ex#L130

  def check_pass(%User{invite_confirmed_at: nil}, _, _, _) do
    {:error, "account unconfirmed"}
  end

  def check_pass(user, password, crypto, opts) do
    super(user, password, crypto, opts)
  end
end

The problem I'm running into is that if I want to output a custom message for my active: false path I run into the problem that I can't easily define the report handler function like the ones already in the Phauxth.Login.Base ones.

IE: If I try to add a report method like this to my Beffect.Accounts.Login I get an conflict compile time error.

defmodule Beffect.Accounts.Login do
   ....
     alias Phauxth.Log
     def report({:error, "account locked"}, _, meta) do
        Log.warn(%Log{message: "account locked", meta: meta})
        {:error,  "Account Locked!"}
    end
  ...
end

Produces:
(CompileError) lib/beffect/accounts/login.ex:33: imported Phauxth.Login.Base.report/3 conflicts with local function

My question is:

1: where should I be defining the custom report/3 functions.
2: is there a way we can decouple the Phauxth.Login.Base methods from custom logic paths like Confirm. Not sure having the report methods there is the best place for them.

Can't find any backup files when running installer

I ran the installer with an existing app (mix phauxth.new --confirm --remember) and whether I prompted "y" or "n" when asking if I wanted to overwrite files, I couldn't find any backup files.
Thank you for Phauxth.

Authenticate subsequent requests with token only (without user_id)

I have noticed that authentication works for subsequent requests (after login) by sending authorization header and user_id as parameter.

  1. Is it possible, taken also security considerations into account, to authenticate subsequent requests with authorization token only?

  2. If user_id must be submitted with every subsequent request, how to obtain it from the server? On successful login, the user gets access_token only. Is it safe to return the user_id with that response also?

Thank you.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.