Phoenix API Toolkit v0.9.0 PhoenixApiToolkit.Security.Plugs View Source
Security-related plugs.
Several of these plugs are based on recommendations for API's by the OWASP guidelines.
Link to this section Summary
Functions
Assigns the client's IP to the conn as client_ip
. Prefers IP in header "x-forwarded-for"
over
the directly detected remote IP.
Adds security headers to the response as recommended for API's by OWASP. Sets
"x-frame-options": "deny"
and "x-content-type-options": "nosniff"
.
Checks if the request's "content-type"
header is present. Content matching is done by Plug.Parsers
.
Check if the JWT in conn.assigns.jwt
has an "aud"
claim that matches the exp_aud
parameter.
This assign is set by PhoenixApiToolkit.Security.Oauth2Plug
and should contain a JOSE.JWT
struct.
Check if the JWT in conn.assigns.jwt
has a "scope"
claim that matches the exp_scopes
parameter.
This assign is set by PhoenixApiToolkit.Security.Oauth2Plug
and should contain a JOSE.JWT
struct.
Link to this section Functions
assign_client_ip(conn, opts \\ [])
View Sourceassign_client_ip(Plug.Conn.t(), Plug.opts()) :: Plug.Conn.t()
Assigns the client's IP to the conn as client_ip
. Prefers IP in header "x-forwarded-for"
over
the directly detected remote IP.
Examples
use Plug.Test
def conn_with_ip, do: conn(:get, "/") |> Map.put(:remote_ip, {127, 0, 0, 12})
# by default, the value of `remote_ip` is used
iex> (conn_with_ip() |> assign_client_ip()).assigns.client_ip
"127.0.0.12"
# if header "x-forwarded-for" is set, its value is preferred
iex> (conn_with_ip() |> put_req_header("x-forwarded-for", "10.0.0.1") |> assign_client_ip()).assigns.client_ip
"10.0.0.1"
put_security_headers(conn, opts \\ [])
View Sourceput_security_headers(Plug.Conn.t(), Plug.opts()) :: Plug.Conn.t()
Adds security headers to the response as recommended for API's by OWASP. Sets
"x-frame-options": "deny"
and "x-content-type-options": "nosniff"
.
Examples
use Plug.Test
# it does what it says it does
iex> conn = conn(:get, "/")
iex> put_security_headers(conn).resp_headers -- conn.resp_headers
[{"x-frame-options", "deny"}, {"x-content-type-options", "nosniff"}]
require_content_type(conn, opts \\ [])
View Sourcerequire_content_type(Plug.Conn.t(), Plug.opts()) :: Plug.Conn.t()
Checks if the request's "content-type"
header is present. Content matching is done by Plug.Parsers
.
The filter is only applied to methods which are expected to carry contents, to PUT
, POST
and PATCH
methods, that is. Only one content-type
header is allowed. A noncompliant request causes a
PhoenixApiToolkit.Security.MissingContentTypeError
to be raised,
resulting in a 415 Unsupported Media Type response.
Examples
use Plug.Test
# safe methods pass through
iex> conn = conn(:get, "/")
iex> conn == require_content_type(conn)
true
# compliant unsafe methods (put, post and patch) pass through
iex> conn = conn(:post, "/") |> put_req_header("content-type", "application/json")
iex> conn == require_content_type(conn)
true
# noncompliant unsafe methods cause a MissingContentTypeError to be raised
iex> conn(:post, "/") |> require_content_type()
** (PhoenixApiToolkit.Security.MissingContentTypeError) missing 'content-type' header
verify_oauth2_aud(conn, exp_aud)
View Sourceverify_oauth2_aud(Plug.Conn.t(), binary()) :: Plug.Conn.t()
Check if the JWT in conn.assigns.jwt
has an "aud"
claim that matches the exp_aud
parameter.
This assign is set by PhoenixApiToolkit.Security.Oauth2Plug
and should contain a JOSE.JWT
struct.
If not, a PhoenixApiToolkit.Security.Oauth2TokenVerificationError
is raised,
resulting in a 401 Unauthorized response.
Examples
use Plug.Test
def conn_with_aud(aud), do: conn(:get, "/") |> assign(:jwt, %{fields: %{"aud", aud}})
# if aud matches, the conn is passed through
iex> conn = conn_with_aud("my resource server")
iex> conn == conn |> verify_oauth2_aud("my resource server")
true
# an error is raised if aud does not match
iex> conn_with_aud("my resource server") |> verify_oauth2_aud("another server")
** (PhoenixApiToolkit.Security.Oauth2TokenVerificationError) Oauth2 token invalid: aud mismatch
verify_oauth2_scope(conn, exp_scopes)
View Sourceverify_oauth2_scope(Plug.Conn.t(), [binary()]) :: Plug.Conn.t()
Check if the JWT in conn.assigns.jwt
has a "scope"
claim that matches the exp_scopes
parameter.
This assign is set by PhoenixApiToolkit.Security.Oauth2Plug
and should contain a JOSE.JWT
struct.
If not, a PhoenixApiToolkit.Security.Oauth2TokenVerificationError
is raised,
resulting in a 401 Unauthorized response.
Examples
use Plug.Test
def conn_with_scope(scope), do: conn(:get, "/") |> assign(:jwt, %{fields: %{"scope", scope}})
# if there is a matching scope, the conn is passed through
iex> conn = conn_with_scope("admin read:phone")
iex> conn == conn |> verify_oauth2_scope(["admin"])
true
iex> conn == conn |> verify_oauth2_scope(["admin", "not:a:match"])
true
iex> conn == conn |> verify_oauth2_scope(["admin", "read:phone"])
true
# an error is raised if there is no matching scope
iex> conn_with_scope("admin read:phone") |> verify_oauth2_scope(["not:a:match"])
** (PhoenixApiToolkit.Security.Oauth2TokenVerificationError) Oauth2 token invalid: scope mismatch