Readme add
This commit is contained in:
commit
5d4d4e8495
217
README.md
Normal file
217
README.md
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
# Auth tutorial for setting up basic user auth with `put_session()`
|
||||||
|
|
||||||
|
**NOTE: PHOENIX_APP and PHOENIX_APP_WEB are just macros here, in your app user your apps name!**
|
||||||
|
|
||||||
|
## Deps used
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Admin auth
|
||||||
|
{:basic_auth, "~> 2.2.2"},
|
||||||
|
# Password hashing
|
||||||
|
{:bcrypt_elixir, "~> 2.0"}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
Start off by running `mix deps.get`
|
||||||
|
|
||||||
|
### User Schema
|
||||||
|
|
||||||
|
Make sure that you have some context that is like `Accounts.User` with `email:unique` and `password_hash` and that you have `Accounts` module with `get_user!(id)`... etc.
|
||||||
|
|
||||||
|
You can do this with
|
||||||
|
|
||||||
|
```bash
|
||||||
|
terminal:~/PHOENIX_PROJECT/$ mix phx.gen.html Accounts User users email:string:unique password_hash:string
|
||||||
|
```
|
||||||
|
|
||||||
|
### Password hashing
|
||||||
|
|
||||||
|
After that open up `accounts/user.ex` and add line bellow to `changeset()`
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
def changeset(user, attrs) do
|
||||||
|
...
|
||||||
|
|> update_change(:password_hash, &Bcrypt.hash_pwd_salt/1)
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
### Auth helper
|
||||||
|
|
||||||
|
In `PHOENIX_APP_WEB/` create folder `/helpers` and create file `auth.ex`
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
defmodule PHOENIX_APP_WEB.Helpers.Auth do
|
||||||
|
import Plug.Conn, only: [get_session: 2]
|
||||||
|
alias PHOENIX_APP.{Repo, Accounts.User}
|
||||||
|
|
||||||
|
def signed_in?(conn) do
|
||||||
|
user_id = get_session(conn, :current_user_id)
|
||||||
|
if user_id, do: !!Repo.get(User, user_id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
Then open your `PHOENIX_APP_WEB.ex` and inside add this `import`, we are doing this because we want to have this function in every `view`
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
def view do
|
||||||
|
quote do
|
||||||
|
...
|
||||||
|
import PHOENIX_APP_WEB.Helpers.Auth, only: [signed_in?: 1]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
### Session templates/view/router/controller
|
||||||
|
|
||||||
|
#### Templates
|
||||||
|
|
||||||
|
Create `/session` folder in `/templates` and inside create 2 files `sign-in.html.eex` and `sign-up.html.eex`
|
||||||
|
One will be used for Signing in and the other for Signing out
|
||||||
|
|
||||||
|
###### /session/sign-in.html
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
<%= form_for @conn, Routes.session_path(@conn, :create_session),[as: :session] , fn f -> %>
|
||||||
|
|
||||||
|
<%= label f, :email %>
|
||||||
|
<%= text_input f, :email %>
|
||||||
|
<%= error_tag f, :email %>
|
||||||
|
|
||||||
|
<%= label f, :password %>
|
||||||
|
<%= password_input f, :password_hash %>
|
||||||
|
<%= error_tag f, :password_hash %>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<%= submit "Sign in" %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
###### /session/sign-up.html
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
<%= form_for @changeset, Routes.session_path(@conn, :create_user) , fn f -> %>
|
||||||
|
<%= if @changeset.action do %>
|
||||||
|
<div class="alert alert-danger">
|
||||||
|
<p>Oops, something went wrong! Please check the errors below.</p>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<%= label f, :email %>
|
||||||
|
<%= text_input f, :email %>
|
||||||
|
<%= error_tag f, :email %>
|
||||||
|
|
||||||
|
<%= label f, :password %>
|
||||||
|
<%= password_input f, :password_hash %>
|
||||||
|
<%= error_tag f, :password_hash %>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<%= submit "Sign up" %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Views
|
||||||
|
|
||||||
|
Create `session_view.ex` in `/views` folder
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
defmodule PHOENIX_APP_WEB.SessionView do
|
||||||
|
use PHOENIX_APP_WEB, :view
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Router
|
||||||
|
|
||||||
|
under you `scope "/"` add following
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
scope "/", PHOENIX_APP_WEB do
|
||||||
|
pipe_through :browser
|
||||||
|
...
|
||||||
|
# Sign in
|
||||||
|
get "/sign-in", SessionController, :sign_in
|
||||||
|
post"/sign-in", SessionController, :create_session
|
||||||
|
|
||||||
|
# Sign up
|
||||||
|
get "/sign-up", SessionController, :sign_up
|
||||||
|
post"/sign-up", SessionController, :create_user
|
||||||
|
|
||||||
|
# Sign out
|
||||||
|
post "/sign-out", SessionController, :sign_out
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Controller
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
defmodule PHOENIX_APP_WEB.SessionController do
|
||||||
|
use PHOENIX_APP_WEB, :controller
|
||||||
|
|
||||||
|
alias PHOENIX_APP.Accounts
|
||||||
|
alias Accounts.User
|
||||||
|
|
||||||
|
def sign_in(conn, _params) do
|
||||||
|
# If user is logged in and tries to connecto to "/sign-in" redirect him
|
||||||
|
if is_logged?(conn) do
|
||||||
|
redirect(conn, to: Routes.user_path(conn, :show))
|
||||||
|
else
|
||||||
|
render(conn, "sign-in.html")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def sign_up(conn, _params) do
|
||||||
|
# If user is logged in and tries to connecto to "/sign-up" redirect him
|
||||||
|
if is_logged?(conn) do
|
||||||
|
redirect(conn, to: Routes.user_path(conn, :show))
|
||||||
|
else
|
||||||
|
changeset = Accounts.change_user(%User{})
|
||||||
|
render(conn, "sign-up.html", changeset: changeset)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp is_logged?(conn), do: !!get_session(conn, :current_user_id)
|
||||||
|
|
||||||
|
def create_session(conn, %{"session" => auth_params} = _params) do
|
||||||
|
user = Accounts.get_by_email(auth_params["email"])
|
||||||
|
|
||||||
|
case Bcrypt.check_pass(user, auth_params["password_hash"]) do
|
||||||
|
{:ok, user} ->
|
||||||
|
conn
|
||||||
|
|> put_session(:current_user_id, user.id)
|
||||||
|
|> put_flash(:info, "Sign in, successful!")
|
||||||
|
|> redirect(to: Routes.user_path(conn, :show))
|
||||||
|
|
||||||
|
{:error, _} ->
|
||||||
|
conn
|
||||||
|
|> put_flash(:error, "Invalid e-mail/password. Try again!")
|
||||||
|
|> redirect(to: Routes.session_path(conn, :sign_in))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_user(conn, %{"user" => user_params}) do
|
||||||
|
case Accounts.create_user(user_params) do
|
||||||
|
{:ok, user} ->
|
||||||
|
conn
|
||||||
|
|> put_session(:current_user_id, user.id)
|
||||||
|
|> put_flash(:info, "Sign up, successful!")
|
||||||
|
|> redirect(to: Routes.user_path(conn, :show))
|
||||||
|
|
||||||
|
{:error, %Ecto.Changeset{} = changeset} ->
|
||||||
|
render(conn, "sign-up.html", changeset: changeset)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def sign_out(conn, _params) do
|
||||||
|
conn
|
||||||
|
|> delete_session(:current_user_id)
|
||||||
|
|> put_flash(:info, "Signed out.")
|
||||||
|
|> redirect(to: Routes.page_path(conn, :index))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
```
|
Loading…
Reference in New Issue
Block a user