diff --git a/features/config.exs b/features/config.exs
index 10fb29a1eb14eb46974f4dc3c00088bba806b1c7..a75437b245684d42990238f4d28456c080a2d360 100644
--- a/features/config.exs
+++ b/features/config.exs
@@ -3,9 +3,13 @@ defmodule WhiteBreadConfig do
 
   suite name:          "User Registration Features",
         context:       UserRegistrationContext,
-        feature_paths: ["features/"]
+        feature_paths: ["features/user_registration.feature"]
 
   suite name:          "User Login Features",
         context:       UserLoginContext,
-        feature_paths: ["features/"]
+        feature_paths: ["features/user_login.feature"]
+
+  suite name:          "Password Change Features",
+        context:       PasswordChangeContext,
+        feature_paths: ["features/password_change.feature"]
 end
diff --git a/features/contexts/password_change_context.exs b/features/contexts/password_change_context.exs
new file mode 100644
index 0000000000000000000000000000000000000000..1d6100479797b8d504275193031f9432e7b2d807
--- /dev/null
+++ b/features/contexts/password_change_context.exs
@@ -0,0 +1,118 @@
+defmodule PasswordChangeContext do
+  use WhiteBread.Context
+  use Hound.Helpers
+  alias PropTrackr.Accounts
+  alias PropTrackr.Repo
+  alias PropTrackr.Accounts.User
+  import PropTrackr.Authentication
+
+  scenario_starting_state fn _state ->
+    Ecto.Adapters.SQL.Sandbox.checkout(PropTrackr.Repo)
+    Ecto.Adapters.SQL.Sandbox.mode(PropTrackr.Repo, {:shared, self()})
+    Hound.start_session()
+    %{}
+  end
+
+  scenario_finalize fn _status, _state ->
+    Ecto.Adapters.SQL.Sandbox.checkin(PropTrackr.Repo)
+    Hound.end_session()
+  end
+
+  given_ ~r/^there exists following accounts$/, fn state, %{table_data: table} ->
+    table                                                                                
+    |> Enum.map(fn user_details -> User.changeset(%User{}, user_details) end)                            
+    |> Enum.each(fn changeset -> Repo.insert!(changeset) end)
+
+    existing_user = List.first(table)
+
+    new_password = "new password cool"
+
+    {
+      :ok,
+      state
+      |> Map.put(:email, existing_user[:email])
+      |> Map.put(:password, existing_user[:password])
+      |> Map.put(:new_password, new_password)
+    }
+  end
+
+  and_ ~r/^I am logged in$/, fn state ->
+    setup_session(state[:email], state[:password])
+    {:ok, state}
+  end
+
+  and_ ~r/^I want to change my password$/, fn state ->
+    navigate_to("/me/password")
+    {:ok, state}
+  end
+
+  and_ ~r/^I enter my current password$/, fn state ->
+    fill_field({:id, "current_password"}, state[:password])
+    {:ok, state}
+  end
+
+  and_ ~r/^I enter my current password invalid$/, fn state ->
+    fill_field({:id, "current_password"}, "obviously_invalid")
+    {:ok, state}
+  end
+
+  and_ ~r/^I enter my new password and confirm it$/, fn state ->
+    fill_field({:id, "new_password"}, state[:new_password])
+    fill_field({:id, "new_password_confirmation"}, state[:new_password])
+    {:ok, state}
+  end
+
+  and_ ~r/^I enter my new password and confirm it with a password that does not match$/, fn state ->
+    fill_field({:id, "new_password"}, state[:new_password])
+    fill_field({:id, "new_password_confirmation"}, "obviously_invalid")
+    {:ok, state}
+  end
+
+  and_ ~r/^I enter my new password and confirm it that match the current password$/, fn state ->
+    fill_field({:id, "new_password"}, state[:password])
+    fill_field({:id, "new_password_confirmation"}, state[:password])
+    {:ok, state}
+  end
+
+  when_ ~r/^I click change password$/, fn state ->
+    click({:id, "change_password"})
+    {:ok, state}
+  end
+
+  then_ ~r/^I should receive a success notification$/, fn state ->
+    assert visible_in_page? ~r"Password changed successfully! Please log in again"
+    {:ok, state}
+  end
+
+  then_ ~r/^I should receive an error message stating invalid current password$/, fn state ->
+    assert visible_in_page? ~r"Invalid current password!"
+    {:ok, state}
+  end
+
+  then_ ~r/^I should receive an error message stating that the new passwords don't match$/, fn state ->
+    assert visible_in_page? ~r"New passwords don't match!"
+    {:ok, state}
+  end
+
+  then_ ~r/^I should receive an error message stating that the new password is the same as the old password$/, fn state ->
+    assert visible_in_page? ~r"New password can't be the same as the old password!"
+    {:ok, state}
+  end
+
+  and_ ~r/^I should be shown the change password form again$/, fn state ->
+    assert visible_in_page? ~r"Change your password"
+    {:ok, state}
+  end
+
+  and_ ~r/^I should be redirected to the login page$/, fn state ->
+    assert current_path() == "/login"
+    {:ok, state}
+  end
+
+  defp setup_session(email, password) do
+    navigate_to("/login")
+    fill_field({:id, "email"}, email)
+    fill_field({:id, "password"}, password)
+    click({:id, "login_button"})
+  end
+end
diff --git a/features/password_change.feature b/features/password_change.feature
new file mode 100644
index 0000000000000000000000000000000000000000..1d4a3054362fb59e3f904746d06c4d0bf9943234
--- /dev/null
+++ b/features/password_change.feature
@@ -0,0 +1,49 @@
+Feature: Password Change
+
+    Scenario: Authenticated user can change their password
+        Given there exists following accounts
+              | name     | surname | birth_date | phone_number | email                      | password | confirm_password |
+              | Existing | Account | 2000-01-01 | 000          | existing.account@gmail.com | password | password         |
+        And I am logged in
+        And I want to change my password
+        And I enter my current password
+        And I enter my new password and confirm it
+        When I click change password
+        Then I should receive a success notification
+        And I should be redirected to the login page
+
+    Scenario: Authenticated user cannot change their password when invalid current password
+        Given there exists following accounts
+              | name     | surname | birth_date | phone_number | email                      | password | confirm_password |
+              | Existing | Account | 2000-01-01 | 000          | existing.account@gmail.com | password | password         |
+        And I am logged in
+        And I want to change my password
+        And I enter my current password invalid
+        And I enter my new password and confirm it
+        When I click change password
+        Then I should receive an error message stating invalid current password
+        And I should be shown the change password form again
+
+    Scenario: Authenticated user cannot change their password when new passwords don't match
+        Given there exists following accounts
+              | name     | surname | birth_date | phone_number | email                      | password | confirm_password |
+              | Existing | Account | 2000-01-01 | 000          | existing.account@gmail.com | password | password         |
+        And I am logged in
+        And I want to change my password
+        And I enter my current password
+        And I enter my new password and confirm it with a password that does not match
+        When I click change password
+        Then I should receive an error message stating that the new passwords don't match
+        And I should be shown the change password form again
+
+    Scenario: Authenticated user cannot change their password when new password is the same as the old password
+        Given there exists following accounts
+              | name     | surname | birth_date | phone_number | email                      | password | confirm_password |
+              | Existing | Account | 2000-01-01 | 000          | existing.account@gmail.com | password | password         |
+        And I am logged in
+        And I want to change my password
+        And I enter my current password
+        And I enter my new password and confirm it that match the current password
+        When I click change password
+        Then I should receive an error message stating that the new password is the same as the old password
+        And I should be shown the change password form again
diff --git a/lib/proptrackr_web/controllers/password_controller.ex b/lib/proptrackr_web/controllers/password_controller.ex
new file mode 100644
index 0000000000000000000000000000000000000000..4bfcb299b72fba33aa8b431f14406146fcf1ebee
--- /dev/null
+++ b/lib/proptrackr_web/controllers/password_controller.ex
@@ -0,0 +1,49 @@
+defmodule PropTrackrWeb.PasswordController do
+  use PropTrackrWeb, :controller
+
+  alias PropTrackr.Repo
+  alias PropTrackr.Accounts.User
+  import Ecto.Query, only: [from: 2]
+
+  def index(conn, _params) do
+    render conn, "index.html"
+  end
+
+  def create(conn, %{"current_password" => current_password, "new_password" => new_password, "new_password_confirmation" => new_password_confirmation}) do
+    cond do
+      new_password != new_password_confirmation ->
+        conn
+        |> put_flash(:error, "New passwords don't match!")
+        |> render("index.html")
+      # TODO: Insecure, needs refactoring when taking security into account
+      new_password == current_password ->
+        conn
+        |> put_flash(:error, "New password can't be the same as the old password!")
+        |> render("index.html")
+      true ->
+        user_id = get_session(conn, :user_id)
+        user = Repo.get(User, user_id)
+
+        if user.password == current_password do
+          {row_count, _} = from(u in User, where: u.id == ^user_id, select: u)
+                           |> Repo.update_all(set: [password: new_password])
+
+          if row_count == 1 do
+            conn
+            |> delete_session(:user_id)
+            |> put_flash(:info, "Password changed successfully! Please log in again")
+            |> redirect(to: ~p"/login")
+          else
+            # Note: Adding this just in case the update fails for some odd reason
+            conn
+            |> put_flash(:error, "Unknown error has occurred!")
+            |> redirect(to: ~p"/")
+          end
+        else
+          conn
+          |> put_flash(:error, "Invalid current password!")
+          |> render("index.html")
+        end
+    end
+  end
+end
diff --git a/lib/proptrackr_web/controllers/password_html.ex b/lib/proptrackr_web/controllers/password_html.ex
new file mode 100644
index 0000000000000000000000000000000000000000..eb00040f876d7933c46cc9a8447ec99004c7a2bd
--- /dev/null
+++ b/lib/proptrackr_web/controllers/password_html.ex
@@ -0,0 +1,5 @@
+defmodule PropTrackrWeb.PasswordHTML do
+  use PropTrackrWeb, :html
+
+  embed_templates "password_html/*"
+end
diff --git a/lib/proptrackr_web/controllers/password_html/index.html.heex b/lib/proptrackr_web/controllers/password_html/index.html.heex
new file mode 100644
index 0000000000000000000000000000000000000000..2f24942621f38bdbf8577bfa105cf377350e4636
--- /dev/null
+++ b/lib/proptrackr_web/controllers/password_html/index.html.heex
@@ -0,0 +1,15 @@
+<.header>
+  Change your password
+</.header>
+
+<.simple_form :let={f} for={} action={~p"/me/password"}>
+  <.input field={f[:current_password]} type="password" required label="Current password" />
+  <.input field={f[:new_password]} type="password" required label="New password" />
+  <.input field={f[:new_password_confirmation]} type="password" required label="Confirm password" />
+  <:actions>
+    <.button id="change_password">Change password</.button>
+  </:actions>
+</.simple_form>
+
+
+<.back navigate={~p"/"}>Changed your mind?</.back>
diff --git a/lib/proptrackr_web/router.ex b/lib/proptrackr_web/router.ex
index 768dd5e0ffb0262c8a74885241658a8214001010..872edcab7660b48368e634c75cdb688d97af3430 100644
--- a/lib/proptrackr_web/router.ex
+++ b/lib/proptrackr_web/router.ex
@@ -22,6 +22,7 @@ defmodule PropTrackrWeb.Router do
     resources "/users", UserController, only: [:index]
     resources "/register", RegisterController, only: [:index, :create]
     resources "/login", LoginController, only: [:index, :create]
+    resources "/me/password", PasswordController, only: [:index, :create]
   end
 
   # Other scopes may use custom stacks.
diff --git a/test/proptrackr_web/controllers/password_controller_test.exs b/test/proptrackr_web/controllers/password_controller_test.exs
new file mode 100644
index 0000000000000000000000000000000000000000..ae23a4cdf996f5c7aaba8e0983dcdba7ccf1ae2a
--- /dev/null
+++ b/test/proptrackr_web/controllers/password_controller_test.exs
@@ -0,0 +1,79 @@
+defmodule PropTrackrWeb.PasswordControllerTest do
+  use PropTrackrWeb.ConnCase
+  alias PropTrackr.Accounts.User
+  alias PropTrackr.Repo
+
+  @user_credentials %{email: "test.user@gmail.com", password: "testing", new_password: "new_password"}
+
+  setup do
+    user = %User{
+      name: "Test",
+      surname: "User",
+      birth_date: "2000-01-01",
+      phone_number: "000",
+      bio: "Yo",
+      email: "test.user@gmail.com",
+      password: "testing",
+      confirm_password: "testing",
+    }
+    user = Repo.insert!(user)
+
+    {:ok, %{user: user}}
+  end
+
+  test "Authenticated user should be able to change their password with correct current credentials and matching new passwords", %{conn: conn, user: user} do
+    conn = conn |> setup_session(user)
+    conn = conn
+           |> post("/me/password",
+                   current_password: @user_credentials[:password],
+                   new_password: @user_credentials[:new_password],
+                   new_password_confirmation: @user_credentials[:new_password])
+    assert redirected_to(conn) == "/login"
+    conn = get conn, redirected_to(conn)
+    assert html_response(conn, 200)
+    assert get_flash(conn, :info) =~ ~r/Password changed successfully! Please log in again/
+    assert get_session(conn, :user_id) == nil
+
+    updated_user = Repo.get(User, user.id)
+    assert updated_user.password == @user_credentials[:new_password]
+  end
+
+  test "Authenticated user should receive an error if the current password is invalid", %{conn: conn, user: user} do
+    conn = conn |> setup_session(user)
+    conn = conn
+           |> post("/me/password",
+                   current_password: "obviously_invalid",
+                   new_password: @user_credentials[:new_password],
+                   new_password_confirmation: @user_credentials[:new_password])
+    assert html_response(conn, 200)
+    assert get_flash(conn, :error) =~ ~r/Invalid current password!/
+  end
+
+  test "Authenticated user should receive an error if the new password does not match the new password confirmation", %{conn: conn, user: user} do
+    conn = conn |> setup_session(user)
+    conn = conn
+           |> post("/me/password",
+                   current_password: @user_credentials[:password],
+                   new_password: @user_credentials[:new_password],
+                   new_password_confirmation: "obviously_does_not_match")
+    assert html_response(conn, 200)
+    assert get_flash(conn, :error) =~ ~r/New passwords don't match!/
+  end
+
+  test "Authenticated user should receive an error if the new password is the same as the old password", %{conn: conn, user: user} do
+    conn = conn |> setup_session(user)
+    conn = conn
+           |> post("/me/password",
+                   current_password: @user_credentials[:password],
+                   new_password: @user_credentials[:password],
+                   new_password_confirmation: @user_credentials[:password])
+    assert html_response(conn, 200)
+    assert get_flash(conn, :error) =~ ~r/New password can't be the same as the old password!/
+  end
+
+  defp setup_session(conn, user) do
+    conn = conn |> post("/login", email: user.email, password: user.password)
+    conn = get conn, redirected_to(conn)
+    conn
+  end
+end