README.md

![test workflow](https://github.com/cogini/tesla_middleware_dynamic_headers/actions/workflows/test.yml/badge.svg)
[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](CODE_OF_CONDUCT.md)

# Tesla.Middleware.DynamicHeaders

Middleware for the [Tesla](https://hexdocs.pm/tesla/readme.html) HTTP client
that sets value for HTTP headers dynamically at runtime. 

This is most useful to handle secrets such as auth tokens. If you set them at
compile time, then they are hard coded into the release file, which is a
security risk. Similarly, if you build your code in a CI system, then you have
to make the secrets available there. 


## Installation

Add `tesla_middleware_dynamic_headers` to the list of dependencies in `mix.exs`:

```elixir
def deps do
  [
    {:tesla_middleware_dynamic_headers, "~> 0.7.0"}
  ]
end
```

## Configuration

Add this middleware as a plug in your client.

Add `plug Tesla.Middleware.DynamicHeaders` to the client and
specify the list of dynamic headers. 

The plug takes a single option, either a list of tuples or a function. The
first element of the tuple is the header name. Other values are as follows:

* `{application, key}`: Reads the value from `Application.get_env/2`
* `{application, key, default}`: Reads the value from `Application.get_env/3`
* Function with arity 1: Calls the function with the header name to get the value
* Any other value is passed through as is, same as `Tesla.Middleware.Headers`

If the option is a zero-arity function, it is called to generate a list of
`{header_name, value`} tuples.

## Examples

The following example shows configuration via a list of headers:

```elixir
defmodule FooClient do
  use Tesla

  @app :foo_client

  plug(Tesla.Middleware.BaseUrl, "https://example.com/")

  plug(Tesla.Middleware.DynamicHeaders, [
    {"X-Foo-Token", {@app, :foo_token}},
    {"X-Bar-Token", {@app, :bar_token, "default"}},
    {"Authorization", &get_authorization/1},
    {"content/type", "application/json"}
    ])

  plug(Tesla.Middleware.Logger)

  defp get_authorization(header_name) do
    {header_name, "token: " <> Application.get_env(@app, :auth_token)}
  end
end
```

The following example shows how to use a custom function to generate all the headers:

```
defmodule FooClient do
  use Tesla

  @app :foo_client

  plug Tesla.Middleware.DynamicHeaders, &get_dynamic_headers/0

  defp get_dynamic_headers do
    Application.get_env(@app, :headers)
  end
end
```

The equivalent configuration in `config/test.exs` might look like:

```elixir
config :foo_client,
  foo_token: "footoken",
  bar_token: "bartoken",
  auth_token: "authtoken"

config :foo_client,
  headers: [
    {"Authorization", "token: authtoken"}
  ]
```

In production, you would set environment variables with the tokens, then read them
in `config/runtime.exs`:

```elixir
config :foo_client,
  foo_token: = System.get_env("FOO_TOKEN") || raise "missing environment variable FOO_TOKEN"
```

Documentation is here: https://hexdocs.pm/tesla_middleware_dynamic_headers

This project uses the Contributor Covenant version 2.1. Check [CODE_OF_CONDUCT.md](/CODE_OF_CONDUCT.md) for more information.