4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ConnからSocketにコンバートする方法+phx.gen.authのログイン処理に関するメモ

Last updated at Posted at 2023-11-26

ConnからSocketにコンバートする方法+phx.gen.authのログイン処理に関するメモ

Socketは生成されるときに、Connをもとに生成される。

具体的にどのように記述するべきなのか調べたメモ。

ConnとSocketとは

Plug.ConnHTTPリクエストを受信したときに生成される構造体である。
リクエストとレスポンスに用いられ、リクエスト用のフィールド
レスポンス用のフィールド、またPhoenix側で値を格納するフィールドを含んでいる。

またPlug.Conn(モジュール)自体はリクエストとレスポンスを操作するためのモジュールであり、後述するPlug.Sessionにアクセスするためにも使用する。

Phoenix.LiveView.SocketliveルートにHTTPリクエストを受信したときに生成される構造体である。
これが状態と呼ばれるものであり、ライフサイクル上で必要なデータを格納している。

Plug.Sessionはルートに到達したときに生成される構造体である。セッション管理に使用される。

コンバートの方法

このような場合、まずlive_sessionというマクロでliveセッションを作成する必要がある。

liveセッションとは、いくつかのliveルートをグループ化するものである

このマクロにはon_mountというオプションがある。

on_mountコールバックを実装したモジュールと第一引数のアトムを、次の形式で指定する。(パターンマッチで処理を分岐させている)

よってlive_sessionマクロは次の形式で記述する。

live_session セッション名,
  on_mount: [{モジュール, アトム}] do
    ルート
    ...
  end

liveセッション内で必要となる共通のデータを状態に追加する処理などを実行することができる。

live_sessionは次のようにscope内に定義することができる。

live_session :return_to_home_live_index_required_authentication,
  on_mount: [{SampleWeb.AccountAuth, :ensure_authenticated}] do
    live "/", HomeLive.Index, :index
  end

なお、liveセッション名は理解しやすい名前にすること。

on_mountの実装

on_mountの実装は次の形式で記述する。

def on_mount(アトム, params, session, socket) do
  ...
end

戻り値は、次のタプルとなる。

  • {:cont, socket} - このままsocketを渡し、mountを開始する。
  • {:halt, socket} - mountを中止する。リダイレクトを行う場合はこちらを返すようにする。

実装は例のようになる。

def on_mount(アトム, _params, _session, socket) do
  case :takashi do
    :takashi -> {:cont, assign(socket, :name, "takashi")}
    :tanuki -> {:halt, Phoenix.LiveView.redirect(socket, to: ~p"/")}
  end
end

データの受け渡し

connのデータをsocketに渡すためには、pipe_through(scopeの固有の処理)でPlug.sessionへの値の挿入を行う必要がある。

次のput_takashiという処理でデータの受け渡しを行うものとする。

pipe_through [:browser, put_takashi]

以下のように定義されている。

def put_takashi(conn, _opts) do
  put_session(conn, :takashi, "takashi")
end

処理が実行されると、Plug.Connprivateフィールドにある:plug_sessionというキーの値であるmaptakashi: "takashi"が挿入される。

private: %{ :plug_session => %{..., takashi: "takashi"}, ...}

この:plug_sessionの値はLiveViewのmount第2引数で参照することができる。

def mount(_params, session, socket) do
  IO.puts "session: #{inspect session}"
  ...
end

IO.putsの出力は以下のようになる。

session: %{..., "takashi" => "takashi"}

sessionに値を渡すことができたので、次はassignsに渡す。

sessionから値を取得するだけ。

def on_mount(アトム, _params, session, socket) do
    {:cont, assign(socket, :name, session[:takashi])
end

結論

ルートにリクエストを受信する前にsocketにデータを追加したい場合、次の手順を実行する。

  • scopelive_sessionマクロの記述をする。
  • liveセッション名と実行するon_mount{モジュール名, アトム}で指定する。
  • on_mountコールバックを実装して、on_mount: に指定する。
  • do .. endにルートをliveルートを記述する。

phx.gen.authのログイン処理に関するメモ

以下のスコープがあるとする。

  scope "/", SampleWeb do
    pipe_through [:browser, :require_authenticated_account]

    live_session :require_authenticated_account,
      on_mount: [{SampleWeb.AccountAuth, :ensure_authenticated}] do
      live "/accounts/settings", AccountSettingsLive, :edit
      live "/accounts/settings/confirm_email/:token", AccountSettingsLive, :confirm_email
    end
  end

スコープ内に記述されていることを要約すると次のようになる。

  • このスコープは内のliveルートはログインを必要とする。ログインしていない場合、ログインページにリダイレクトする。
  • live_sessionマクロに記述しているliveルートは/accounts/settings/以下略はログインを必要とする。ログインしていないとき、ログインページにリダイレクトする。

疑問に思うことは次のようなことである。

「どちらもログインしていない場合はログインページにリダイレクトするので、require_authenticated_accountを実行しない場合、どのような動作をするのか」

調査結果

require_authenticated_accountの処理の内容は、conn.assigns[:current_account]の存在するかどうかにより次の動作を行う。

存在する場合:
何もしない

存在しない場合:
flashメッセージを表示して、現在のパスをセッションに追加し、ログインページにリダイレクト。

on_mount(:ensure_authenticated, ...)の処理の内容は、session.assigns.current_accountの存在するかどうかにより次の動作を行う。

存在する場合:
socketに追加するだけ。

存在しない場合:
flashメッセージを表示して、ログインページにリダイレクト。

大きく異なる点は、ログイン後にリダイレクト前のページに戻れるかどうかである。

つまり、該当ルートに到達したときの処理は次のようになる。

  1. ログインしていないのでrequire_authenticated_accountでログインページにリダイレクト
  • このときmaybe_store_return_to(%{method: "GET"} = conn)によってsessionの:account_return_toというキーの値に現在のパスが格納される
  1. ログインページで入力を行う
  • 入力(構造体のインスタンス)がAccountSessionController, :createのparamsに渡される
  1. 実行されるアクション内でAccountAuth.log_in_accountが呼び出される
  • sessionを初期化した後、予め取得した:account_return_toの値(リダイレクト前パス)にリダイレクト(or/に飛ぶ)

よって:require_authenticated_accounログイン後にもとのページに戻るための処理であるので、実行しない場合はログイン後に/にリダイレクトする(signed_in_path()に記述されている)。

なお、connにcurrent_accountを追加する処理は、pipelineに追加されているfetch_current_accountによるものである。

以下のような記述は、on_mount側でログインを確認するまでもないので、on_mount(:mount_current_account)を実行しても良いということになる。

pipe_through [:browser, :require_authenticated_account]
  ...
  on_mount: [{SampleWeb.AccountAuth, :mount_current_account}] do
    ...
  end
4
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?