DEV Community

Haseeb Annadamban
Haseeb Annadamban

Posted on • Edited on

How to implement Authorization in elixir and phoenix

In the world of web development, ensuring the security and privacy of user data is paramount. One of the key aspects of this is implementing robust user authorization systems. In this article, we will delve into the process of building a simple yet effective user authorization system for the Phoenix framework, a system that can be seamlessly integrated into applications of various sizes. Whether you're a seasoned Elixir developer or a beginner just getting started with Phoenix, this guide will provide you with a practical approach to implementing authorization in your applications.

Essentially, authorization allows a user or an entity to perform an action if they have the necessary permissions. There are multiple approaches to address this. But this one matches my requirements and it allows me to easily apply this logic between multiple projects. As usual there are libraries to do this. But in my case it was not necessary.

Let's assume we want to authorize a rule where 'only the user who created a post can delete the post'. Basically, We can simply create a function to do that and call it from the action accessing it.

def authorize_delete_post(user, post) do
  if(user.id == post.user_id) do
    {:ok}
  else
   {:unauthorized}
  end
end
Enter fullscreen mode Exit fullscreen mode

This is the simplest type of authorization. It will work fine, but if you're reading this, you're likely seeking a more robust solution.

Authorize module

It is recommended to centralize all authorization logic. So, we can create a new module Authorize and move it there.

defmodule MyApp.Authorize do

alias MyApp.Accounts.User
alias MyApp.Posts.Post

 def check(:delete_post, %User{id: user_id}, %Post{user_id: user_id}), do: {:ok}
 def check(:delete_post, %User{}, %Post{}), do: {:unauthorized}
end
Enter fullscreen mode Exit fullscreen mode

From now on, we can call Authorize.check(:delete_post, user, post) to authorize the user to delete the post. it will return {:ok} if user_id of post is equal to id of user.

We can refactor here, as there will be a need to authorize many more actions. But remember, this part should be done based on your application's requirements.

defmodule MyApp.Authorize

alias MyApp.Accounts.User
alias MyApp.Posts.Post

 def check(:delete_post, %User{}=user, %Post{}=post), do: authorize_created_by(user, post)


 defp authorize_created_by(%User{id: user_id}, %{user_id: user_id}), do: ok
 defp authorize_created_by(%User{}, _), do: unauthorized

 defp ok, do: {:ok}
 defp unauthorized, do: {:unauthorized}
end
Enter fullscreen mode Exit fullscreen mode

we can latter add

def check(:update_post, %User{}=user, %Post{}=post), do: authorize_created_by(user, post)
Enter fullscreen mode Exit fullscreen mode

to do authorization for updating the post.

That concludes the implementation part. Now, we can optimize on the controller end.

Sending common error messages upon authorization error.

we have a plug in phoenix controller called action_fallback that allows us to call another plug as a fallback action. Follow the examples from https://hexdocs.pm/phoenix/Phoenix.Controller.html#action_fallback/1 to understand how to do that. I don't want to cover it here since it is already well documented. Also as a bonus you have information on how you should call our authorization module in there. So, in our case the FallbackController will have this plug.

def call(conn, {:unauthorized}) do
  # do something and return 403
end
Enter fullscreen mode Exit fullscreen mode

Conclusion

In conclusion, implementing a user authorization system in Elixir and Phoenix doesn't have to be a daunting task. With the right approach and understanding, you can build a system that is not only simple and easy to read, but also easy to implement and optimize based on your application's business logic. The method we've explored in this article provides a solid foundation, but remember, the world of web development is always evolving. Always be open to exploring new techniques, libraries, and best practices to ensure your authorization system remains robust, secure, and efficient. Happy coding!

Top comments (2)

Collapse
 
sergeychechaev profile image
Sergey

There is confusion at the beginning of the article.
I think you need to replace:

if(user.id == post.id) do

if(user.id == post.user_id) do

Collapse
 
haseebeqx profile image
Haseeb Annadamban

Thanks, Fixed it