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

nietaki
Just a quick heads up: There seems to be a bug in Erlang/OTP 21.3, which can cause some errors when making http requests. If you’re using...
New
egze
I was preparing to deploy a production application to AWS Fargate, and to practice I wanted to play with DNS polling and node discovery o...
New
tfwright
I thought I’d share a small project I’m working on to gain some familiarty with LiveView in a Phoenix app. Github Repo Deployment It’s...
New
9mm
So I’m really loving elixir. BY FAR the most excruciating piece of learning a functional language for me is having to “transform” all my ...
New
eclark
I’ve been working on a phoenix project lately and I wanted to use the latest versions of everything. Webpack 5 had some breaking changes ...
New
siever
I just wrote a simple guide on how you can setup a productive elixir development environment in vim. Its really easy, just a few steps. ...
New
voltone
The EEF’s Security WG has released the first public draft of the Secure Coding and Deployment Hardening Guidelines for BEAM languages. “...
New
WolfDan
So my main OS is Windows, I do must of my work with it, Elixir and vscode elixirls works just fine when you’re working only with elixir, ...
New
GenericJam
Just leaving some breadcrumbs for future me and future others like me. Connect with TCP (not secured) - most servers will reject but use...
New
fmcgeough
pipe into case? I use that fairly frequently…unless I’m misunderstanding what you’re wanting…could be… its still very early… str = "Hell...
New

Other popular topics Top

Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID&lt;0.412.0&gt; terminating ** (Postgrex.Error) FATAL...
New
JDanielMartinez
Hi! May someone helps me, please! I have two apps into an umbrella project: the first one is Database, which manages queries, and the se...
New
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
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
romenigld
I am trying to run a deploy with docker and I successfully runned with this command: docker build -t romenigld/blog-prod . but when I t...
New
dokuzbir
Hello, I am trying to convert my lists to string without losing brackets.For start i have 3 map. They look like these buyer = %{ id: ...
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
fayddelight
I tried installing elixir 1.11.2 erlang 23.3.4 via asdf in my zsh shell. Enabled the versions locally and globally. When I list them ...
New
baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New

We're in Beta

About us Mission Statement