To invalidate JWTs, people use one of two methods
- blacklist/whitelist (with guardian_db).
- a refresh token (which allows regenerating access tokens) with a short expiring access token.
I dont want to use guardian_db in my project. So, how i generate access token and refresh token in the login endpoint?
My code is:
File mix.ex
# Authentication library
{:guardian, "~> 1.0"}
File config.exs
# Guardian configuration
config :my_proj, MyProj.Guardian,
issuer: "my_proj",
verify_module: Guardian.JWT,
secret_key: "Xxxxxxxxxxxxxxxxxxxxxxxxxx",
allowed_drift: 2000,
verify_issuer: true,
ttl: {5, :minutes}
# Ueberauth configuration
config :ueberauth, Ueberauth,
base_path: "/api/auth",
providers: [
identity: {
Ueberauth.Strategy.Identity,
[
callback_methods: ["POST"],
callback_path: "/api/auth/login",
nickname_field: :email,
param_nesting: "account",
uid_field: :email
]
}
]
File aut_access_pipeline.ex
defmodule MyProjWeb.Plug.AuthAccessPipeline do
use Guardian.Plug.Pipeline, otp_app: :my_proj
plug(Guardian.Plug.VerifySession, claims: %{"typ" => "access"})
plug(Guardian.Plug.VerifyHeader, claims: %{"typ" => "access"})
plug(Guardian.Plug.EnsureAuthenticated)
plug(Guardian.Plug.LoadResource, ensure: true)
plug(MyProjWeb.Plug.CurrentUser)
plug(MyProjWeb.Plug.CheckToken)
end
File auth_controller.ex
defmodule MyProjWeb.AuthenticationController do
def login(conn, %{"email" => email, "password" => password}) do
case Accounts.get_user_by_email_and_password(email, password) do
{:ok, user} ->
handle_login_response(conn, user, "login.json")
{:error, _reason} ->
conn
|> put_status(:unauthorized)
|> Error.render(:invalid_credentials)
|> halt
end
end
def refresh(conn, %{"jwt" => existing_jwt}) do
case MyProj.Guardian.refresh(existing_jwt, ttl: {5, :minutes}) do
{:ok, {_old_token, _old_claims}, {new_jwt, _new_claims}} ->
current_user = current_resource(conn)
user = MyProj.Accounts.get_user!(current_user.id)
conn
|> put_resp_header("authorization", "Bearer #{new_jwt}")
|> render(MyProjWeb.AuthenticationView, json_file, %{jwt: new_jwt, user: user})
{:error, _reason} ->
conn
|> put_status(:unauthorized)
|> Error.render(:invalid_credentials)
end
end
defp handle_login_response(conn, user, json_file) do
new_conn = MyProj.Guardian.Plug.sign_in(conn, user, [])
jwt = MyProj.Guardian.Plug.current_token(new_conn)
%{"exp" => exp} = MyProj.Guardian.Plug.current_claims(new_conn)
new_conn
|> put_resp_header("authorization", "Bearer #{jwt}")
|> put_resp_header("x-expires", "#{exp}")
|> render(MyProjWeb.AuthenticationView, json_file, %{jwt: jwt, user: user})
end
end
end
File guardian.ex
defmodule MyProj.Guardian do
use Guardian, otp_app: :my_proj
def subject_for_token(user = %User{}, _claims),
do: {:ok, "User:#{user.id}"}
def subject_for_token(_user, _claims) do
{:error, :no_resource_id}
end
def resource_from_claims(%{"sub" => "User:" <> id}) do
{:ok, MyProj.Accounts.get_user!(id)}
end
def resource_from_claims(_claims) do
{:error, :no_claims_sub}
end
end
With this code i have a valid token for access but i dont know how generate the refresh token for regenerate/refresh the access token when he expires...
anyone can help?
My solution is something like this...
In the login endpoint i respond with 2 tokens (access and refresh) ...
Basically the solution is to use the MyProj.Guardian.exchange(...)
In the refresh endpoint i respond with the new access token ...