先日書いた 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
- メディア: 単行本(ソフトカバー)