Phoenix では mix phx.gen.auth で、簡単に認証システムを作成し、既存システムに組み込んでいけます。この記事ではどのような認証システムが作成され、どのように利用できるのかを見ていきたいと思います。
mix phx.gen.auth ドキュメント
インターネット創世記の、昔々、ブログシステムを運営していた経緯から、PHPやNodejs、Meteor、Django、AWS JavaScriptなどでWebアプリを実験するときは、必ず認証システムを作成してました。もちろんちょっと前のPhoenixでもそうでした。それで言語・フレームワークの使い易さを計っていました。それが今では mix phx.gen.auth コマンド一発で可能なのはちょっと感動的です。
【関連記事】
Phoenix で Blog を超簡単に作る - phx.gen.auth , phx.gen.html
Phoenix 認証システム - mix phx.gen.auth
Phoenix LiveViewの基本設定 - mix phx.gen.schema
Phoenix1.6の基本的な仕組み - mix phx.gen.html
Elixir/Phoenix のシンプル認証 auth_plug
1.前準備
プロジェクトを作成します。
mix phx.new auth_test
cd auth_test
以下のコマンドを発行します。Accounts context module と Accounts.User schema module 、最後に schema moduleの複数形で users tableが作成されます。さらに認証システムに必要なファイル一式が生成されます。
mix phx.gen.auth Accounts User users
以下のコマンドで生成されたファイルの設定を行います。
mix deps.get
mix ecto.setup
以上で前準備が整いました。
2. 認証システムにページを組み込む
phx.gen.auth で生成した認証システムに、新たに生成したページを router.ex 組み込み、どのように認証システムが働くかを確認します。router.ex には以下のような plugs が作成され追加されています。
- fetch_current_user - 可能ならば current user の情報を取得する
- require_authenticated_user - fetch_current_user で取得した user が存在し、認証されている必要がある
- redirect_if_user_is_authenticated - 認証済の user は利用できないページである
それぞれの定義は以下の user_auth.ex で確認できます。
---
@doc """
Authenticates the user by looking into the session
and remember me token.
"""
def fetch_current_user(conn, _opts) do
{user_token, conn} = ensure_user_token(conn)
user = user_token && Accounts.get_user_by_session_token(user_token)
assign(conn, :current_user, user)
end
defp ensure_user_token(conn) do
if user_token = get_session(conn, :user_token) do
{user_token, conn}
else
conn = fetch_cookies(conn, signed: [@remember_me_cookie])
if user_token = conn.cookies[@remember_me_cookie] do
{user_token, put_session(conn, :user_token, user_token)}
else
{nil, conn}
end
end
end
@doc """
Used for routes that require the user to not be authenticated.
"""
def redirect_if_user_is_authenticated(conn, _opts) do
if conn.assigns[:current_user] do
conn
|> redirect(to: signed_in_path(conn))
|> halt()
else
conn
end
end
@doc """
Used for routes that require the user to be authenticated.
If you want to enforce the user email is confirmed before
they use the application at all, here would be a good place.
"""
def require_authenticated_user(conn, _opts) do
if conn.assigns[:current_user] do
conn
else
conn
|> put_flash(:error, "You must log in to access this page.")
|> maybe_store_return_to()
|> redirect(to: Routes.user_session_path(conn, :new))
|> halt()
end
end
---
それでは新たに、hello.html.heex を作成し、router.ex のそれぞれの箇所に組み込み、効果をみていきます。
<h1>Hello World !!!</h1>
<%= if @current_user do %>
<h2><%= @current_user.email %></h2>
<% else %>
<h2>Guest</h2>
<% end %>
以下の実験を行う前に、ユーザ登録を行っておきます。
2-1. pipe_through :browser
router.ex に "/hello" パスを挿入します。
scope "/", AuthTestWeb do
pipe_through :browser
---
get "/hello", PageController, :hello # ここに挿入
end
未ログイン の状態で http://localhost:4000/hello にアクセスする。Guestが表示されました。
ログイン済 の状態で http://localhost:4000/hello にアクセスする。メールアドレスが表示されました。
2-2. pipe_through [:browser, :redirect_if_user_is_authenticated]
router.ex に "/hello" パスを挿入します。
scope "/", AuthTestWeb do
pipe_through [:browser, :redirect_if_user_is_authenticated]
---
get "/hello", PageController, :hello # ここに挿入
end
未ログイン の状態で http://localhost:4000/hello にアクセスする。Guestが表示されました。
ログイン済 の状態で http://localhost:4000/hello にアクセスする。トップページに リダイレクト されました。
2-3. pipe_through [:browser, :require_authenticated_user]
router.ex に "/hello" パスを挿入します。
scope "/", AuthTestWeb do
pipe_through [:browser, :require_authenticated_user]
---
get "/hello", PageController, :hello # ここに挿入
end
未ログイン の状態で http://localhost:4000/hello にアクセスする。ログイン画面にリダイレクトされました。
ログイン済 の状態で http://localhost:4000/hello にアクセスする。メールアドレスが表示されました。
今回は以上です。
Phoenixの良いところは、生成された template などはカスタマイズして使うことが前提となっているので、字面の日本語化やカスタマイズなどは簡単に行えることかなと感じてます。