Authorization

Learn how to implement the authorization feature.

Permissible actions

In this last lesson, we tackle authorization: deciding if an action is permissible. The action we really care about is joining a Channel. The rules for this are simple. We want only two players to join a Channel on any given topic-subtopic and also want those two players to have different screen names.

Authentication resources

In Islands, we don’t need authentication—determining if users are who they say they are. If your application needs authentication, there are resources out there to help. Check out the documentation for Phoenix.Token if token-based authentication is needed.

Now that we have Presence, we can write functions to check both of the authorization conditions we outline. We can roll them into a single function that determines whether a given player can join.

The first condition we need to check is the number of players that have already joined the Channel. The Presence.list/1 function returns a map. This map’s keys are the screen names of all the players who have joined the channel on a specific topic-subtopic. We can write a function to return that number by getting the Presence list and counting the keys:

defp number_of_players(socket) do 
  socket
  |> Presence.list()
  |> Map.keys()
  |> length()
end

With these two functions, we have enough information to see if a player is authorized to join the Channel:

defp authorized?(socket, screen_name) do
  number_of_players(socket) < 2 && !existing_player?(socket, screen_name)
end

We can also tell if a player is already subscribed to this game Channel by checking if a given screen name is already a key in the Presence map:

defp existing_player?(socket, screen_name) do 
  socket
  |> Presence.list()
  |> Map.has_key?(screen_name)
end

New IEx session

Now, we can use the authorized?/2 function in join/3 to decide if we should let a new player join:

def join("game:" <> _player, %{"screen_name" => screen_name}, socket) do
  if authorized?(socket, screen_name) do
    send(self(), {:after_join, screen_name})
    {:ok, socket}
  else
    {:error, %{reason: "unauthorized"}}
  end
end

Note: Run the app below and open the SPA link in two separate browser tabs. Run the following commands in the JavaScript consoles as instructed.

Get hands-on with 1200+ tech skills courses.