Phoenix Elixir – Make Authenticate Page (Register, Login, Logout)

Phoenix Elixir – Make Authenticate Page (Register, Login, Logout)

Postingan kali ini akan memaparkan bagaimana membuat fitur autentikasi pada Phoenix Framework yang terdiri dari fitur register, login dan logout. Sebelum kita lanjutkan ke tahapan membuat proses register, saya asumsikan anda sudah memahami cara membuat fitur CRUD pada Phoenix Framework (bisa dilihat di https://sabitlabscode.wordpress.com/2017/01/04/phoenix-elixir-make-crud-with-postgresql/ bagi anda yang belum paham). Proses register dimulai dengan men-generate CRUD tabel user dengan field tabel sebagai berikut : username , name , password dan email. Semua field yang dibuat memiliki tipe string kecuali field id dan timestamps() yang dibentuk otomatis oleh Phoenix. Setelah melakukan generate CRUD pada tabel user, pastikan seluruh fungsi CRUD berjalan dengan baik. Untuk mempersingkat maka proses “register” yang dibuat adalah proses action “new” dan “create” pada UserController (anda bisa membuat fungsi register tersendiri untuk memperindah managemen code anda), ke depannya kita akan arahkan tautan register ke action tersebut.

Guna meningkatkan security pada proses autentikasi, kita harus melakukan beberapa modifikasi berikut pada proses register yang dibuat sebelumnya:

  • enkripsi pada field password. Setiap user yang melakukan proses register, field password yang diisi oleh user harus di enkripsi kemudian disimpan ke dalam database.
  • User harus mengisi password sebanyak 2 kali (password dan password confirmation) untuk memastikan password yang dimasukkan benar.

Untuk menambahkan password confirmation, kita harus menambahkan sebuah field pada model “User” dan menandakan field tersebut menjadi sebuah field virtual yang sebenarnya tidak ada pada tabel. Berikut schema pada model User setelah ditambahkan field password confirmation:

schema "users" do
    field :username, :string
    field :name, :string
    field :password, :string
    field :email, :string
    field :password_confirmation, :string, virtual: true

    timestamps()
  end

Tambahkan juga field tersebut pada form.html.eex. Ketika pengguna menginput data user ke dalam form, maka aplikasi harus memastikan bahwa antara field password dan password_confirmation harus sama. Masukkan validasi berikut pada model User untuk melakukan pengecekan tersebut :

 @required_fields ~w(username name email password password_confirmation)
  @optional_fields ~w()
  @doc """
  Builds a changeset based on the `struct` and `params`.
  """

  def changeset(struct, params \\ %{}) do
    struct
    |> cast(params, @required_fields)
    |> validate_required([:username, :name, :password, :password_confirmation, :email])
    |> validate_format(:email, ~r/@/, message: "Please fix your email format")
    |> validate_confirmation(:password)
  end

Validasi “validate_confirmation” akan mengecek kesamaan field parameter dengan field yang bernama parameter+”_confirmation” dalam hal ini “password” dan “password_confirmation.

Ingat, ketika melakukan proses penyimpanan maka aplikasi akan melakukan enkripsi field password. Buatlah sebuah fungsi untuk melakukan enkripsi pada model User :

  def generate_password_and_store_user(changeset) do
    put_change(changeset, :password, hashpwsalt(changeset.params["password"]))
      |> Repo.insert
  end

Fungsi di atas dapat dipanggil dengan parameter sebuah “changeset”. Nilai dari changeset tersebut akan memodifikasi changeset dengan parameter “password” dimana nilainya diganti dengan nilai hasil enkripsi. Setelah merubah nilai pada parameter “password” dilakukan proses insert data ke database.

Import Comeonin agar dapat menggunakan fungsi “hashpwsalt” pada proses enkripsi :

 import Comeonin.Bcrypt, only: [hashpwsalt: 1]

Pada UserController action create, masukkan code berikut :

 def create(conn, %{"user" => user_params}) do
    changeset = User.changeset(%User{}, user_params)
    if changeset.valid? do
      new_user = User.generate_password_and_store_user(changeset)

      conn
        |> put_flash(:info, "Successfully registered and logged in")
        |> redirect(to: user_path(conn, :index))
    else
      render conn, "new.html", changeset: changeset
    end
  end

Yang terjadi pada code di atas adalah :

  • mendefinisikan variabel “changeset” yang di dapat dari post form “new” dan dibundle ke dalam User changeset
  • jika variabel changeset sudah memenuhi validasi pada model User, maka akan memanggil fungsi “generate_password_and_store_user pada model User. Fungsi ini akan memodifikasi field password untuk kemudian disimpan ke dalam database.

Proses register telah dapat anda jalankan..

LOGIN dan LOGOUT

Proses login dan logout akan kita letakkan pada sebuah controller terpisah. Buatlah sebuah controller dengan nama AuthController. Di dalam controller ini akan terdapat fungsi “login”, “authenticate” dan “logout”. Berikut code pada AuthController :

defmodule MyApp.AuthController do
  use FotoKerja.Web, :controller

  def login(conn, _params) do
    render(conn, "login.html")
  end

  def authenticate(conn, %{"user" => user_params}) do
    case MyApp.Auth.login(user_params, FotoKerja.Repo) do
      {:ok, user} ->
        conn
        |> put_session(:current_user, user.id)
        |> put_flash(:info, "Logged in")
        |> redirect(to: "/")
      :error ->
        conn
        |> put_flash(:info, "Wrong email or password")
        |> render("login.html")
    end
  end

  def logout(conn, _) do
    conn
      |> delete_session(:current_user)
      |> put_flash(:info, "Logged out")
      |> redirect(to: "/")
  end

end

Action “login” hanya akan menampilkan sebuah form “login.html”, form ini berisi field username dan password untuk proses login. Action “authenticate” akan bertindak sebagai “post” pada action login dimana disini akan mengecek inputan login user dari fungsi Auth.login yang akan kita buat nanti. Sedangkan fungsi logout akan menghapus session “:current_user” yang artinya menghapus session user yang sedang login.

Untuk kebutuhan view pada proses login, buatlah sebuah file “web/views/auth_view.ex” dengan code berikut :

defmodule FotoKerja.AuthView do
  use FotoKerja.Web, :view
end

Dan file “web/templates/auth/login.html.eex” yang berisi text input username dan password. Untuk username gunakan text_input dan password gunakan password_input.

Tambahkan file “web/models/auth.ex”, file ini akan melakukan proses login. Masukkan code berikut :

defmodule MyApp.Auth do
   alias MyApp.User

  def login(params, repo) do
    user = repo.get_by(User, username: String.downcase(params["username"]))
    case authenticate(user, params["password"]) do
      true -> {:ok, user}
      _    -> :error
    end
  end

  defp authenticate(user, password) do
    case user do
      nil -> false
      _   -> Comeonin.Bcrypt.checkpw(password, user.password)
    end
  end

  def current_user(conn) do
    id = Plug.Conn.get_session(conn, :current_user)
    if id, do: FotoKerja.Repo.get(User, id)
  end

  def logged_in?(conn), do: !!current_user(conn)
end

Code di atas terdiri dari beberapa fungsi, berikut penjelasan masing-masing fungsi :

  • authenticate : fungsi ini akan mengecek apakah password seorang user sudah sama dengan password yang diinput oleh user yang mencoba login. Tentunya pengecekan password akan menggunakan fungsi dari Comeonin untuk matching dengan nilai yang di enkripsi.
  • login : fungsi authenticate akan dipanggil pada fungsi ini, fungsi login pertama-tama akan memanggil data user yang meiliki “username” yang sama seperti nilai username yang diinput pada proses login. Jika data username tersebut ada pada database, barulah fungsi “authenticate” dipanggil untuk mengecek password.
  • current_user : akan mengembalikan data user yang sedang login
  • logged_in : mengecek apakah orang yang mengakses aplikasi dalam kondisi login atau tidak

Kembali pada “UserController” pada action authenticate yang memanggil Auth.login untuk proses autentikasi. Seperti dapat anda lihat bahwa fungsi Auth.login akan mengecek kebenaran “username” kemudian memanggil “authenticate” untuk mengecek kebenaran “password”.

Informasi tentang user yang telah melakukan proses login akan disimpan ke dalam session “:curent_user”. Dapat dilhat pada “UserController” ketika proses ketika proses login berhasil dilakukan, akan menjalankan perintah berikut :

        |> put_session(:current_user, user.id)

Perintah di atas akan menyimpan data session “:current_user” ke dalam data dengan data “user.id”. Dan ketika melihat AuthController action “logout”, kita menemukan code untuk menghapus nilai session :current_user seperti berikut :

      |> delete_session(:current_user)

Selanjutnya kita akan membuat sebuah tautan dengan kondisi sebagai berikut :

  • Jika user belum login, akan menampilkan tautan untuk login dan register
  • Jika user telah login, akan menampilkan email user dan tautan untuk logout.
  • Kita akan meletakkan tautan tersebut pada bagian atas/header aplikasi

Masukkan code berikut pada “web/templates/layout/app.html.eex” :

           <%= if logged_in?(@conn) do %>
              <li><%= current_user(@conn).email %></li>
              <li><%= link "Logout", to: auth_path(@conn, :logout), method: :delete %></li>
            <% else %>
              <li><%= link "Login",    to: "/login" %></li>
              <li><%= link "Register", to: user_path(@conn, :new) %></li>
            <% end %>

Kita memanggil fungsi logged_in dan current_user yang ada pada model Auth. Agar template di atas dapat menggunakan kedua fungsi tersebut, kita harus melakukan import pada file “web/web.ex” tepatnya di dalam fungsi “view” seperti berikut :

      import Phoenix.Controller, only: [get_csrf_token: 0, get_flash: 2, view_module: 1]

Setelah modifikasi di atas, silahkan jalankan aplikasi.. anda dapat melakukan proses login seperti berikut :

jan-11-2017-02-13-24

Thanks to :

http://nithinbekal.com/posts/phoenix-authentication/

http://meatherly.github.io/2015/05/11/phoenixauthentication/

Happy coding…

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: