User authentication in Phoenix?

authentication
phoenix
#30

I’ve tried Addict and Openmaize in a Phoenix application.

I didn’t like Addict too much because it relies heavily on JavaScript (for example, on the redirections).

Instead, I chose Openmaize – it’s very well documented, doesn’t rely on JS, and provides signup/login/logout/password reset features out of the box. Ah, and two-factor authentication as well.

12 Likes
#31

Coherence is a new library by @smpallen99: https://github.com/smpallen99/coherence

Note: This is an early release, under active development. But it looks very promising so far.

9 Likes
#32

I would love to get feedback on Coherence. If you give it a try, let me know how it works for you. And more importantly, if it doesn’t, I’ like to know why.

Steve

6 Likes
#33

Hello! It seems to be very promising. It is the best authentication package, it follows the “convention over configuration”. It should only enlarge.

How can I make a user only modify their own data?

1 Like
#34

@claudiojulio Thanks for the feedback. Controlling who can access what resource is the role of authorization. Coherence is a user management and authorization solution. You will need an authorization package like canary for this.

I will soon add a something about using canary with coherence to either the wiki or the readme.

Steve

4 Likes
#35

Coherence seems great :+1:

1 Like
#36

I use ueberauth/guardian. Specifically ueberauth for LDAP (custom module) and GMail login. Guardian for API JWT tokens, and phoenix session tokens for normal user interface (because it has to hit the DB/Cache to access the significantly more detailed permission set on normal access where-as ‘most’ API calls do not need to via the JWT token).

Presence is actually quite nice as it is, I do use it to watch ‘Users’ in some, but I also have some key’d on server-to-server connections and more as well. I would not want it tied to a User format as it already supports that perfectly as it is, while also supporting a generic use-case. :slight_smile:

I made a detailed generic permission system that works seemlessly wonderfully with canary: https://hex.pm/packages/permission_ex

I have a database that feeds a time-based cache for holding the permission structures. But I can define my permissions like:


defmodule MyServer.Perms.Help do
  @behaviour MyServer.PermsBehaviour
  defstruct action: nil, uid: :_

  def get_as_new(), do: %MyServer.Perms.Help{action: "", uid: :_}
end


defmodule MyServer.Perms.Help.Issue do
  @behaviour MyServer.PermsBehaviour
  defstruct action: nil, id: :_

  def get_as_new(), do: %MyServer.Perms.Help.Issue{action: "", id: :_}
end

That with my Canada conn handler will grab from the cache/db so I can do things (via the Happy module as well) like:

  def delete_tag(conn, %{"id" => id_param, "tag" => tag_param}) do
    happy_path!(else: handle_error(conn)) do
      {id, ""} = Integer.parse(id_param)
      @perm true = conn |> can?(delete_tag(%Perms.Help.Issue{id: id}))
      @param {:ok, tag_id} when is_integer(tag_id) = case Integer.parse(tag_param) do
        {tag_id, ""} -> {:ok, tag_id}
        _ -> {:ok, Repo.one!(query_tag_by_name(tag_param)).name}
      end
      Repo.delete_all(from it in Issue.Tag, where: it.issue_id == ^id and it.tag_id == ^tag_id)
      conn
      |> put_flash(:info, gettext("Successfully removed tag"))
      |> redirect(to: help_issue_path(conn, :show, id))
    end
  end

  def edit(conn, %{"id" => id}) do
    happy_path!(else: handle_error(conn)) do
      @perm true = conn |> can?(edit(%Perms.Help.Issue{}))
      issue = Repo.get!(Issue, id)
      @perm true = conn |> can?(edit(%Perms.Help.Issue{id: issue.id}))
      changeset = Issue.changeset(issue)
      render(conn, :edit, issue: issue, changeset: changeset)
    end
  end

Or other things like that. Basically canada’s :action is autofilled into a permission if an action field exists on it and action is not null from canada, and any other arguments are as passed in to the permission structure. This is then run through my cache to get the permission of the user and then tested. My conn handler (I have others too) is:

defimpl Canada.Can, for: Plug.Conn do # Some stuff stripped for brevity

  alias MyServer.PermissionHelper

  def can?(conn, nil, req) do
    user = UserHelper.get_from_conn(conn)
    is_user_authorized?(req, user)
  end
  def can?(conn, action, %{action: _old_action} = req) do
    user = UserHelper.get_from_conn(conn)
    is_user_authorized?(%{req | action: action}, user)
  end

  defp is_user_authorized?(required, nil) do
    is_perms_authorized?(required, PermissionHelper.get_permissions(nil))
  end
  defp is_user_authorized?(required, %MyServer.LDAPUser{uid: uid}) do
    # TODO:  The `required` var might be a list do remember!!!  Not used yet, but someday...
    is_perms_authorized?(required, PermissionHelper.get_permissions(uid))
  end

  defp is_perms_authorized?([required], perms) do
    is_perms_authorized?(required, perms)
  end
  defp is_perms_authorized?({:any, required}, perms) do
    required
    |> Enum.any?(&is_perms_authorized?(&1, perms))
  end
  defp is_perms_authorized?({:all, required}, perms) do
    required
    |> Enum.all?(&is_perms_authorized?(&1, perms))
  end
  defp is_perms_authorized?(required, perms) do
    PermissionEx.test_tagged_permissions(required, perms)
  end

And of course if anyone sees bugs please tell me. :slight_smile:

9 Likes
#37

I just pushed the CoherenceDemo canary branch to show an example of how to add Authorization to a project using Coherence.

4 Likes
#38

Thanks for contributing man!

I am new to phoenix and I am looking at user authentication and there’s nothing like Devise here until I found Coherence. :slight_smile:

Most of the tutorials found online are"roll-your-own" authentication methods using comeonin or using ueberauth

1 Like
#39

Thanks for the detail example. I began my project using Ueberauth/Guardian. But after reading code and examples, I decided I am just not smart enough yet to understand what I need to do to implement a full authentication solution using it.

Looking at your Coherence & Canary demo, I feel initially more confident. However, I could not determine if Coherence supports authentication mechanism for Phoenix Channels.

I need to ensure socket messages identify an authenticated user. I believe I can use Canary to authorize the resulting action from the socket message. But how can Coherence be used to pass a token in the message? Or is this an inappropriate usage scenario?

#40

I have to bitch a little about this too, doing user auth. with phoenix(oauth) for the first time was painful to say the least… It took me about 10x as long as first time with django or x js framework and I am still not sure about the damn thing :slight_smile:

Also I could find just 1 example including react…

2 Likes
#41

I believe it’s possible to use Ueberauth with Coherence through ueberauth_identity package. From there, you can still use guardian and what not. Anyway, I’m about to try just that.

1 Like
#42

Is it just me or is using Ueberauth + Guardian really complicated? Or it could be that there aren’t enough resources that teaches you how to use them. I couldn’t get them to work by just reading the documentations. Phoenix Guardian Demo Application led me to the right direction but it is a lot to digest without any blog posts to compliment it. Here is what I needed to get them to work together:

 28 files changed, 678 insertions(+), 35 deletions(-)
 create mode 100644 priv/repo/migrations/20160910210757_create_user.exs
 create mode 100644 priv/repo/migrations/20160910214502_create_authorization.exs
 create mode 100644 priv/repo/migrations/20160910223402_create_guardian_db.exs
 create mode 100644 web/auth/guardian_serializer.ex
 create mode 100644 web/auth/user_from_auth.ex
 create mode 100644 web/controllers/auth_controller.ex
 create mode 100644 web/controllers/helpers.ex
 create mode 100644 web/controllers/signup_controller.ex
 create mode 100644 web/models/authorization.ex
 create mode 100644 web/models/guardian_token.ex
 create mode 100644 web/models/user.ex
 create mode 100644 web/models/user_from_auth.ex
 create mode 100644 web/templates/auth/login.html.eex
 create mode 100644 web/templates/layout/login_bar.html.eex
 create mode 100644 web/templates/signup/new.html.eex
 create mode 100644 web/views/auth_view.ex
 create mode 100644 web/views/helpers.ex
 create mode 100644 web/views/signup_view.ex

I believe a lot of the implementation can be put into the libraries with the option to have custom implementations or the approach Coherence seems to be taking by using generators.

5 Likes
#43

It is not that it is complicated, just that it is generic. It assumes nothing about your web server, if you even use a web server, or anything of the sort. It is a general library to handle essentially any form of auth, such as I use Google Auth and I built an LDAP auth that is used in mine through ueberauth, I’ve not seen that being possible in any other library yet. If you need the power then it is very nice. If you are making a simple sign-up site with no external links then eh, not needed.

As for Guardian, it is an awesome JWT library, and I use it for sockets and API endpoint, I ‘barely’ use it in my app (just as a ‘holder’ for a better lookup) and I instead use my PermissionEx library with the Canada library for permission handling in pages.

4 Likes
#44

@mayppong any update on Ueberauth + Coherence + ueberauth_identity ?

did this combination work for you?

1 Like
#45

Sort of. It’s kind of working but I haven’t actually move any further than “oh it’s working, let’s move on to other part of the project now”.

I didn’t use Guardian though since I would have to also used hassox/guardian_db to prevent replay attack and consequently have to migrate couple more tables. It became a giant mess of many dependencies and tables very quickly. I haven’t found any good solution suggested short of more tables and more background processes.

I sort of stuck with Coherence once Ueberauth handed the request over. That said, I also made a package, birbnest, for storing session in an Agent on server-side instead of worrying about JWT stuff. It’s not really actively maintained since it’s a pet project and it’s not hard to hand-rolled yourself tbh. In any case, JWT wasn’t worth the effort for what I needed.

#46

this DOES feel like a mess imho, I just cracked my head on
guardiandb guardian ueberauth and now starting canary … I wish coherence
and exadmin had a bridge to later

surely one hex could combine all this in a standard way?

#47

Too many ways to do authorization though. There can be plug authorization, fine-grained permission that changes based on what is grabbed from the DB (like mine), etc… etc…

#48

I do agree, somewhat. Maybe not one hex to rule them all, but something like needing guardian and guardian_db seems silly. To me, this replay attack is a bug and guardian needs to be fixed. The fact that guardian_db has existed for so long and not folded into guardian yet doesn’t inspire confidence in Ueberauth.

That said, I’m not into the JWT anyway so I’m probably pretty bias against guardian already.

#49

Guardian_db is for when you are using a JWT as a session container, which you should not really be doing. JWT’s are intended for stateless API connections, the session should be used for stateful connections like HTTP.