Current section
Files
Jump to
Current section
Files
lib/ex_twilio/worker_capability.ex
defmodule ExTwilio.WorkerCapability do
@moduledoc """
Capability tokens are used to sign communications from devices
to Twilio. You create a token on your server, specify what
capabilities you would like your device to have, then pass
the token to your client to use. The tokens generated are
JSON Web Tokens (JWT).
- [Capability docs](https://www.twilio.com/docs/api/client/capability-tokens)
- [JWT docs](https://jwt.io/introduction/)
## Example
ExTwilio.WorkerCapability.new("worker_sid", "workspace_sid")
|> ExTwilio.WorkerCapability.token
"xxxxx.yyyyy.zzzzz"
"""
alias ExTwilio.Config
use Joken.Config
defstruct account_sid: nil,
auth_token: nil,
policies: [],
start_time: nil,
ttl: nil,
worker_sid: nil,
workspace_sid: nil
@type t :: %__MODULE__{
account_sid: String.t() | nil,
auth_token: String.t() | nil,
policies: list,
start_time: non_neg_integer | nil,
ttl: non_neg_integer | nil,
worker_sid: String.t() | nil,
workspace_sid: String.t() | nil
}
@doc """
Initialises a new capability specification with a TTL of one hour,
and the accound sid and auth token taken from the configuration.
## Example
ExTwilio.WorkerCapability.new
"""
@spec new(String.t(), String.t()) :: t
def new(worker_sid, workspace_sid) do
%__MODULE__{}
|> starting_at(:erlang.system_time(:seconds))
|> with_ttl(3600)
|> with_account_sid(Config.account_sid())
|> with_auth_token(Config.auth_token())
|> with_worker_sid(worker_sid)
|> with_workspace_sid(workspace_sid)
end
@doc """
Sets the time at which the TTL begins in seconds since epoch.
## Example
Sets the TTL to begin on 24th May, 2016
ExTwilio.WorkerCapability.starting_at(1464096368)
"""
@spec starting_at(t, non_neg_integer) :: t
def starting_at(capability_struct = %__MODULE__{}, start_time) do
%{capability_struct | start_time: start_time}
end
@doc """
Sets the Twilio account sid used to issue the token.
## Example
Sets the account sid to be XXX
ExTwilio.WorkerCapability.with_account_sid('XXX')
"""
@spec with_account_sid(t, String.t()) :: t
def with_account_sid(capability_struct = %__MODULE__{}, account_sid) do
%{capability_struct | account_sid: account_sid}
end
@doc """
Sets the Twilio account auth token used to sign the capability token.
## Example
Sets the auth token to be XXX
ExTwilio.WorkerCapability.with_auth_token('XXX')
"""
@spec with_auth_token(t, String.t()) :: t
def with_auth_token(capability_struct = %__MODULE__{}, auth_token) do
%{capability_struct | auth_token: auth_token}
end
@doc """
Sets the Twilio worker sid used to sign the capability token.
## Example
Sets the worker sid to be XXX
ExTwilio.WorkerCapability.with_worker_sid('XXX')
"""
@spec with_worker_sid(t, String.t()) :: t
def with_worker_sid(capability_struct = %__MODULE__{}, worker_sid) do
%{capability_struct | worker_sid: worker_sid}
end
@doc """
Sets the Twilio workspace sid used to sign the capability token.
## Example
Sets the workspace sid to be XXX
ExTwilio.WorkerCapability.with_workspace_sid('XXX')
"""
@spec with_workspace_sid(t, String.t()) :: t
def with_workspace_sid(capability_struct = %__MODULE__{}, workspace_sid) do
%{capability_struct | workspace_sid: workspace_sid}
end
@doc """
Sets the TTL of the token in seconds.
- [TTL docs](
https://www.twilio.com/docs/api/client/capability-tokens#token-expiration)
## Example
Sets the TTL to one hour
ExTwilio.WorkerCapability.with_ttl(3600)
"""
@spec with_ttl(t, non_neg_integer) :: t
def with_ttl(capability_struct = %__MODULE__{}, ttl) do
%{capability_struct | ttl: ttl}
end
def allow_activity_updates(
capability_struct = %__MODULE__{
policies: policies,
worker_sid: worker_sid,
workspace_sid: workspace_sid
}
) do
policy =
add_policy(worker_reservation_url(workspace_sid, worker_sid), "POST", true, nil, %{
"ActivitySid" => %{required: true}
})
Map.put(capability_struct, :policies, [policy | policies])
end
def allow_reservation_updates(
capability_struct = %__MODULE__{
policies: policies,
worker_sid: worker_sid,
workspace_sid: workspace_sid
}
) do
policies = allow(policies, task_url(workspace_sid), "POST")
policy = add_policy(worker_reservation_url(workspace_sid, worker_sid), "POST")
Map.put(capability_struct, :policies, [policy | policies])
end
@doc """
Generates a JWT token based on the requested policies
## Example
Generates and signs a token with the provided capabilities
ExTwilio.WorkerCapability.token
"""
@spec token(t) :: String.t()
def token(%__MODULE__{
account_sid: account_sid,
auth_token: auth_token,
policies: policies,
start_time: start_time,
ttl: ttl,
worker_sid: worker_sid,
workspace_sid: workspace_sid
}) do
policies
|> allow(websocket_requests_url(worker_sid, account_sid), "GET")
|> allow(websocket_requests_url(worker_sid, account_sid), "POST")
|> allow(workspaces_base_url(workspace_sid), "GET")
|> allow(activity_url(workspace_sid), "GET")
|> allow(task_url(workspace_sid), "GET")
|> allow(worker_reservation_url(workspace_sid, worker_sid), "GET")
|> jwt_payload(account_sid, expiration_time(start_time, ttl), workspace_sid, worker_sid)
|> generate_jwt(auth_token)
end
def allow(policies, url, method, query_filters \\ %{}, post_filters \\ %{}) do
policy = add_policy(url, method, true, query_filters, post_filters)
[policy | policies]
end
defp websocket_requests_url(worker_sid, account_sid) do
"#{Config.task_router_websocket_base_url()}/#{account_sid}/#{worker_sid}"
end
defp expiration_time(start_time, ttl) do
start_time + ttl
end
defp jwt_payload(policies, issuer, expiration_time, workspace_sid, worker_sid) do
%{
"iss" => issuer,
"exp" => expiration_time,
"workspace_sid" => workspace_sid,
"friendly_name" => worker_sid,
"account_sid" => issuer,
"version" => "v1",
"policies" => policies,
"channel" => worker_sid,
"worker_sid" => worker_sid
}
end
defp add_policy(url, method) do
add_policy(url, method, true, %{}, %{})
end
defp add_policy(url, method, allowed, query_filters, post_filters) do
%{
"url" => url,
"post_filter" => post_filters,
"method" => method,
"allowed" => allowed,
"query_filter" => query_filters
}
end
defp generate_jwt(payload, secret) do
signer = Joken.Signer.create("HS256", secret)
Joken.generate_and_sign!(%{}, payload, signer)
end
defp workspaces_base_url(workspace_sid) do
"#{Config.task_router_url()}/Workspaces/#{workspace_sid}"
end
defp task_url(workspace_sid) do
"#{workspaces_base_url(workspace_sid)}/Tasks/**"
end
defp activity_url(workspace_sid) do
"#{workspaces_base_url(workspace_sid)}/Activities"
end
defp worker_reservation_url(workspace_sid, worker_sid) do
"#{workspaces_base_url(workspace_sid)}/Workers/#{worker_sid}"
end
end