先日書いた phx_gen_auth とおなじく認証のしくみを Phoenix に組みこむためパッケージ。
phx_gen_auth が認証のしくみを実現するコードを生成するライブラリなのに対し(なので phx_gen_auth 自体はアプリケーション内で利用されない)、Pow はアプリケーションの一部として実行時に認証のしくみを提供するのが大きな違い。
結論
独自の authorization plug を実装し、トークンをセッションに登録する。 LiveView でそのトークンを受け取りユーザを取得する。
実例
アプリケーションを用意する
$ mix phx.new my_app --live $ cd my_app
Pow を追加する
mix.exs の deps に pow を追加、パッケージを取得しコンパイルする。
defmodule MyApp.MixProject do use Mix.Project # ... def deps do # ... {:pow, "~> 1.0"} end end
$ mix do deps.get, deps.compile
認証のしくみを用意する
mix pow.install タスクを利用して、認証のしくみに必要なファイルを生成する。
あわせて既存のファイルに対する変更が表示されるので、それにしたがっってファイルを編集する。
なお、Endpoint には後述するようにカスタマイズした独自のモジュールを指定するので、 Endpoint の編集はそこで説明する。
$ mix pow.install
* creating priv/repo/migrations
* creating priv/repo/migrations/20200915121951_create_users.exs
* creating lib/my_app/users
* creating lib/my_app/users/user.ex
Pow has been installed in your phoenix app!
There are three files you'll need to configure first before you can use Pow.
First, append this to `config/config.exs`:
config :my_app, :pow,
user: MyApp.Users.User,
repo: MyApp.Repo
Next, add `Pow.Plug.Session` plug to `lib/my_app_web/endpoint.ex` after `plug Plug.Session`:
defmodule MyAppWeb.Endpoint do
use Phoenix.Endpoint, otp_app: :my_app
# ...
plug Plug.Session, @session_options
plug Pow.Plug.Session, otp_app: :my_app
plug MyAppWeb.Router
end
Last, update `lib/my_app_web/router.ex` with the Pow routes:
defmodule MyAppWeb.Router do
use MyAppWeb, :router
use Pow.Phoenix.Router
# ... pipelines
scope "/" do
pipe_through :browser
pow_routes()
end
# ... routes
end
Config
config/config.exs に Pow の設定を追加する。
config :my_app, :pow, user: MyApp.Users.User, repo: MyApp.Repo
Router
lib/my_app_web/router.ex で定義されるモジュール MyAppWeb.Router で Pow.Phoenix.Router を use する。
defmodule MyAppWeb.Router do use MyAppWeb, :router use Pow.Phoenix.Router # 追加 # ... end
scope を追加し pow_routes/0 を追加する。
これによって認証に必要な登録やログインのルーティングが追加される。
defmodule MyAppWeb.Router do # ... scope "/" do pipe_through :browser pow_routes() end # ... end
pipeline :protected を追加する。
defmodule MyAppWeb.Router do # ... pipeline :protected do plug Pow.Plug.RequireAuthenticated, error_handler: Pow.Phoenix.PlugErrorHandler end # ... end
LiveView を含む scope の pipe_through に :protected を追加する。
これによって LiveView のリソースにアクセスするにはログインが必要になる。
defmodule MyAppWeb.Router do # ... scope "/", MyAppWeb do pipe_through [:browser, :protected] # :protected を追加 live "/", PageLive, :index live "/user", UserLive # 追加 end # ... end
Authorization Plug
ドキュメントにある独自の authorization plug を定義する。
ファイルはどこに配置してもよいけれども、ここではモジュール名に合わせて lib/my_app_web/pow/plug.ex に配置する。
ログインのときに呼び出される create/3 で、セッションにユーザ ID にもとづくトークンを記録する。
LiveView ではこのトークンを利用してログインしているユーザを取得する。
defmodule MyAppWeb.Pow.Plug do use Pow.Plug.Base @session_key :pow_user_token @salt "user salt" @max_age 86400 @impl true def fetch(conn, _config) do conn = Plug.Conn.fetch_session(conn) token = Plug.Conn.get_session(conn, @session_key) MyAppWeb.Endpoint |> Phoenix.Token.verify(@salt, token, max_age: @max_age) |> maybe_load_user(conn) end defp maybe_load_user({:ok, user_id}, conn), do: {conn, MyApp.Repo.get(MyApp.Users.User, user_id)} defp maybe_load_user({:error, _any}, conn), do: {conn, nil} @impl true def create(conn, user, _config) do token = Phoenix.Token.sign(MyAppWeb.Endpoint, @salt, user.id) conn = conn |> Plug.Conn.fetch_session() |> Plug.Conn.put_session(@session_key, token) {conn, user} end @impl true def delete(conn, _config) do conn |> Plug.Conn.fetch_session() |> Plug.Conn.delete_session(@session_key) end end
Endpoint
lib/my_app_web/endpoint.ex に上で定義した plug を設定する。
Pow.Plug は Session の結果に依存し、Router は Pow.Plug の結果に依存するため、この位置に記述する。
defmodule MyAppWeb.Endpoint do # ... plug Plug.Session, @session_options plug MyAppWeb.Pow.Plug, otp_app: :my_app # Session の後、Router の前のこの位置に追加 plug MyAppWeb.Router end
LiveView
認証を必要とする LiveView を定義する。
mount/3 の第二引数のセッション情報から、先に記述した plug の create/3 で記録したトークンを取得する。
このトークンからユーザ ID を復元し、ユーザのデータを取得する。
defmodule MyAppWeb.UserLive do use MyAppWeb, :live_view @salt "user salt" @max_age 86400 def mount(_params, %{"pow_user_token" => token}, socket) do {:ok, user_id} = Phoenix.Token.verify(MyAppWeb.Endpoint, @salt, token, max_age: @max_age) current_user = MyApp.Repo.get(MyApp.Users.User, user_id) {:ok, assign(socket, current_user: current_user)} end def render(assigns) do ~L""" <%= @current_user.email %> """ end end
サーバを起動し動作を確認する
サーバを起動し、追加した LiveView の URL http://localhost:4000/user にアクセスする。
$ iex -S mix phx.server
認証されていないのでログインページにリダイレクトされる。
ユーザ登録のためにログインページに表示されているリンク Register をクリックする。
表示されるユーザ登録ページでメールアドレスとパスワードと確認用のパスワードを入力する。
ユーザ登録が完了すると、LiveView のページが表示され、登録したメールアドレスが表示される。
いつか読むはずっと読まない: Peytoia nathorsti
石化した古生物を扱う学問であっても、それもまたダイナミックな人間の営みであることをいつも感じます。

- 作者:土屋 健
- 発売日: 2020/02/12
- メディア: 単行本(ソフトカバー)