hopefully done

This commit is contained in:
2023-02-18 18:13:08 +00:00
parent 820a717665
commit 2b3ad0b217
9 changed files with 118 additions and 8 deletions

View File

@@ -28,6 +28,11 @@ config :phoenix_api_template, PhoenixApiTemplateWeb.Auth.Guardian,
issuer: "phoenix_api_template", issuer: "phoenix_api_template",
secret_key: "" secret_key: ""
config :guardian, Guardian.DB,
repo: PhoenixApiTemplate.Repo,
schema_name: "guardian_tokens",
sweep_interval: 60
# Use Jason for JSON parsing in Phoenix # Use Jason for JSON parsing in Phoenix
config :phoenix, :json_library, Jason config :phoenix, :json_library, Jason

View File

@@ -5,3 +5,7 @@ end
defmodule PhoenixApiTemplateWeb.Auth.ErrorResponse.Forbidden do defmodule PhoenixApiTemplateWeb.Auth.ErrorResponse.Forbidden do
defexception message: "Forbidden", plug_status: 403 defexception message: "Forbidden", plug_status: 403
end end
defmodule PhoenixApiTemplateWeb.Auth.ErrorResponse.NotFound do
defexception message: "Not Found", plug_status: 404
end

View File

@@ -29,18 +29,58 @@ defmodule PhoenixApiTemplateWeb.Auth.Guardian do
user -> user ->
case validate_password(password, user.hashed_password) do case validate_password(password, user.hashed_password) do
true -> create_token(user) true -> create_token(user, :access)
false -> {:error, :unauthorized} false -> {:error, :unauthorized}
end end
end end
end end
def authenticate(token) do
with {:ok, claims} <- decode_and_verify(token),
{:ok, user} <- resource_from_claims(claims),
{:ok, _old, {new_token, _claims}} <- refresh(token) do
{:ok, user, new_token}
end
end
defp validate_password(password, hashed_password) do defp validate_password(password, hashed_password) do
Bcrypt.verify_pass(password, hashed_password) Bcrypt.verify_pass(password, hashed_password)
end end
defp create_token(user) do defp create_token(user, type) do
{:ok, token, _claims} = encode_and_sign(user) {:ok, token, _claims} = encode_and_sign(user, %{}, token_options(type))
{:ok, user, token} {:ok, user, token}
end end
defp token_options(type) do
case type do
:access -> [token_type: "access", ttl: {2, :hour}]
:reset -> [token_type: "reset", ttl: {15, :minute}]
:admin -> [token_type: "admin", ttl: {90, :day}]
end
end
def after_encode_and_sign(resource, claims, token, _options) do
with {:ok, _} <- Guardian.DB.after_encode_and_sign(resource, claims["typ"], claims, token) do
{:ok, token}
end
end
def on_verify(claims, token, _options) do
with {:ok, _} <- Guardian.DB.on_verify(claims, token) do
{:ok, claims}
end
end
def on_revoke(claims, token, _options) do
with {:ok, _} <- Guardian.DB.on_revoke(claims, token) do
{:ok, claims}
end
end
def on_refresh({old_token, old_claims}, {new_token, new_claims}, _options) do
with {:ok, _, _} <- Guardian.DB.on_refresh({old_token, old_claims}, {new_token, new_claims}) do
{:ok, {old_token, old_claims}, {new_token, new_claims}}
end
end
end end

View File

@@ -1,8 +1,9 @@
defmodule PhoenixApiTemplateWeb.UserController do defmodule PhoenixApiTemplateWeb.UserController do
use PhoenixApiTemplateWeb, :controller use PhoenixApiTemplateWeb, :controller
alias PhoenixApiTemplateWeb.Auth.ErrorResponse
alias PhoenixApiTemplateWeb.Auth.ErrorResponse.Unauthorized alias PhoenixApiTemplateWeb.Auth.ErrorResponse.Unauthorized
alias PhoenixApiTemplateWeb.Auth.ErrorResponse.Forbidden
alias PhoenixApiTemplateWeb.Auth.ErrorResponse.NotFound
alias PhoenixApiTemplateWeb.Auth.Guardian alias PhoenixApiTemplateWeb.Auth.Guardian
alias PhoenixApiTemplate.Accounts alias PhoenixApiTemplate.Accounts
alias PhoenixApiTemplate.Accounts.User alias PhoenixApiTemplate.Accounts.User
@@ -20,7 +21,7 @@ defmodule PhoenixApiTemplateWeb.UserController do
if conn.assigns.user.id == user.id do if conn.assigns.user.id == user.id do
conn conn
else else
raise ErrorResponse.Forbidden raise Forbidden
end end
end end
@@ -40,7 +41,11 @@ defmodule PhoenixApiTemplateWeb.UserController do
end end
def sign_in(conn, %{"email" => email, "password" => password}) do def sign_in(conn, %{"email" => email, "password" => password}) do
case Guardian.authenticate(email, password) do authorize_account(conn, email, password)
end
defp authorize_account(conn, email, hash_password) do
case Guardian.authenticate(email, hash_password) do
{:ok, user, token} -> {:ok, user, token} ->
conn conn
|> Plug.Conn.put_session(:user_id, user.id) |> Plug.Conn.put_session(:user_id, user.id)
@@ -52,6 +57,17 @@ defmodule PhoenixApiTemplateWeb.UserController do
end end
end end
def sign_out(conn, %{}) do
user = conn.assigns[:user]
token = Guardian.Plug.current_token(conn)
Guardian.revoke(token)
conn
|> Plug.Conn.clear_session()
|> put_status(:ok)
|> render("user_token.json", %{user: user, token: nil})
end
def show(conn, %{"id" => id}) do def show(conn, %{"id" => id}) do
user = Accounts.get_user!(id) user = Accounts.get_user!(id)
render(conn, "show.json", user: user) render(conn, "show.json", user: user)
@@ -72,4 +88,27 @@ defmodule PhoenixApiTemplateWeb.UserController do
send_resp(conn, :no_content, "") send_resp(conn, :no_content, "")
end end
end end
def refresh_session(conn, %{}) do
old_token = Guardian.Plug.current_token(conn)
case Guardian.decode_and_verify(old_token) do
{:ok, claims} ->
case Guardian.resource_from_claims(claims) do
{:ok, user} ->
{:ok, _old, {new_token, _new_claims}} = Guardian.refresh(old_token)
conn
|> Plug.Conn.put_session(:user_id, user.id)
|> put_status(:ok)
|> render("user_token.json", %{user: user, token: new_token})
{:error, _reason} ->
raise NotFound
end
{:error, _reason} ->
raise NotFound
end
end
end end

View File

@@ -45,5 +45,7 @@ defmodule PhoenixApiTemplateWeb.Router do
get "/users/by_id/:id", UserController, :show get "/users/by_id/:id", UserController, :show
put "/users/:id", UserController, :update put "/users/:id", UserController, :update
get "/user/refresh_session", UserController, :refresh_session
get "/sign_out", UserController, :sign_out
end end
end end

View File

@@ -11,7 +11,8 @@ defmodule PhoenixApiTemplateWeb.Telemetry do
children = [ children = [
# Telemetry poller will execute the given period measurements # Telemetry poller will execute the given period measurements
# every 10_000ms. Learn more here: https://hexdocs.pm/telemetry_metrics # every 10_000ms. Learn more here: https://hexdocs.pm/telemetry_metrics
{:telemetry_poller, measurements: periodic_measurements(), period: 10_000} {:telemetry_poller, measurements: periodic_measurements(), period: 10_000},
{Guardian.DB.Token.SweeperServer, []}
# Add reporters as children of your supervision tree. # Add reporters as children of your supervision tree.
# {Telemetry.Metrics.ConsoleReporter, metrics: metrics()} # {Telemetry.Metrics.ConsoleReporter, metrics: metrics()}
] ]

View File

@@ -44,7 +44,8 @@ defmodule PhoenixApiTemplate.MixProject do
{:plug_cowboy, "~> 2.5"}, {:plug_cowboy, "~> 2.5"},
{:guardian, "~> 2.0"}, {:guardian, "~> 2.0"},
{:bcrypt_elixir, "~> 3.0"}, {:bcrypt_elixir, "~> 3.0"},
{:envar, "~> 1.1.0"} {:envar, "~> 1.1.0"},
{:guardian_db, "~> 2.0"}
] ]
end end

View File

@@ -15,6 +15,7 @@
"expo": {:hex, :expo, "0.3.0", "13127c1d5f653b2927f2616a4c9ace5ae372efd67c7c2693b87fd0fdc30c6feb", [:mix], [], "hexpm", "fb3cd4bf012a77bc1608915497dae2ff684a06f0fa633c7afa90c4d72b881823"}, "expo": {:hex, :expo, "0.3.0", "13127c1d5f653b2927f2616a4c9ace5ae372efd67c7c2693b87fd0fdc30c6feb", [:mix], [], "hexpm", "fb3cd4bf012a77bc1608915497dae2ff684a06f0fa633c7afa90c4d72b881823"},
"gettext": {:hex, :gettext, "0.22.0", "a25d71ec21b1848957d9207b81fd61cb25161688d282d58bdafef74c2270bdc4", [:mix], [{:expo, "~> 0.3.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "cb0675141576f73720c8e49b4f0fd3f2c69f0cd8c218202724d4aebab8c70ace"}, "gettext": {:hex, :gettext, "0.22.0", "a25d71ec21b1848957d9207b81fd61cb25161688d282d58bdafef74c2270bdc4", [:mix], [{:expo, "~> 0.3.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "cb0675141576f73720c8e49b4f0fd3f2c69f0cd8c218202724d4aebab8c70ace"},
"guardian": {:hex, :guardian, "2.3.1", "2b2d78dc399a7df182d739ddc0e566d88723299bfac20be36255e2d052fd215d", [:mix], [{:jose, "~> 1.8", [hex: :jose, repo: "hexpm", optional: false]}, {:plug, "~> 1.3.3 or ~> 1.4", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "bbe241f9ca1b09fad916ad42d6049d2600bbc688aba5b3c4a6c82592a54274c3"}, "guardian": {:hex, :guardian, "2.3.1", "2b2d78dc399a7df182d739ddc0e566d88723299bfac20be36255e2d052fd215d", [:mix], [{:jose, "~> 1.8", [hex: :jose, repo: "hexpm", optional: false]}, {:plug, "~> 1.3.3 or ~> 1.4", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "bbe241f9ca1b09fad916ad42d6049d2600bbc688aba5b3c4a6c82592a54274c3"},
"guardian_db": {:hex, :guardian_db, "2.1.0", "ec95a9d99cdd1e550555d09a7bb4a340d8887aad0697f594590c2fd74be02426", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.1", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:guardian, "~> 1.0 or ~> 2.0", [hex: :guardian, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.13", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "f8e7d543ac92c395f3a7fd5acbe6829faeade57d688f7562e2f0fca8f94a0d70"},
"jason": {:hex, :jason, "1.4.0", "e855647bc964a44e2f67df589ccf49105ae039d4179db7f6271dfd3843dc27e6", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "79a3791085b2a0f743ca04cec0f7be26443738779d09302e01318f97bdb82121"}, "jason": {:hex, :jason, "1.4.0", "e855647bc964a44e2f67df589ccf49105ae039d4179db7f6271dfd3843dc27e6", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "79a3791085b2a0f743ca04cec0f7be26443738779d09302e01318f97bdb82121"},
"jose": {:hex, :jose, "1.11.5", "3bc2d75ffa5e2c941ca93e5696b54978323191988eb8d225c2e663ddfefd515e", [:mix, :rebar3], [], "hexpm", "dcd3b215bafe02ea7c5b23dafd3eb8062a5cd8f2d904fd9caa323d37034ab384"}, "jose": {:hex, :jose, "1.11.5", "3bc2d75ffa5e2c941ca93e5696b54978323191988eb8d225c2e663ddfefd515e", [:mix, :rebar3], [], "hexpm", "dcd3b215bafe02ea7c5b23dafd3eb8062a5cd8f2d904fd9caa323d37034ab384"},
"mime": {:hex, :mime, "2.0.3", "3676436d3d1f7b81b5a2d2bd8405f412c677558c81b1c92be58c00562bb59095", [:mix], [], "hexpm", "27a30bf0db44d25eecba73755acf4068cbfe26a4372f9eb3e4ea3a45956bff6b"}, "mime": {:hex, :mime, "2.0.3", "3676436d3d1f7b81b5a2d2bd8405f412c677558c81b1c92be58c00562bb59095", [:mix], [], "hexpm", "27a30bf0db44d25eecba73755acf4068cbfe26a4372f9eb3e4ea3a45956bff6b"},

View File

@@ -0,0 +1,17 @@
defmodule PhoenixApiTemplate.Repo.Migrations.CreateGuardianDBTokensTable do
use Ecto.Migration
def change do
create table(:guardian_tokens, primary_key: false) do
add(:jti, :string, primary_key: true)
add(:aud, :string, primary_key: true)
add(:typ, :string)
add(:iss, :string)
add(:sub, :string)
add(:exp, :bigint)
add(:jwt, :text)
add(:claims, :map)
timestamps()
end
end
end