View Source AppIdentity.Plug (AppIdentity for Elixir v1.2.0)
A Plug that verifies App Identity proofs provided via one or more HTTP headers.
When multiple proof values are provided in the request, all must be
successfully verified. If any of the proof values cannot be verified,
request processing halts with 403 Forbidden
. Should no proof headers be
included, the request is considered invalid.
All of the above behaviours can be modified through configuration.
checking-appidentity-plug-results
Checking AppIdentity.Plug Results
The results of AppIdentity.Plug are stored in Plug.Conn private storage as
a map under the :app_identity
key (this can be changed through the name
option), keyed by the header group name. When using the headers
option,
each header is its own group. The header_groups
option explicitly defines
headers that will be treated as belonging to the same group.
Header results only appear in the result map if they are present in the
request. If AppIdentity.Plug is configured for app1
and app2
headers,
but there are only values in app1
, the resulting output will not include
app2
.
results-partitioning-name-or-header-groups
Results Partitioning: name
or Header Groups?
AppIdentity.Plug provides two main ways to partition processed results:
name
or header groups (either automatic grouping via headers
or explicit
grouping via header_groups
).
Most applications that require result partitioning will use header groups, because there's only one pool of applications defined. In this case, use the following configuration as a guide.
plug AppIdentity.Plug, finder: &MyApp.Application.get/1,
on_failure: :continue,
header_groups: %{
"application" => ["application-identity"],
"service" => ["service-identity"]
}
Later in request processsing, a controller or a route-specific Phoenix
pipeline coud call a require_application
function which pulls from
conn.private.app_identity
with the appropriate header group name for
verification.
If there are separate pools of applications defined, or there is a need to
have different on_failure
conditions, then configure two AppIdentity.Plugs
with different name
s . The following example configuration would allow
application-identity
headers to fail without halting (even if omitted),
but a missing or incorrect service-identity
header would cause failures
immediately.
plug AppIdentity.Plug, finder: &MyApp.Application.get/1,
on_failure: :continue,
headers: ["application-identity"]
plug AppIdentity.Plug, name: :service_app_identity,
finder: &MyApp.ServiceApplication.get/1,
header: ["service-identity"]
If multiple AppIdentity.Plug configurations are used, different name
values must be specified or the later plug will overwrite the results from
the earlier plug.
telemetry
Telemetry
When telemetry is enabled, this plug will emit [:app_identity, :plug, :start]
and [:app_identity, :plug, :stop]
events.
Link to this section Summary
Types
AppIdentity.Plug configuration options prior to validation.
Normalized options for AppIdentity.Plug.
Link to this section Types
@type on_failure() :: :forbidden | :continue | {:halt, Plug.Conn.status()} | {:halt, Plug.Conn.status(), Plug.Conn.body()}
@type on_failure_fn() :: (Plug.Conn.t() -> on_failure()) | {module(), function :: atom()}
@type option() :: AppIdentity.disallowed() | {:headers, [binary()]} | {:header_groups, %{required(binary()) => [binary()]}} | {:apps, [AppIdentity.App.input() | AppIdentity.App.t()]} | {:finder, AppIdentity.App.finder()} | {:name, atom()} | {:on_failure, on_failure() | on_failure_fn()}
AppIdentity.Plug configuration options prior to validation.
apps
: A list ofAppIdentity.App
orAppIdentity.App.input/0
values to be used for proof validation. Duplicate values will be ignored.plug AppIdentity.Plug, apps: [app1, app2], ...
disallowed
: A list of algorithm versions that are not allowed when processing received identity proofs. SeeAppIdentity.disallowed/0
.plug AppIdentity.Plug, disallowed: [1], ...
finder
: AnAppIdentity.App.finder/0
function to load anAppIdentity.App.input/0
from an external source given a parsed proof.plug AppIdentity.Plug, finder: &ApplicationModel.get/1
headers
: A list of HTTP header names, which will be normalized on initialization.plug AppIdentity.Plug, headers: ["application-identity"], ...
The result output uses each header name as the key for the related proof results. A configuration of
headers: ["app1", "app2"]
can produce a result map like%{"app1" => [...], "app2" => [...]}
.Duplicate header names will result in an error. This option must be omitted if
header_groups
is used.header_groups
: A map of header group names to HTTP header names. Both header group names and HTTP header names must be binary strings.When using
header_groups
, there is no guaranteed order for processing groups, but the each headers within a group will be processed in the order provided.plug AppIdentity.Plug, header_groups: %{ "application" => ["application", "my-application"], "service" => ["service", "my-service"], }, ...
The result output uses each header group name as the key for the related proof results from any header in that group. A configuration of
header_groups: %{"app" => ["app1", "app2"], "svc" => ["svc1"]}
can produce a result map like%{"app" => [...], "svc" => [...]}
.Duplicate header names across any header groups will result in an error. This option must be omitted if
headers
is used.name
: An atom which will be used to store theAppIdentity.Plug
results in Plug.Conn private storage. If not provided, defaults to:app_identity
. Required ifAppIdentity.Plug
will be specified more than once as results are not merged.plug AppIdentity.Plug, name: :service_app, ...
on_failure
: The behaviour of the AppIdentity.Plug when proof validation fails. Must be one of the following values::forbidden
: Halt request processing and respond with a403
(forbidden) status. This is the same as{:halt, :forbidden}
. This is the defaulton_failure
behaviour.plug AppIdentity.Plug, on_failure: :forbidden
{:halt, Plug.Conn.status()}
: Halt request processing and return the specified status code. An empty body is emitted.plug AppIdentity.Plug, on_failure: {:halt, :forbidden}
{:halt, Plug.Conn.status(), Plug.Conn.body()}
: Halt request processing and return the specified status code. The body value is included in the response.plug AppIdentity.Plug, on_failure: {:halt, :forbidden, ["Evicted"]}
:continue
: Continue processing, ensuring that failure states are recorded for the application to act on at a later point. This could be used to implement a distinction between validating a proof and requiring that the proof is valid.plug AppIdentity.Plug, on_failure: :continue
A 1-arity anonymous function or a {module, function} tuple accepting
Plug.Conn
that returns one of the above values.plug AppIdentity.Plug, on_failure: {ApplicationModel, :resolve_proof_failure} plug AppIdentity.Plug, on_failure: &ApplicationModel.resolve_proof_failure/1
At least one of apps
or finder
must be supplied. If both are
present, apps are looked up in the apps
list first.
Only one of headers
or header_groups
may be supplied. If both are
present, an exception will be raised.
@type t() :: %AppIdentity.Plug{ apps: %{optional(AppIdentity.id()) => AppIdentity.App.t()}, disallowed: [AppIdentity.version()], finder: nil | AppIdentity.App.finder(), header_groups: nil | %{required(binary()) => binary()}, headers: nil | [binary()], name: atom(), on_failure: on_failure() | {:fn, on_failure()} }
Normalized options for AppIdentity.Plug.