- Auth is one of the critical features, leaks in which can tumble entire Orgs
Users will provide traditional creds while Registration; will be stored safely in DB.
Sessions will be used with info of User; will have Users marked authenticated on giving correct creds.
- comeonin is a spec for password hashing libraries; we'll use Pbkdf2 password hashing as it doesn't need native bindings alongwith being resistant to Dictionary/Rainbow attacks
there is a library maintained by same contributor called phxauth managin Auth for Phoenix & Plug-based Webapps, Getting Started
- add
{:pbkdf2_elixir, "~> 1.4"}tomix.exs; thenmix deps.get
-
we'll use one changeset per use-case; Chapter-04's Changeset handle all attributes yet, except Passwords
-
build another changeset for sensitive data case as credential changes
-
add 2 more fields to Schema,
:password(not stored, since plaintext) &:password_hash(will be added to DB)
virtual: truemarks Struct only fields for Ecto, which are not to be persisted in backed DB
- add Videologue.Accounts.User.registration_changeset/2 to handle user registrations
here
registration_changeset/2resuseschangese/2as first Pipe; thencast -> validate_required -> validate_lengththe password; then pushes Changeset toput_pass_hash/1which persists Hash if Password validated else returns Changeset with errors
- in projects for real use, shall use libs/measures to ensure Passwords are stronger
add a new migration to provide a temp-pass for all existing users
-
keep Videologue.Accounts
create_userto manage any workflow requiring it -
add
Videologue.Accounts.register_user/1&change_registration/2managing a/c registration with passwords; just usingregistration_changesetinstead ofchangeset -
update VideologueWeb.UserController
newto usechange_registration/2andcreateto useregister_user/1 -
update new.html.eex to have a new
divfor Password field
now we need an Auth Service, make it available throughout Pipelines via Plugs
-
typical Plug take a conn, return a conn
-
2 kinds of Plugs:
module plugs(providig 2 functionsinit/1&call/2with config details) andfunction plugs(a single function)
endpoint.exusage ofplug Plug.RequestIdis a module plug;router.exusage ofplug :accepts, ["json"]is a function plug
- used when to share a plug across modules; spec need it to have
init/1&call/2
defmodule ThisPlug do
def init(opts), do: opts
def call(conn, _opts), do: conn
end
- uses result of
initas 2nd arg tocall.. in PRODinitruns only once at compile time; during DEV it runs compile time
Having common Data Structure piped through a chain as input/output is Key. Right Common Data Structure here is key.
Plug.Conn Fields, hexdocs
Cowboy (default Phoenix web-server) parses inbound request, which contains string except where otherwsie specified
-
hostandmethodare obvious;path_infois List of path segments;req_headersis list of tuples for headers;schemeis Protocol as atom -
there are fetchable fields in Conn as well (empty until called for to save process) like
cookiesare request/response cookies
paramsare request params; some plugs help parse these from query string or request body
- some fields are used to process web request & keep info of plug pipeline
assigns, user-defined map for whatever
halted, connection might be halted in cases like auth fail
-
can also find a
secret_key_basefor encryption -
Response fields
resp_body,resp_cookies,resp_headers,status -
Some private fields reserved for adapter/frameworks:
adapteris underlying web-server,privatefield has map for private use of frameworks
- auth works in 2 stages
store user-id in session when new user registers/a user logs in
check if a new user is in session; store in
conn.assignsfor every incoming so accessible in controllers & views
-
create
VideologueWeb.Authplug viaimport Plug.Connat auth.ex addinguserdetails to Assigns foruser_idin current session -
add this Auth plug in browser pipeline
-
let's disable access to
:index&:showinVideologueWeb.UserControllerunless logged in -
if we add
authenticate/1private function; using it as following inindex/2(similarly forshow) would work fine
def index(conn, _params) do
case authenticate(conn) do
%Plug.Conn{halted: true} = conn ->
conn
conn ->
users = Accounts.list_users()
render(conn, "index.html", users: users)
end
end
...
defp authenticate(conn) when is_nil(conn.assigns.current_user) do
conn
|> put_flash(:error, "You are not logged in.")
|> redirect(to: Routes.page_path(conn, :index))
|> halt()
end
defp authenticate(conn) do
conn
end
- that would be repititive, bloated & error prone; let's Plug the
authenticate/2function by also passing_optsas param andplug :authenticate when action in [:index, :show]
with Plug we don't need to update the
indexorshowaction; since Plug pipeline explicitly itself checks forhalted: truein Connection between Plug calls
-
add
VideologueWeb.Auth.login/2which update Assigns forcurrent_user, putsuser.idin session info & configures session renew totrueavoiding Session Fixation attacks -
add Pipe for this
logintoUserController.do_create(conn, {:ok, user})for Session to be updated and log-in to happen on user creation
- add
Videologue.Accounts.authenticate_by_username_and_password/2to auth from DB data
Pbkdf2.no_user_verify()gets used to avoid Timing Attack by simulating a password check with variable timing
- add
sessionsresource to Routes calling for SessionController for:new,:createand:deleteactions
add a session view for Templates to get a home
:newto just render new.html.eex which shows a Log-in form if no user is logged in, else just info of logged in user and a link to log-out to log back in
- add Log-out link to
app.eexlayout as well; alongwith current-user if logged-in otherwise a Log-in/Register link
we're passing
deleteHTTP method for Log-out link, which makeslinkto generate a form tag instead of anchor tag... method defaults to GET
-
add
VideologueWeb.Auth.logout/1todropsession -
add
VideologueWeb.SessionControllerfordeleteaction using thislogout
we have a full working Log-in/out and Sign-up flow now