What are the advantages of using guardian_db?

I understand that using a database storage for authentication tokens as provided by guardian_db:

a. makes it possible to revoke tokens server-side / especially in case a clients token had been stolen
b. makes it possible to limit valid tokens per user to say 1 at each given time

Are there any additional advantages beyond these? I will be happy if you could share your ideas / experiences regarding this. Thank you.

2 Likes

Honestly, guardian_db breaks the contract that JWT is supposed to hold, which is a time-limited, database-less token with information. If you even have a database to hold and verify it with then you probably don’t need guardian at all.

4 Likes

So in that case what is the other options to handle the authentication? ^^

2 Likes

Uhh, but guardian doesn’t handle authentication. o.O

Things like Ueberauth (any form of authentication) or Coherence (identity authentication only, but lots of templates pre-built) is generally what is used.

Guardian can pass authentication or authorization information (or not, or other information, or whatever) between servers that do not share the same security information. If you only have one server, or the servers share the same security information, then there is absolutely no point to using Guardian, just use Phoenix.Token as its already built in, smaller, faster, more efficient, easier API, etc


In essence, if you don’t know what JWT is or you don’t need it, then Guardian is not useful (and use Guardian handles more than just JWT now, but similar styles, if you still don’t know what JWT is, you don’t need Guardian).

I am curious though, where are you getting the ‘handle the authentication’ part of Guardian from, it does no username/password validation, no OAuth, no anything of the sort, it is just a JWT-based information storage library. The information you store in it you have to get from somewhere else anyway.

3 Likes

Oh sure I think I chose an incorrect expression (Sorry I still improving my english language skills)

I understand the fact that Guardian just encode the data that you give

Normally Guardian and authentication is mentioned together, as I understand you do all the auth login logic with Elixir itself (password/email validation etc
) and you encode the data to recognize that user with JWT and send the result token to the client, the client uses that given encoded data as a header to be identified on server in every request (I saw this case more in SPA applications and is the only place I saw it, but I’m sure there is more options to sue it) just like if it is authenticated and can access to some information in the server

But this way to “store” information with JWT has the problems that are described in this post (token stolen etc
), so my question is more related to it, is there better ways to handle this “token client identification” in a better way? (or rather, what are the options to solve the problem described here?)

2 Likes

For me, I have already use Guardian along an Elixir OAuth2 library for Goggle users, Phoenix user model and table storing passwords for email/password authentication on the back-end. I need the JWT token since my Phoenix application is an API only application accessed by a VueJS SPA. Works great. I guess this is a reasonable use of Guardian in such context @OvermindDL1? Correct me if I am wrong, thanks.

1 Like

That is very very heavy. There is no need to ever send a JWT round-trip from client back to yourself, that is just excess wasted efficiency for no gain whatsoever, this is when Phoenix.Token should be used.

Phoenix.Token, it’s already built-in to phoenix. ^.^

Are you just connecting back to your own server? If so then no, this is not even remotely a good usage of JWT. ^.^;

JWT is for encoding data to be used by other servers that you do not control, not for passing data back to yourself.

2 Likes

So, you are saying that my application can identify users using Phoenix token and bind API calls to their authorized resources only by Phoenix token sent in request headers?

2 Likes

Yes, this is precisely the point of them, and hence why they are built in. ^.^

Just sign/encrypt a token using the single function, stuffing in whatever data you want (which takes up much less space than JWT’s), and make a plug to take in the token however you send it and then just call decrypt on it, you have the data back in exactly the way you sent it.

3 Likes

You mean creating a router pipeline with plug that looks for token in request data, verify it and thus becoming a port for accessing protected resources?

1 Like

A port? Unsure of the terminology there.

But bog-standard plug, the same way you would do any other, nothing special. ^.^

EDIT: There’s even a library of pre-built plugs to handle all the common cases of phoenix token usage too (but it is so trivial to write your own for your own customized usage that there is usually no point):

4 Likes

Based on @OvermindDL1 thoughts I have managed with the help of some online tutorial to implement this:

defmodule  AcrosapWeb.Authenticate do
  import Plug.Conn
  import Ecto.Query

  @auth_scheme "Bearer"

  def init(options) do
    options
  end

  def call(conn, _) do

    if get_req_header(conn, "authorization") == [] do
      conn |> put_resp_header("content-type", "application/json; charset=utf-8") |> send_resp(401, Poison.encode!(%{error: "Unauthorized"}, pretty: true))
    else
      conn
    end

    result = conn
             |> get_req_header("authorization")
             |> List.first
             |> String.slice(String.length(@auth_scheme) + 2..-1)
             |> validate_token()

    case result do
      :invalid_token ->
        conn |> put_resp_header("content-type", "application/json; charset=utf-8") |> send_resp(401, Poison.encode!(%{error: "Unauthorized"}, pretty: true))
      { :authenticated, user } ->
        conn |> assign(:current_user, user)
    end
  end

  def validate_token(token) do
    case token do
      nil -> :invalid_token
      _ ->
        {result, user_id} = Phoenix.Token.verify(AcrosapWeb.Endpoint, "user salt", token, max_age: 86400)
        case  {result, user_id} do
          {:error, :invalid} -> :invalid_token
          _ -> { :authenticated, user_id }
        end
    end
  end
end

And use it in router.ex pipeline definition.

Is this the way to do it, would you suggest some improvements for this code? Also I want to ask about user_salt, where to store it? As environmental variable on application level?

I appreciate you points, you have way more experience than me. Yet I made some benchmarks :slight_smile:

Using Guardian and Guardian DB togother makes the response time longer:
~100ms for generating a token compared to using Phoenix.Token
~100-300ms on subsequent resource requests, since the token is DB checked on every request.

Important
My Phoenix application is located some 3,000 km away from where the PostgreSQL DB is located, of course that has some impact on the results.

However, when Guardian DB is put aside, there is no response time differences between Guardian and Phoenix.Token.

Anyway, the whole discussion was informative and I learned some new stuff.

You can use both guardian and phoenix token database-less if you want as well. That is why you can store information in the phoenix token after all. ^.^

1 Like

Exactly, that is a clear advantage, to use data stored in the token without querying the database. I think it all depends on the needs. In certain situations, it is inevitable to have a database token storage e.g. if server side token revoking is needed.

But I am still curios, if I use a plain token / guardian authentication (with no database token storage) and I am implementing some role-based authorizations (Enum for role ids: 1: admin, 2: normal user etc) stored in Users table, would not the Users table be queried for the role_id of the connection assigned user on each http request (I believe that assigning user’s object based on received token to the connection, loads user’s data from database on each request, correct)?

Or is it fine to skip this database checking for role_id by encoding the role_id itself inside the token? Is that safe? There is no possibility that a token could be tampered with to change a role_id (from say normal to admin), for example?

I know this might be basics :slight_smile:

Yep if you need to revoke it then you need some kind of datastore to state whether it is revoked or not, you cannot update the client’s token without a realtime communication after all. ^.^

Only if your code does so. Guardian and Phoenix.Token’s are just storage, whatever you do with that storage is all up to you.

The token could not be tampered with, that is the point of the Phoenix encryption “secret” string. :wink:

2 Likes

You should checkout Phauxth. You can easily extend it to support role based authorization. You can mix and match session and token authentication via plugs. Honestly, it’s easy and amazing. And Phoenix is amazing. :slight_smile:

1 Like

I had implemented it before. It was easy to work with. I gave Guardian a try to take advantage of GuardianDB. I have to decide if server-side revoking is that important, if so GuardianDB takes care of that out of the box.

As to role based authorizations, I had success with a library called BodyGuard, worked fine. I will look into Phauxth in this regard, as for me, I am learning more about Elixir and Phoenix with every additional library I test.

As for authorization, I have checked all available libraries, they all authorize against the User object (i.e. load the user from the database and check against role_id etc.). There is currently no contributed library relying on data encoded in tokens alone for issuing authorizations.

Not like that is hard though, like for Phoenix.Token just store a couple of atoms indicating the permissions allowed, or if space as at a premium then encode it in an integer. ^.^

1 Like