njwest

njwest

Phoenix 1.3 JWT Auth API with Guardian JWTs, password hashing

Greetings: I just wrote a step-by-step guide on building a Phoenix 1.3 JWT Auth API with Guardian JWTs and Comeonin password hashing.

I would love some feedback, as I’m sure there are some things I missed/things I can improve!

Medium Article: Phoenix JWT Auth API with Guardian

Github Repo

Also: this is Part I of a two-part series; for my next trick, I’ll show how to build a React Native client for this API :slight_smile:

Most Liked

voger

voger

I went through the tutorial. Good timing. I am trying to learn how to use Guardian and this article is very comprehensive. I like how you explain things along the way. I also picked a couple of tricks. Thanks.

I do have some feedback. I hope I am not being that guy hung in every minor detail :lol: .

  1. Ecto creates databases with mix ecto.create not mix ecto.migrate.

  2. When creating Accounts.Users add in routers

  scope "/api", MyApiWeb do
    pipe_through :api
    resources "/users", UserController, except: [:new, :edit]
  end

before running mix ecto.migrate otherwise it throws CompileError about undefined
user_path/3

  1. Personally I prefer file names as lib/myApi/accounts/user.ex instead of user.ex in lib/myApi/accounts/. I think it is easier to follow.

  2. The first listing of put_pasword_hash(changeset)... and later import Comeonin.Bcrypt… trim our put_change function:.. The second listing of put_password_hash has no difference from the first listing

  3. Mention that we need to alias myApi.Guardian inside our user_controller.ex, before altering the create/2 function, otherwise it tries to use the Guardian module from the guardian library

  4. Inside the create controller why do we need a with inside a with? It works with

    with {:ok, %User{} = user} <- Accounts.create_user(user_params),
         {:ok, token, _claims} <- Guardian.encode_and_sign(user) do
    # magic ...
    end

or I am missing something in the flow of the code?

  1. You create the sign_in/2 inside the UserController. Shouldn’t the session be handled by a separate SessionController?

  2. show/2 in UserController why does it need the if? The conn passes through LoadResource and EnsureAuthenticated so the user is already authenticated and loaded inside the conn. Also the EnsureAuthenticated already returns an {:error, :unauthenticated}

Also, if it doesn’t get out of article’s scope, can you please add how to use guardian_db?

Thanks again for the nice tutorial.

njwest

njwest

@voger

Many thanks for the thoughtful feedback and improvements, Voger! Will be implementing your improvements shortly

Re: 7. I don’t use a SessionController because this app doesn’t use sessions – it only uses JWT token exchange.

The sign_in function only calls Accounts.insert_or_update_user(changeset), which leads to Guardian.encode_and_sign(user) on success – this returns a JWT, which can be used by a client to act on the server, but it does not create a session on the server.

This is one of the great advantages of JWTs in appropriate contexts: your server doesn’t have to create and manage sessions for logged in users; your server only has to act when a client makes a request, and rather than checking the client’s session on the server to see if it should proceed, it just has to validate the token sent by the client.

You can use Guardian for sessions, using Guardian.Plug.sign_in(user) in a session login function and Guardian.Plug.VerifySession in your auth pipeline, but in the case of this API-only app, I did not find it necessary

Thanks again for the feedback! :slight_smile:

script

script

Hi I just followed your guide. This was my first experience with the jwt . The side details you provided in the post are very well written and organised. I really enjoyed it . I think the code for guardian pipeline should be inside the router. But overall its really simple what you described. And I am waiting for its second part.

njwest

njwest

Hi Script! Thanks for the feedback, I’ll post back in this thread when the second part is up. :slight_smile:

Regarding putting the pipeline in the router, to my eyes the code is cleaner with the pipeline in a separate file because of the lengthy Guardian.Plug.Pipeline, :otp declaration, module: declaration, error_handler: declaration, but I totally get the argument for having all pipeline logic in the router as well.

Cheers!

Where Next?

Popular in Guides/Tuts Top

Neurofunk
Hi folks! I wrote an article about BEAM and Haskell Interoperabilty. I'm describing the use of the erlang-ffi project, that impersonates ...
New
Logan
Here is an example of a Mix application that utilizes Cowboy to handle websocket connections. If anyone has an idea about making this wor...
New
OndrejValenta
Me and my boys started a new website specifically designed for other ASP.NET programmers that struggle, as we do, with their transformati...
New
marcelo
I wrote a small article on how to use current_user with coherence on Phoenix. There are already a couple of blogposts about it but I thin...
New
jtormey
Hello! Having written a lot of LiveView code, I’ve made some VS Code snippets to speed up writing callbacks for LiveViews and LiveCompon...
New
berts-4865
Here is a quick guide to uploading a file from the browser to DO spaces. It is crude, but will hopefully save sometime time and frustrat...
New
AstonJ
This was originally posted on my blog, but since my I’ve taken it down (I hadn’t posted anything on it for a while and since it was runni...
New
bluegene
Hi guys, I’ve been on a personal journey to learn Elixir for the past two years. During this journey I’ve been using the spaced repetiti...
New
zookzook
If you want to use the MongoDB in your next Killer-App-Project, but you did not dare ask because otherwise many would advise you to use P...
New
anuragg
We finally have a Mix clustering guide to go with Phoenix deployment with Mix Releases. Deploy an Elixir Cluster with Mix Releases and l...
New

Other popular topics Top

yurko
Here are few pieces of (common) Linux knowledge that we use for reasonably small one server apps. We use Ubuntu but this should work for ...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
jerry
Good day to you all. I have been struggling to get a query involving like and ilike to work. Can anyone assist me on this, please? pro...
New
_russellb
I want to try my hand at web scraping. What tools/libraries do I need to use. I’m hoping to turn this into something professional so don’...
New
ycv005
I have followed this StackOverflow post to install the specific version of Erlang. And When I am running mix ecto.setup then getting fol...
New
KronicDeth
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine) This is a plugin that adds support for Elixir to JetBrains IntelliJ...
289 35421 110
New
mcarvalho
What is the difference between System.get_env and Application.get_env? For example, what are best practices to use one versus another.
New
rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list....
New
9mm
I am constructing a JSON object (map) and I need to conditionally set a field. I’m trying to write proper elixir-way code… and I’m at a l...
New
magnetic
Hey :wave:t3: Elixir community, I’ve been learning Elixir, and working on some side projects. My editor of choice is VSCode, and althoug...
New

We're in Beta

About us Mission Statement