Current section

Files

Jump to
guardian lib guardian phoenix socket.ex
Raw

lib/guardian/phoenix/socket.ex

if Code.ensure_loaded?(Phoenix) do
defmodule Guardian.Phoenix.Socket do
@moduledoc """
Provides functions for managing authentication with sockets.
This module mostly provides convenience functions for storing tokens, claims and resources
on the socket assigns.
The main functions you'll be interested in are:
* `Guardian.Phoenix.Socket.authenticated?` - check if the socket has been authenticated
* `Guardian.Phoenix.Socket.authenticate` - Sign in a resource to a socket. Similar to `Guardian.Plug.authenticate`
### Getters
Once you're authenticated with your socket, you can use the getters
to fetch information about the authenticated resource for the socket.
* `Guardian.Phoenix.Socket.current_claims`
* `Guardian.Phoenix.Socket.current_token`
* `Guardian.Phoenix.Socket.current_resource`
These are the usual functions you'll want to use when dealing with authentication on sockets.
There is a bit of a difference between the usual `Guardian.Plug.sign_in` and the socket one.
The socket authenticate receives a token and signs in from that.
Please note that this is mere sugar on the underlying Guardian functions.
As an example:
```elixir
defmodule MyApp.UserSocket do
use Phoenix.Socket
def connect(%{"token" => token}, socket) do
case Guardian.Phoenix.Socket.authenticate(socket, MyApp.Guardian, token) do
{:ok, authed_socket} ->
{:ok, authed_socket}
{:error, _} -> :error
end
end
# This function will be called when there was no authentication information
def connect(_params, socket) do
:error
end
end
```
If you want to authenticate on the join of a channel, you can import this
module and use the authenticate function as normal.
"""
import Guardian.Plug.Keys
alias Guardian.Plug, as: GPlug
alias Phoenix.Socket
@doc """
Puts the current token onto the socket for later use.
Get the token from the socket with `current_token`
"""
@spec put_current_token(
socket :: Socket.t(),
token :: Guardian.Token.token() | nil,
key :: atom | String.t() | nil
) :: Socket.t()
def put_current_token(socket, token, key \\ :default) do
Socket.assign(socket, token_key(key), token)
end
@doc """
Put the current claims onto the socket for later use.
Get the claims from the socket with `current_claims`
"""
@spec put_current_claims(
socket :: Socket.t(),
new_claims :: Guardian.Token.claims() | nil,
atom | String.t() | nil
) :: Socket.t()
def put_current_claims(socket, new_claims, key \\ :default) do
Socket.assign(socket, claims_key(key), new_claims)
end
@doc """
Put the current resource onto the socket for later use.
Get the resource from the socket with `current_resource`
"""
@spec put_current_resource(
socket :: Socket.t(),
resource :: Guardian.Token.resource() | nil,
key :: atom | String.t() | nil
) :: Socket.t()
def put_current_resource(socket, resource, key \\ :default) do
Socket.assign(socket, resource_key(key), resource)
end
@doc """
Fetches the `claims` map that was encoded into the token from the socket.
"""
@spec current_claims(Socket.t(), atom | String.t()) :: Guardian.Token.claims() | nil
def current_claims(socket, key \\ :default) do
key = claims_key(key)
socket.assigns[key]
end
@doc """
Fetches the token that was provided for the initial authentication.
This is provided as an encoded string and fetched from the socket.
"""
@spec current_token(Socket.t(), atom | String.t()) :: Guardian.Token.token() | nil
def current_token(socket, key \\ :default) do
key = token_key(key)
socket.assigns[key]
end
@doc """
Fetches the resource from that was previously put onto the socket.
"""
@spec current_resource(Socket.t(), atom | String.t()) :: Guardian.Token.resource() | nil
def current_resource(socket, key \\ :default) do
key = resource_key(key)
socket.assigns[key]
end
@doc """
Boolean if the token is present or not to indicate an authenticated socket
"""
@spec authenticated?(Socket.t(), atom | String.t()) :: true | false
def authenticated?(socket, key \\ :default) do
current_token(socket, key) != nil
end
@doc """
Assigns the resource, token and claims to the socket.
Use the `key` to specify a different location. This allows
multiple tokens to be active on a socket at once.
"""
@spec assign_rtc(
socket :: Socket.t(),
resource :: Guardian.Token.resource() | nil,
token :: Guardian.Token.token() | nil,
claims :: Guardian.Token.claims() | nil,
key :: atom | String.t() | nil
) :: Socket.t()
def assign_rtc(socket, resource, token, claims, key \\ :default) do
socket
|> put_current_token(token, key)
|> put_current_claims(claims, key)
|> put_current_resource(resource, key)
end
@doc """
Given an implementation module and token, this will
* decode and verify the token
* load the resource
* store the resource, claims and token on the socket.
Use the `key` to store the information in a different location.
This allows multiple tokens and resources on a single socket.
"""
@spec authenticate(
socket :: Socket.t(),
impl :: module,
token :: Guardian.Token.token() | nil,
claims_to_check :: Guardian.Token.claims(),
opts :: Guardian.opts()
) :: {:ok, Socket.t()} | {:error, atom | any}
def authenticate(socket, impl, token, claims_to_check \\ %{}, opts \\ [])
def authenticate(_socket, _impl, nil, _claims_to_check, _opts), do: {:error, :no_token}
def authenticate(socket, impl, token, claims_to_check, opts) do
with {:ok, resource, claims} <-
Guardian.resource_from_token(impl, token, claims_to_check, opts),
key <- Keyword.get(opts, :key, GPlug.default_key()) do
authed_socket = assign_rtc(socket, resource, token, claims, key)
{:ok, authed_socket}
end
end
end
end