I have an application that uses JWT authentication with Guardian. When a user signs in, the response contains the jwt in the body. The front-end (which is an SPA) then stores that jwt in localStorage, and attaches it to the Authorization
header of every request sent from there on. The server then verifies this using Guardian's built-in verification plug:
pipeline :api do
plug :accepts, ["json"]
plug Guardian.Plug.VerifyHeader, realm: "Bearer"
end
I would like to change this so that, instead of storing the JWTs in localStorage (which isn't secure), the server sends them to the front-end as secure cookies (with Secure
and HttpOnly
settings). I then want Guardian to read the jwt from the cookie, rather than from the Authorization
header.
Does Guardian support this functionality?
Here is my SessionController create
function:
def create(conn, params) do
case authenticate(params) do
{:ok, user} ->
new_conn = Guardian.Plug.api_sign_in(conn, user, :access)
jwt = Guardian.Plug.current_token(new_conn)
new_conn
|> put_status(:created)
|> render("show.json", user: user, jwt: jwt)
:error ->
conn
|> put_status(:unauthorized)
|> render("error.json")
end
end
UPDATE: This is with Guardian 1.0 (which you should use if you can!).
I think you can just do something like this:
defmodule MyApp.SessionController do
def login(conn, params) do
# your code to find the user based on auth strategy here
{:ok, jwt, _} = MyApp.Guardian.encode_and_sign(user)
conn = put_session(conn, :auth_token, jwt)
# send your response...
end
end
Would use HttpOnly
and Secure
flags on the cookie. Should be fine to store client side this way.
To fetch the user, you may need to write your own little plug, but your MyApp.Guardian
module could look something like this:
defmodule MyApp.Guardian do
use Guardian, otp_app: :my_app
alias MyApp.Accounts
def subject_for_token(%Accounts.Human{} = human, _claims) do
{:ok, to_string(human.id)}
end
def subject_for_token(_, _) do
{:error, :resource_not_found}
end
def resource_from_claims(%{"sub" => sub}) do
case Accounts.find_human(sub) do
nil -> {:error, :resource_not_found}
human -> {:ok, human}
end
end
def resource_from_claims(_) do
{:error, :resource_not_found}
end
end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With