if Code.ensure_loaded?(Plug) do
defmodule Guardian.Plug.EnsureAuthenticated do
@moduledoc """
This plug ensures that a valid token was provided and has been verified on the request.
If one is not found, the `auth_error` will be called with `:unauthenticated`
This, like all other Guardian plugs, requires a Guardian pipeline to be setup.
It requires an implementation module, an error handler and a key.
These can be set either:
1. Upstream on the connection with `plug Guardian.Pipeline`
2. Upstream on the connection with `Guardian.Pipeline.{put_module, put_error_handler, put_key}`
3. Inline with an option of `:module`, `:error_handler`, `:key`
Options:
* `claims` - The literal claims to check to ensure that a token is valid
* `max_age` - If the token has an "auth_time" claim, check it is not older than the maximum age.
* `key` - The location to find the information in the connection. Defaults to: `default`
* `halt` - Whether to halt the connection in case of error. Defaults to `true`
## Example
```elixir
# setup the upstream pipeline
plug Guardian.Plug.EnsureAuthenticated, claims: %{"typ" => "access"}
plug Guardian.Plug.EnsureAuthenticated, key: :secret
```
"""
@behaviour Plug
@impl Plug
@spec init(Keyword.t()) :: Keyword.t()
def init(opts), do: opts
@impl Plug
@spec call(conn :: Plug.Conn.t(), opts :: Keyword.t()) :: Plug.Conn.t()
def call(conn, opts) do
conn
|> Guardian.Plug.current_token(opts)
|> verify(conn, opts)
|> respond()
end
@spec verify(token :: Guardian.Token.token(), conn :: Plug.Conn.t(), opts :: Keyword.t()) ::
{{:ok, Guardian.Token.claims()} | {:error, any}, Plug.Conn.t(), Keyword.t()}
defp verify(nil, conn, opts), do: {{:error, :unauthenticated}, conn, opts}
defp verify(_token, conn, opts) do
result =
conn
|> Guardian.Plug.current_claims(opts)
|> verify_claims(opts)
{result, conn, opts}
end
@spec respond({{:ok, Guardian.Token.claims()} | {:error, any}, Plug.Conn.t(), Keyword.t()}) :: Plug.Conn.t()
defp respond({{:ok, _}, conn, _opts}), do: conn
defp respond({{:error, reason}, conn, opts}) do
conn
|> Guardian.Plug.Pipeline.fetch_error_handler!(opts)
|> apply(:auth_error, [conn, {:unauthenticated, reason}, opts])
|> Guardian.Plug.maybe_halt(opts)
end
@spec verify_claims(Guardian.Token.claims(), Keyword.t()) :: {:ok, Guardian.Token.claims()} | {:error, any}
defp verify_claims(claims, opts) do
to_check = opts |> Keyword.get(:claims) |> Guardian.stringify_keys()
Guardian.Token.Verify.verify_literal_claims(claims, to_check, opts)
end
end
end