13
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

ElixirAdvent Calendar 2021

Day 10

phx_gen_authでAPIのトークン認証を実装してみた

Last updated at Posted at 2021-09-28

はじめに

本記事はPhoenix 1.6で組み込まれたアプリケーションに認証機能を追加するphx.gen.authを使用して、
通常のユーザー認証とAPIのトークン認証を実装していきます

本記事はPhoenix 1.6を使用するため、以下で最新版にアップデートをお願いします

mix archive.install hex phx_new

プロジェクト作成

mix phx.new auth_sample
cd auth_sample
mix ecto.create

通常の認証機能を追加

mix phx.gen.auth Accounts User users

コマンド一発で登録、ログイン、パスワードリセット、パスワード・アドレス変更、メール送信まで作ってくれるって素晴らしいですね

APIのトークン認証を実装

APIの作成

こちらとgen.authで生成されたファイルのcreateの箇所をベースにログインAPIを作成します
lib/auth_sample_web/controllers/user_session_controller.ex

フォームデータではないので受け取るパラメーターを
%{"user" => params} から、
%{"email" => email, "password" => password}にしているので注意してください

生成されたトークンはBinaryなので、レスポンスで返す用に Base64でエンコードします
また検証用にstatus APIも作成します

[new]lib/auth_sample_web/controllers/user_api_session_controller.ex
defmodule AuthSampleWeb.UserApiSessionController do
  use AuthSampleWeb, :controller

  alias AuthSample.Accounts

  def create(conn, %{"email" => email, "password" => password}) do
    if user = Accounts.get_user_by_email_and_password(email, password) do
      render(
        conn,
        "token.json",
        token: Accounts.generate_user_session_token(user) |> Base.encode64()
      )
    else
      conn
      |> put_status(:unauthorized)
      |> put_view(AuthSampleWeb.ErrorView)
      |> render(:"401")
    end
  end

  def status(conn, _params) do
    user = conn.assigns.current_user
    render(conn, "status.json", status: user.email <> " enable")
  end
end

viewファイルの作成も忘れずに

[new]lib/auth_sample_web/views/user_api_session_view.ex
defmodule AuthSampleWeb.UserApiSessionView do
  use AuthSampleWeb, :view

  def render("token.json", %{token: token}) do
    %{token: token}
  end

  def render("status.json", %{status: status}) do
    %{status: status}
  end
end

header Authorizationを確認

require_authenticated_userの下辺りにpipelineを追加
authorization headerは Bearer tokenとなっているのでBearerのあとのスペースを忘れないこと

tokenはbase64でDBのtokenはBinaryなのでデコードが必要です
ユーザーが取得したらconnにアサインして各APIから参照できるようにしておきます

[edit]lib/auth_sample/controllers/user_auth.ex
defmodule AuthSampleWeb.UserAuth do
  import Plug.Conn
  import Phoenix.Controller

...
  def require_verify_header(conn, _opts) do
    with ["Bearer " <> token] <- get_req_header(conn, "authorization"),
         user <- Accounts.get_user_by_session_token(token |> Base.decode64!()) do
      conn
      |> assign(:current_user, user)
    else
      _error ->
        conn
        |> put_status(:unauthorized)
        |> put_view(AuthSampleWeb.ErrorView)
        |> render(:"401")
        |> halt()
    end
...
  end
end

routerにapi scopeを認証なし、ありの2つを作成

[edit]lib/auth_sample_web/router.ex
defmodule AuthSampleViewWeb.Router do
  use AuthSampleWeb, :router

  import AuthSampleWeb.UserAuth

...
  scope "/api", AuthSampleWeb do
    pipe_through :api

    post "/log_in", UserApiSessionController, :create
  end

  scope "/api", AuthSampleWeb do
    pipe_through [:api, :require_verify_header]

    get "/status", UserApiSessionController, :status
  end
...
end

実装は以上です!
Guardianより簡単!

動作確認

login
スクリーンショット 2021-09-29 0.04.05.png

tokenなし
スクリーンショット 2021-09-29 0.04.42.png

tokenあり
スクリーンショット 2021-09-29 0.04.57.png

参考ページ

https://www.phoenixframework.org/
https://hexdocs.pm/phoenix/mix_phx_gen_auth.html
https://dev.to/onpointvn/implement-jwt-authentication-with-phoenix-token-n58

13
8
3

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
13
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?