この記事は Elixir Advent Calendar 2022 カレンダー1の22日目です。昨日は @torifukukaiou さんでした!
Phoenix1.6で mix phx.gen.auth
が使えるようになり、コマンド一発で認証システムを実装できるようになりました が、LiveView実装ではないので、アカウント周りだけ別でUI実装しないといけなかったり、他がLiveView実装だとController(非LiveView)と行き来しないといけなかったりと、少し不便でした。
そんな中、Phoenix1.7でphx.gen.authがLiveView化するとの情報が!ということで、まだRCではありますが、phx.gen.authで生成されるLiveView周りのコードを見ていきます
公式ドキュメント(HexDocs)を見比べてみる
Phoenixの公式ドキュメントでは、「GUIDES」と「MIX TASKS」のそれぞれに phx.gen.auth
についての解説ページがあります。
Phoenix1.7での変更点を含め細かい違いはありますが、LiveView化については特に言及されていません。単に更新が追いついていないだけでしょうか
Phoenix1.7.0-rc.0のプロジェクトを作成する
本題ではないのでさらっと。途中のYes/NoはすべてYesを選択しましょう。
docker run --rm -it -p 4000:4000 elixir:1.14.2-alpine sh
# 以下、Dockerコンテナ内でのコマンド実行
mix archive.install hex phx_new 1.7.0-rc.0
mix phx.new sample --database sqlite3
cd sample
apk add build-base inotify-tools
sed -i -e 's|{127, 0, 0, 1}|{0, 0, 0, 0}|' config/dev.exs
mix ecto.create
mix phx.server
これで http://localhost:4000 にアクセスできるようになります。
実際に mix phx.gen.auth
を実行してみる
Context名が Accounts
、Schema名が User
、テーブル名が users
となるように実行します。
mix phx.gen.auth Accounts User users
An authentication system can be created in two different ways:
- Using Phoenix.LiveView (default)
- Using Phoenix.Controller only
Do you want to create a LiveView based authentication system? [Yn]
LiveViewを使うかどうか聞かれましたね!今回はYesを選択しましょう。
(ファイルを作成するログがたくさん出るが、省略)
Please re-fetch your dependencies with the following command:
$ mix deps.get
Remember to update your repository by running migrations:
$ mix ecto.migrate
Once you are ready, visit "/users/register"
to create your account and then access "/dev/mailbox" to
see the account confirmation email.
とりあえず、言うとおりにコマンドを実行し、Phoenixを起動しましょう。
mix deps.get
mix ecto.migrate
mix phx.server
アカウント登録 | ログイン | メールアドレス/パスワード変更 | パスワードリセット |
---|---|---|---|
これだけの機能がコマンド一発でできあがるのはステキですね
ここまでのソースコードをGitHubにアップしています。ビジネスロジックはPhoenix1.6までと変わらないので今回はLiveView周りだけを見ていきますが、興味がある方は他のコードも見てみてください
ルーティング
アカウント周りの記述がいろいろと追加されていますが、中でも53・67・79行目の live_session に注目します。前からある機能ではありますが、LiveViewのマウント時に実行される関数を指定できます。例えば
live_session :require_authenticated_user,
on_mount: [{SampleWeb.UserAuth, :ensure_authenticated}] do
...
end
という記述がある場合、ブロック内で指定したLiveViewの mount/3
の前に SampleWeb.UserAuth.on_mount/4
が実行されます。第一引数は :ensure_authenticated
で、該当のコードを見ると「ログインしていなければLiveViewのマウントはされずにログイン画面にリダイレクトする」という動きになっています。
アカウント登録
lib/sample_web/live/user_registration_live.ex
アカウント登録に成功後、63行目で trigger_submit
をtrueにすることで phx-trigger-action
属性が働き、 /users/log_in?_action=registered
宛にForm Submitが発生します。なぜこんな回りくどいことを...と思いつつも、ログインを見てみましょう。
ログイン
lib/sample_web/live/user_login_live.ex
ログイン処理がどこにもないぞ...?と最初は思いましたが、18-40行目をよ〜く見てみると、 simple_form
コンポーネントは使われていますが実態としては普通のformタグになります。そのため、「Sign in」ボタンを押すと /users/log_in
宛にForm Submitが発生し、 SampleWeb.UserSessionController#create/2
が呼ばれます。
lib/sample_web/controllers/user_session_controller.ex の27行目がセッションにアクセストークンを入れているコードになります。なぜこんな回りくどい仕組みになっているかというと、おそらくですが、LiveViewとController(非LiveView)の両方で同一のログイン状態を扱うためのつくりなんだろうな〜と思っています。
メールアドレス/パスワード変更
lib/sample_web/live/user_settings_live.ex
1画面に複数のフォームがありますが、ここは普通のLiveView実装ですね。特に気になるところはありません。
パスワードリセット
lib/sample_web/live/user_forgot_password_live.ex
ここも普通のLiveView実装ですが、パスワードリセットの流れは
- パスワードリセット対象アカウントのメールアドレスを入力
- 届いたメール本文中のURLにアクセス
- lib/sample_web/live/user_reset_password_live.ex で新しいパスワードを設定
というよくある流れです。パスワードリセットが完了するとログイン画面に遷移してログインするように促されますが、単にリダイレクトするだけなので特別なことはないですね。
最後に
私の観測範囲内では、認証ライブラリというとブラックボックスっぽくなっていてカスタマイズするのに苦労するものが多い印象ですが、phx.gen.authはコードジェネレータなので実装を追いやすくカスタマイズも自由にできます。以前仕事でphx.gen.authを使ったときはLiveView化する余裕はありませんでしたが、今後はLiveViewアプリ開発がより一層捗りそうです