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 .
Ecto creates databases with mix ecto.create not mix ecto.migrate.
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
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.
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
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
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?
You create the sign_in/2 inside the UserController. Shouldn’t the session be handled by a separate SessionController?
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?
Voger I believe you already gave my library AccessPass a look…but it is worth noting part of the reason I made it in the first place was a revokable token solution without the need to call all the way to a database each call(like guardian_db)…just something to think about when using guardian_db
@jordiee
Yes I have seen it. I didn’t read the code yet. I plan to do soon. But what I don’t know is how does it handle reboots and blacklisting tokens. I plan to test it soon.
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
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.
Hi Script! Thanks for the feedback, I’ll post back in this thread when the second part is up.
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.