defmodule AccessToken do @moduledoc """ A plug for extracting the access token from the request. The token may be sent by the request either via the params with key `access_token` or a header with name `Authorization` and content `Bearer `. To use it, just plug it into the desired module: plug AccessToken If present, the access token will be accessible through the `assigns` map of the connection. conn.assigns[:access_token] ## Options * `:param` - The name of the HTTP *request* parameter to check for the access token. Default value is `access_token`. plug AccessToken, param: "token" * `:http_header` - The name of the HTTP *request* header to check for the access token. Default value is `authorization`. plug AccessToken, http_header: "custom-authorization" * `:http_header_prefix` - The prefix of the HTTP *request* authorization header. Default value is `Bearer`. plug AccessToken, http_header_prefix: "Token" * `:assign_to` - The name of the key to assign the access token. Defaults to `:access_token` plug AccessToken, assign_to: :token * `:error_status` - The status code to be returned in case the access token is not present. The status can be `nil`, an integer or an atom. The list of allowed atoms is available in `Plug.Conn.Status`. Defaults to `:unauthorized` plug AccessToken, error_status: :bad_request * `:error_handler` - The function to be called in case the access token is not present. The `:error_handler` is set using a `{module, function, args}` triple. The function will receive the `conn` followed by the list of `args` provided. plug AccessToken, error_handler: {Phoenix.Controller, :render, [MyAppWeb.ErrorView, "401.json"]} """ @behaviour Plug def init(opts \\ []) do %{ param: Keyword.get(opts, :param, "access_token"), http_header: Keyword.get(opts, :http_header, "authorization"), http_header_prefix: Keyword.get(opts, :http_header_prefix, "Bearer"), assign_key: Keyword.get(opts, :assign_to, :access_token), error_status: Keyword.get(opts, :error_status, :unauthorized), error_handler: Keyword.get(opts, :error_handler) } end def call(%Plug.Conn{} = conn, config) do case conn.params[config.param] || get_access_token_from_headers(conn, config) do nil -> conn |> Plug.Conn.put_status(config.error_status) |> apply_error_handler(config) |> Plug.Conn.halt() access_token -> Plug.Conn.assign(conn, config.assign_key, String.trim(access_token)) end end defp apply_error_handler(conn, %{error_handler: nil}), do: conn defp apply_error_handler(conn, %{error_handler: {mod, fun, args}}) do apply(mod, fun, [conn] ++ args) end defp get_access_token_from_headers(conn, config) do with [header] <- Plug.Conn.get_req_header(conn, config.http_header), true <- String.starts_with?(header, config.http_header_prefix) do String.replace_leading(header, config.http_header_prefix, "") else _ -> nil end end end