mariosangiorgio
Is there any recommended way of doing API authentication?
I’m working on a toy project and I’d like to add user authentication to regulate the access to its Phoenix API.
As a starting point, I found this post and I like the approach it proposes.
I also found that Phoenix has the facility to generate tokens (Phoenix.Token). I see that the post I linked above uses SecureRandom to generate tokens. Is it recommended to use the output of Phoenix.Token.sign instead? Given that it allows to check whether the token is expired or not it seems to be strictly better than a random number. That would also save me from needing to keep track of the active sessions in the database.
That approach should cover pretty much all I need. Later on I might want to invalidate tokens if the user explicitly logs out, but I could easily do that with a blacklist.
So, everything is good but that I also found out that there are a several competing authentication libraries and I wanted to check if any of those is the primary choice of the community and it’s recommended over the approach I’m thinking of adopting.
Most Liked
OvermindDL1
Do note that Guardian tokens are JWT, meaning they are large and heavy and overkill for many cases. If you can store the authorization needed into the JWT so you can prevent hitting the database in the API then Guardian is worth it, else use Phoenix Tokens and authorize via the database or cache or so.
OvermindDL1
I just roll my own since it is only a half-dozen or so lines of code anyway. Like here is the meat of mine:
def verify_login(%Plug.Conn{}=conn) do
with\
token = Plug.Conn.get_session(conn, "account"),
{:ok, {account_id, account_session}} <- verify("account", token, max_age: token_max_age()),
true <- confirm_account_id_session?(account_id, account_session) do
Plug.Conn.assign(conn, :account_id, account_id)
else
err -> normalize(err)
end
end
def verify_login(token) when is_binary(token), do: verify("account_id", token, max_age: token_max_age())
def verity_login(token) do
Logger.warn("Invalid token typed passed to verity_login of: #{inspect token}")
{:error, :invalid_token}
end
I can pass either a Plug.Conn or a raw token string to this, it then calls verify:
def verify(salt, token, opts \\ []), do: Phoenix.Token.verify(get_token_key(), salt, token, opts)
Which just returns the data of the token. There are a dozen or so other little helper functions too, like I don’t store any permissions or so data in the token (but you could to save a database hit) and instead I just store a ‘session’ token inside the phoenix token, which I look up via:
def get_account_id_session(account_id, account_session) do
query =
from s in DB.Account.Session,
where: is_nil(s.removed_at) and
((s.id == ^account_id and s.token == ^account_session) or (s.id == ^account_session and s.token == ^account_id))
Repo.one(query)
end
I just confirm the account id and session id both (which is stored in the phoenix token) exist in the database (the functions that call these confirm that the age of the token is valid too for the given user and so forth). It is pretty simple stuff and honestly I’ve over-engineered it, you could do it in like 3 lines of code. ^.^;
kokolegorille
I like to use guardian for this task
You can find examples on how to implement it…
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance









