davemccrea

davemccrea

A few questions regarding Phoenix API authentication for a mobile app

Here comes a question about using Phoenix.Token and API authentication more generally.

I am dipping my toes in the water of native app development (using React Native … hopefully in the not-to-distant future LiveView Native is a viable alternative). I have a side project where a mobile app on each platform has become a must-have.

The app has pretty basic business requirements. A user should (1) be able to log in to their organisation, (2) subscribe to changes to a limit set of resources and (3) receive push notifications when the status of the resource changes. Pretty barebones. The user should remain logged in until they log out or uninstall the app from their phone.

I have been back and forth on the issue of authentication. The API surface is quite minimal, and the data exposed to the user is both read-only and low sensitivity in nature.

I would love to hear other people’s experience in setting up authentication for native apps. Here is my approach so far:

  • Passwordless authentication is used. The user enters their email into the app and is sent a magic link / login code.
  • The login code is validated and a Phoenix.Token created containing session_id (sessions are stored in the database). The token is signed with max_age: :infinity.
  • The token is returned in the response and stored in secure storage on the device.
  • Future API requests include the access token in the authorization header (i.e. as a bearer token).
  • Any incoming requests to /api/:resource are handled by a Plug. The token is validated and the session retreived from the database. The user_id is assigned to the conn.

The above solution has some obvious downsides:

  • Tokens are not refreshed. A Dockyard article names a BREACH attack as a possible attack vector.
  • For each request the session is retreived from the database. I don’t think is a huge issue.

And finally … here is some code:

# AppAuth module

def handle_login(:email, email) do
	with {:ok, staff} <- staff_exists?(email),
		 {:ok, login_code} <- LoginCode.create_login_code(staff),
		 {:ok, email} <- Notifier.deliver_login_code(staff, login_code.code) do
	  {:ok, email}
	else
	  error -> error
	end
end

def handle_login(:code, email, code, device_id) do
	with {:ok, %{staff_id: staff_id}} <- LoginCode.verify_login_code(email, code),
	       {:ok, session} <- Session.create_session(staff_id, device_id),
	       access_token <- AccessToken.generate_access_token(session.id) do
	   {:ok, access_token}
	else
	   error -> error
        end
end

# Plug

def call(conn, _opts) do
	with ["Bearer " <> token] <- get_req_header(conn, "authorization"),
	       {:ok, session_id} = AccessToken.verify_access_token(token),
	       {:ok, %{staff_id: staff_id}} <- Session.get_session(session_id) do
	    assign(conn, :staff_id, staff_id)
	else
	    _ ->
		conn
		|> # ...
		|> halt()

	end
end

So to the question(s):

  • Is this secure (enough)? Any glaring misteps?
  • Downsides to not refreshing the token?
  • Downsides to looking up the session on each request? For context, I anticipating serving < 2000 users (but hopefully more!).

Thanks for reading!

Where Next?

Popular in Questions Top

electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
dokuzbir
I want to highlight html closing tags when i click a html tag. That works in .html files but doesnt work for html.eex templates. How can...
New
jay1
Why is it that the mnesia database isn’t the most preferred database for use in Elixir/Phoenix?
New
Emily
I have VueJS GUIs with the project generated using Webpack. I have Elixir modules that will need to be used by the VueJS GUIs. I forese...
New
lucidguppy
I have a super simple question about elixir - how would I take a file like this foo bar baz and output a new file that enumerates th...
New
JDanielMartinez
Hi! May someone helps me, please! I have two apps into an umbrella project: the first one is Database, which manages queries, and the se...
New
rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list. ...
New
shijith.k
I am trying to start a new phoenix project with elixir 1.9, but mix phx.new does not work. It says that ** (Mix) The task "phx.new" could...
New
PeterCarter
There are pre-rolled solutions for other frameworks that do work. However, Phoenix does not seem to have these. Have people had good expe...
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New

Other popular topics Top

danschultzer
None of the current solutions worked well for me, so I went ahead and built a user management system from scratch. This project took far...
548 29377 241
New
electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
ovidiubadita
Hey all, I discovered Elixir and I love it. I always wanted to learn a functional programming and I intended to go for Haskell, but afte...
New
minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
chrismccord
This release brings a number of exciting features, including integration with the new Phoenix LiveDashboard and Phoenix LiveView. There h...
New
josevalim
Hi everyone, One of the features added to Elixir early on to help integration with Erlang code was the idea of overridable function defi...
New
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
axelson
This post is a wiki (feel free to hit the edit button near the bottom right of this post to add your own changes!) This post collects co...
239 47930 226
New
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New

Latest on Elixir Forum

We're in Beta

About us Mission Statement