Packages
drab
0.7.2
0.10.5
0.10.4
0.10.3
0.10.2
0.10.1
0.10.0
0.9.3
0.9.2
0.9.1
0.9.0
0.8.3
0.8.2
0.8.1
0.8.0
0.7.7
0.7.6
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.3
0.6.2
0.6.1
0.6.0
0.6.0-pre.1
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.1
0.4.0
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.1
0.1.0
Remote controlled frontend framework for Phoenix.
Current section
Files
Jump to
Current section
Files
lib/drab/socket.ex
defmodule Drab.Socket do
@moduledoc """
Drab operates on websockets. To enable it, you need to tell your application's socket module to use Drab.
For this, you will need to modify the socket module (by default it is `UserSocket` in `web/channels/user_socket.ex`).
There are two ways to archive this: let the Drab do the stuff, or provide your own `connect/2` callback. First
method is good for the application without socket level authentication. Second one is more elaborate, but
you could provide check or socket modification while connect.
## Method 1: Inject the code with `use Drab.Socket`
The straightforward one, you only need to inject the `Drab.Socket` module into your Socket (by default it is
`UserSocket` in `web/channels/user_socket.ex`):
defmodule MyApp.UserSocket do
use Phoenix.Socket
use Drab.Socket
...
end
This creates a channel "__drab:*" used by all Drab operations.
You may create your own channels inside a Drab Socket, but you *can't provide your own `connect` callback*.
Drab Client (on JS side) always connects when the page loads and Drab's built-in `connect` callback intercepts
this call. If you want to pass the parameters to the Channel, you may do it in `Drab.Client.run/2`, they
will appear in Socket's assigns. Please visit `Drab.Client` to learn more.
This method is supposed to be used with `Drab.Client.run/2` JS code generator.
## Method 2: Use your own `connect/2` callback
In this case, you **must not** add `use Drab.Socket` into your `UserSocket`. Instead, use the following code
snippet:
defmodule MyApp.UserSocket do
use Phoenix.Socket
channel "__drab:*", Drab.Channel
def connect(params, socket) do
Drab.Socket.verify(socket, params)
end
end
`Drab.Socket.verify/2` returns tuple `{:ok, socket}` or `:error`, where `socket` is modified with Drab internal
assigns, as well as with the additional assigns you may pass to `Drab.Client.generate/2`.
This method is supposed to be used with `Drab.Client.generate/2` JS code generator, followed by the
javascript `Drab.connect({token: ...})`, or with `Drab.Client.run/2` with additional assigns.
The following example adds `"auth_token" => "forty-two"` key-value pair to `params` in the `connect/2` callback:
### app.html.eex
<%= Drab.Client.generate(@conn) %>
<script>
if (window.Drab) Drab.connect({auth_token: "forty-two"});
</script>
Please do not forget to verify Drab token, even when using external authorization library:
### user_socket.ex
def connect(%{"auth_token" => auth_token} = params, socket) do
case MyAuthLib.authorize(auth_token) do
{:ok, authorized_socket} -> Drab.Socket.verify(authorized_socket, params)
_ -> :error
end
end
def connect(_, _), do: error
Please visit `Drab.Client` for more detailed information.
## Configuration Options
By default, Drab uses "/socket" as a path. In case of using different one, configure it with:
config :drab,
socket: "/my/socket"
"""
defmacro __using__(_options) do
quote do
channel("__drab:*", Drab.Channel)
def connect(%{"__drab_return" => _} = token, socket) do
Drab.Socket.verify(socket, token)
end
end
end
@doc """
Verifies the Drab token.
Returns:
* `{:ok, socket}` on success, where the socket is modified with internal Drab assigns, as well as
with additinal user's assigns passed by `Drab.Client.generate/2` or `Drab.Client.run/2`
* `:error`, when token is invalid
To be used with custom `connect/2` callbacks.
"""
@spec verify(Phoenix.Socket.t(), map) :: {:ok, term} | :error
def verify(socket, %{"__drab_return" => controller_and_action_token} = token) do
case Phoenix.Token.verify(
socket,
"controller_and_action",
controller_and_action_token,
max_age: 86_400
) do
{:ok, [__controller: controller, __commander: commander, __view: view, __action: action, __assigns: assigns]} ->
own_plus_external_assigns = Map.merge(Enum.into(assigns, %{}), socket.assigns)
socket_plus_external_assings = %Phoenix.Socket{
socket
| assigns: own_plus_external_assigns
}
{:ok,
socket_plus_external_assings
|> Phoenix.Socket.assign(:__controller, controller)
|> Phoenix.Socket.assign(:__commander, commander)
|> Phoenix.Socket.assign(:__view, view)
|> Phoenix.Socket.assign(:__action, action)
|> Phoenix.Socket.assign(:__priv, token["__priv"])}
{:error, _reason} ->
:error
end
end
def verify(_, _) do
:error
end
end