Plug to validate signed request.
def deps do
[{:resuelve_auth, "~> 1.5", organization: "resuelve"}]
end
Add the plugin to a pipeline, following the guides to creating libraries in Elixir.
pipeline :api_auth do
...
plug ResuelveAuth.AuthPlug
end
This library requires OTP >= 21.0
We recommend using asdf. Then you can install and use the correct versions simply by running:
$ asdf install
Here's the list of options that can be configured for the plug.
Option | Description | Default value |
---|---|---|
limit_time | Token lifespan in hours | 168 (1 week) |
secret | Secret key | empty |
handler | Error handler module | ResuelveAuth.Sample.AuthHandler |
You can declare global or per environment configuration for the plug in your config files.
# In config/{config|prod|dev|test|runtime}.exs
config :resuelve_auth,
secret: "my-secret-key",
limit_time: 24,
handler: MyApp.MyAuthHandler
You can also pass a keyword list of options to the plug declaration. This allows to have multiple configurations.
pipeline :auth do
plug ResuelveAuth.AuthPlug, secret: "my-secret-key", ...
end
Note: If you use environmental variables to set any inline configuration option,
you'll need to make sure those variables are available at compilation time since
they'll be used in a macro (your app's router). If you don't need multiple configs
per environment we recommend using config/runtime.exs
file instead.
Inline configuration will override global and per-environment configuration.
By using Phoenix you can create a new secret with:
$ mix phx.gen.secret 32
TICxDq3wquPi49UuMfA4PjnWpz1PqnB1
$ mix phx.gen.secret 64
b9sq3yGrwWKXxpNfx3+a8hEaRa3S5QWMiRg+gPpbzc54ZpjVaqDYD3DRbPuYx621
Another way to create a secret is:
$ date +%s | sha256sum | base64 | head -c 32 ; echo
MGYwM2M1Njk1MGIxYjcyOGY3OTc0ZDk0
$ date +%s | sha256sum | base64 | head -c 64 ; echo
ZGZhMzZhOWQyZTViOWQxNWIyY2NlMGExMDVhMzQ1ZGNkODA1YWUxNmRmMWRjMGZi
And in case you wish to use openssl:
$ openssl rand -base64 32
//ZE5siYI04Bp/2JtFq3uJOpS4XXChADe8b9RHenzFY=
$ openssl rand -base64 64
qlTw8sjiavcPAKIHJbO/zOUqLCS99zmyerjnoRc6FumLIc/Q9K9TjitS4JmTFh5r
3ULjJAMfkouTR1OUV4LZ4Q==
%TokenData{}
is the struct you'll be using to generated tokens. This is how it'd look:
%TokenData{
service: "service-name",
role: "role-name",
meta: "metadata",
timestamp: 1593731494361
}
As you can see, the timestamp field requires an Unix time number, which could be created with:
DateTime.to_unix(DateTime.utc_now(), :millisecond)
Your struct would actually look like this:
%TokenData{
service: "my-api",
role: "admin",
meta: "metadata",
timestamp: DateTime.to_unix(DateTime.utc_now(), :millisecond)
}
This is how you can generate a token (minimum code example):
iex> alias ResuelveAuth.TokenData
iex> alias ResuelveAuth.Helpers.TokenHelper
iex> time = DateTime.to_unix(DateTime.utc_now(), :millisecond)
iex> token_data = %TokenData{
service: "my-api",
role: "admin",
meta: "metadata",
timestamp: time
}
iex> options = [secret: "super-secret-key", limit_time: 4]
iex> token = TokenHelper.create_token(token_data, options)
"eyJ0aW1lc3RhbXAiOjE1OTM3MzQ0MzQ4ODEsInNlc3Npb24iOm51bGwsInNlcnZpY2UiOiJteS1hcGkiLCJyb2xlIjoiYWRtaW4iLCJtZXRhIjoibWV0YWRhdGEifQ==.9AAEBDB040BFB22160B4628EC45D69C3546C0775398D7B03C113C5BDDEC3A74B"
After creating the token you can use it in your requests and validate with the following method:
iex> options = [secret: "super-secret-key", limit_time: 4]
iex> {:ok, result} = TokenHelper.verify_token(token, options)
{:ok,
%{
"meta" => "metadata",
"role" => "admin",
"service" => "my-api",
"session" => nil,
"time" => ~U[2020-07-03 00:00:34.881Z],
"timestamp" => 1593734434881
}}
If the token is invalid, you will see an error like this:
** (MatchError) no match of right hand side value: {:error, :wrong_format}
The following are the errors returned by the plug:
{:error, :expired}
{:error, :unauthorized}
{:error, :wrong_format}
{:error, :invalid_unix_time}
Maybe you want to handle error messages differently or add details in the response. Below is an example of how to customize error handling.
defmodule App.MyErrorHandler do
def errors(conn, reason) do
# Error handler logic
end
end
iex> options = [secret: "super-secret-key", limit_time: 4, handler: App.MyErrorHandler]
iex> {:ok, result} = TokenHelper.verify_token(token, options)
NOTE: verify_token
function is NOT intended to be called directly since it's used internally by the plug; its usage in these examples is for demonstration purposes only.
Take a look to the ResuelveAuth.Sample.AuthHandler module for the default error handler implementation.
This is the list of contributors who have participated in this project.