Protect API behind a bearer token
This commit is contained in:
+1
-1
@@ -11,7 +11,7 @@ config :music_library,
|
|||||||
ecto_repos: [MusicLibrary.Repo],
|
ecto_repos: [MusicLibrary.Repo],
|
||||||
generators: [timestamp_type: :utc_datetime, binary_id: true]
|
generators: [timestamp_type: :utc_datetime, binary_id: true]
|
||||||
|
|
||||||
config :music_library, MusicLibraryWeb, login_password: "change me"
|
config :music_library, MusicLibraryWeb, login_password: "change me", api_token: "change me"
|
||||||
|
|
||||||
# Configures the endpoint
|
# Configures the endpoint
|
||||||
config :music_library, MusicLibraryWeb.Endpoint,
|
config :music_library, MusicLibraryWeb.Endpoint,
|
||||||
|
|||||||
+7
-1
@@ -69,10 +69,16 @@ if config_env() == :prod do
|
|||||||
environment variable LOGIN_PASSWORD is missing.
|
environment variable LOGIN_PASSWORD is missing.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
api_token =
|
||||||
|
System.get_env("API_TOKEN") ||
|
||||||
|
raise """
|
||||||
|
environment variable API_TOKEN is missing.
|
||||||
|
"""
|
||||||
|
|
||||||
host = System.get_env("PHX_HOST") || "example.com"
|
host = System.get_env("PHX_HOST") || "example.com"
|
||||||
port = String.to_integer(System.get_env("PORT") || "4000")
|
port = String.to_integer(System.get_env("PORT") || "4000")
|
||||||
|
|
||||||
config :music_library, MusicLibraryWeb, login_password: login_password
|
config :music_library, MusicLibraryWeb, login_password: login_password, api_token: api_token
|
||||||
|
|
||||||
config :music_library, :dns_cluster_query, System.get_env("DNS_CLUSTER_QUERY")
|
config :music_library, :dns_cluster_query, System.get_env("DNS_CLUSTER_QUERY")
|
||||||
|
|
||||||
|
|||||||
@@ -7,11 +7,28 @@ defmodule MusicLibraryWeb.Auth do
|
|||||||
Plug.Crypto.secure_compare(login_password(), password)
|
Plug.Crypto.secure_compare(login_password(), password)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def require_api_token(conn, _opts) do
|
||||||
|
with ["Bearer " <> token] <- get_req_header(conn, "authorization"),
|
||||||
|
true <- Plug.Crypto.secure_compare(api_token(), token) do
|
||||||
|
conn
|
||||||
|
else
|
||||||
|
_ ->
|
||||||
|
conn
|
||||||
|
|> send_resp(:unauthorized, "Unauthorized API access")
|
||||||
|
|> halt()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
defp login_password do
|
defp login_password do
|
||||||
Application.get_env(:music_library, MusicLibraryWeb)
|
Application.get_env(:music_library, MusicLibraryWeb)
|
||||||
|> Keyword.fetch!(:login_password)
|
|> Keyword.fetch!(:login_password)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp api_token do
|
||||||
|
Application.get_env(:music_library, MusicLibraryWeb)
|
||||||
|
|> Keyword.fetch!(:api_token)
|
||||||
|
end
|
||||||
|
|
||||||
def require_logged_in(conn, _opts) do
|
def require_logged_in(conn, _opts) do
|
||||||
if get_session(conn, :logged_in) do
|
if get_session(conn, :logged_in) do
|
||||||
conn
|
conn
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
defmodule MusicLibraryWeb.Router do
|
defmodule MusicLibraryWeb.Router do
|
||||||
use MusicLibraryWeb, :router
|
use MusicLibraryWeb, :router
|
||||||
|
|
||||||
import MusicLibraryWeb.Auth, only: [require_logged_in: 2]
|
import MusicLibraryWeb.Auth, only: [require_logged_in: 2, require_api_token: 2]
|
||||||
|
|
||||||
pipeline :browser do
|
pipeline :browser do
|
||||||
plug :accepts, ["html"]
|
plug :accepts, ["html"]
|
||||||
@@ -14,6 +14,7 @@ defmodule MusicLibraryWeb.Router do
|
|||||||
|
|
||||||
pipeline :api do
|
pipeline :api do
|
||||||
plug :accepts, ["json"]
|
plug :accepts, ["json"]
|
||||||
|
plug :require_api_token
|
||||||
end
|
end
|
||||||
|
|
||||||
pipeline :logged_in do
|
pipeline :logged_in do
|
||||||
|
|||||||
@@ -335,7 +335,7 @@ msgstr ""
|
|||||||
msgid "Wishlist"
|
msgid "Wishlist"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/auth.ex:20
|
#: lib/music_library_web/auth.ex:37
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "You must be logged in to access this page"
|
msgid "You must be logged in to access this page"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
@@ -7,12 +7,26 @@ defmodule MusicLibraryWeb.CollectionControllerTest do
|
|||||||
%{record: record_fixture_with_artist("Steven Wilson")}
|
%{record: record_fixture_with_artist("Steven Wilson")}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp api_token do
|
||||||
|
Application.get_env(:music_library, MusicLibraryWeb)
|
||||||
|
|> Keyword.fetch!(:api_token)
|
||||||
|
end
|
||||||
|
|
||||||
describe "GET /api/collection/latest" do
|
describe "GET /api/collection/latest" do
|
||||||
setup [:create_record]
|
setup [:create_record]
|
||||||
|
|
||||||
test "it returns the latest record", %{conn: conn, record: record} do
|
test "it requires authentication", %{conn: conn} do
|
||||||
conn = get(conn, ~p"/api/collection/latest")
|
conn = get(conn, ~p"/api/collection/latest")
|
||||||
|
|
||||||
|
assert conn.status == 401
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it returns the latest record", %{conn: conn, record: record} do
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("authorization", "Bearer #{api_token()}")
|
||||||
|
|> get(~p"/api/collection/latest")
|
||||||
|
|
||||||
assert json_response(conn, 200) == %{
|
assert json_response(conn, 200) == %{
|
||||||
"artists" => ["Steven Wilson"],
|
"artists" => ["Steven Wilson"],
|
||||||
"title" => record.title,
|
"title" => record.title,
|
||||||
|
|||||||
Reference in New Issue
Block a user