View Source Phoenix.Token (Phoenix v1.7.10)
Tokens provide a way to generate and verify bearer tokens for use in Channels or API authentication.
The data stored in the token is signed to prevent tampering but not encrypted. This means it is safe to store identification information (such as user IDs) but should not be used to store confidential information (such as credit card numbers).
Example
When generating a unique token for use in an API or Channel it is advised to use a unique identifier for the user, typically the id from a database. For example:
iex> user_id = 1
iex> token = Phoenix.Token.sign(MyAppWeb.Endpoint, "user auth", user_id)
iex> Phoenix.Token.verify(MyAppWeb.Endpoint, "user auth", token, max_age: 86400)
{:ok, 1}
In that example we have a user's id, we generate a token and
verify it using the secret key base configured in the given
endpoint
. We guarantee the token will only be valid for one day
by setting a max age (recommended).
The first argument to both sign/4
and verify/4
can be one of:
- the module name of a Phoenix endpoint (shown above) - where the secret key base is extracted from the endpoint
Plug.Conn
- where the secret key base is extracted from the endpoint stored in the connectionPhoenix.Socket
orPhoenix.LiveView.Socket
- where the secret key base is extracted from the endpoint stored in the socket- a string, representing the secret key base itself. A key base with at least 20 randomly generated characters should be used to provide adequate entropy
The second argument is a cryptographic salt
which must be the same in both calls to sign/4
and verify/4
.
For instance, it may be called "user auth" and treated as namespace
when generating a token that will be used to authenticate users on
channels or on your APIs.
The third argument can be any term (string, int, list, etc.) that you wish to codify into the token. Upon valid verification, this same term will be extracted from the token.
Usage
Once a token is signed, we can send it to the client in multiple ways.
One is via the meta tag:
<%= tag :meta, name: "channel_token",
content: Phoenix.Token.sign(@conn, "user auth", @current_user.id) %>
Or an endpoint that returns it:
def create(conn, params) do
user = User.create(params)
render(conn, "user.json",
%{token: Phoenix.Token.sign(conn, "user auth", user.id), user: user})
end
Once the token is sent, the client may now send it back to the server as an authentication mechanism. For example, we can use it to authenticate a user on a Phoenix channel:
defmodule MyApp.UserSocket do
use Phoenix.Socket
def connect(%{"token" => token}, socket, _connect_info) do
case Phoenix.Token.verify(socket, "user auth", token, max_age: 86400) do
{:ok, user_id} ->
socket = assign(socket, :user, Repo.get!(User, user_id))
{:ok, socket}
{:error, _} ->
:error
end
end
def connect(_params, _socket, _connect_info), do: :error
end
In this example, the phoenix.js client will send the token in the
connect
command which is then validated by the server.
Phoenix.Token
can also be used for validating APIs, handling
password resets, e-mail confirmation and more.
Summary
Functions
Decrypts the original data from the token and verifies its integrity.
Encodes, encrypts, and signs data into a token you can send to clients.
Encodes and signs data into a token you can send to clients.
Decodes the original data from the token and verifies its integrity.
Types
@type max_age_opt() :: {:max_age, pos_integer() | :infinity}
@type signed_at_opt() :: {:signed_at, pos_integer()}
Functions
@spec decrypt(context(), binary(), binary(), [shared_opt() | max_age_opt()]) :: term()
Decrypts the original data from the token and verifies its integrity.
Options
:key_iterations
- option passed toPlug.Crypto.KeyGenerator
when generating the encryption and signing keys. Defaults to 1000:key_length
- option passed toPlug.Crypto.KeyGenerator
when generating the encryption and signing keys. Defaults to 32:key_digest
- option passed toPlug.Crypto.KeyGenerator
when generating the encryption and signing keys. Defaults to:sha256
:max_age
- verifies the token only if it has been generated "max age" ago in seconds. Defaults to the max age signed in the token (86400)
@spec encrypt(context(), binary(), term(), [ shared_opt() | max_age_opt() | signed_at_opt() ]) :: binary()
Encodes, encrypts, and signs data into a token you can send to clients.
Options
:key_iterations
- option passed toPlug.Crypto.KeyGenerator
when generating the encryption and signing keys. Defaults to 1000:key_length
- option passed toPlug.Crypto.KeyGenerator
when generating the encryption and signing keys. Defaults to 32:key_digest
- option passed toPlug.Crypto.KeyGenerator
when generating the encryption and signing keys. Defaults to:sha256
:signed_at
- set the timestamp of the token in seconds. Defaults toSystem.system_time(:second)
:max_age
- the default maximum age of the token. Defaults to 86400 seconds (1 day) and it may be overridden on verify/4.
@spec sign(context(), binary(), term(), [ shared_opt() | max_age_opt() | signed_at_opt() ]) :: binary()
Encodes and signs data into a token you can send to clients.
Options
:key_iterations
- option passed toPlug.Crypto.KeyGenerator
when generating the encryption and signing keys. Defaults to 1000:key_length
- option passed toPlug.Crypto.KeyGenerator
when generating the encryption and signing keys. Defaults to 32:key_digest
- option passed toPlug.Crypto.KeyGenerator
when generating the encryption and signing keys. Defaults to:sha256
:signed_at
- set the timestamp of the token in seconds. Defaults toSystem.system_time(:second)
:max_age
- the default maximum age of the token. Defaults to 86400 seconds (1 day) and it may be overridden on verify/4.
@spec verify(context(), binary(), binary(), [shared_opt() | max_age_opt()]) :: {:ok, term()} | {:error, :expired | :invalid | :missing}
Decodes the original data from the token and verifies its integrity.
Examples
In this scenario we will create a token, sign it, then provide it to a client
application. The client will then use this token to authenticate requests for
resources from the server. See Phoenix.Token
summary for more info about
creating tokens.
iex> user_id = 99
iex> secret = "kjoy3o1zeidquwy1398juxzldjlksahdk3"
iex> namespace = "user auth"
iex> token = Phoenix.Token.sign(secret, namespace, user_id)
The mechanism for passing the token to the client is typically through a cookie, a JSON response body, or HTTP header. For now, assume the client has received a token it can use to validate requests for protected resources.
When the server receives a request, it can use verify/4
to determine if it
should provide the requested resources to the client:
iex> Phoenix.Token.verify(secret, namespace, token, max_age: 86400)
{:ok, 99}
In this example, we know the client sent a valid token because verify/4
returned a tuple of type {:ok, user_id}
. The server can now proceed with
the request.
However, if the client had sent an expired token, an invalid token, or nil
,
verify/4
would have returned an error instead:
iex> Phoenix.Token.verify(secret, namespace, expired, max_age: 86400)
{:error, :expired}
iex> Phoenix.Token.verify(secret, namespace, invalid, max_age: 86400)
{:error, :invalid}
iex> Phoenix.Token.verify(secret, namespace, nil, max_age: 86400)
{:error, :missing}
Options
:key_iterations
- option passed toPlug.Crypto.KeyGenerator
when generating the encryption and signing keys. Defaults to 1000:key_length
- option passed toPlug.Crypto.KeyGenerator
when generating the encryption and signing keys. Defaults to 32:key_digest
- option passed toPlug.Crypto.KeyGenerator
when generating the encryption and signing keys. Defaults to:sha256
:max_age
- verifies the token only if it has been generated "max age" ago in seconds. A reasonable value is 1 day (86400 seconds)